H.264码流中的NALU进行了一个简单的划分,标出了NALU的类型和长度等信息。因为我们在解析SPS和PPS中要使用到指数哥伦布编码的解析,所以有必要了解一下指数哥伦布编码。

一、指数哥伦布编码(理论篇)


指数哥伦布码(Exponential-Golomb code, 即Exp-Golomb code)是熵编码的一种编码方式,正常来说,可以拓展为k阶,但是在H264中使用的是0阶指数哥伦布编码,在H.264中使用ue(v)表示0阶无符号指数哥伦布编码的解码过程,用se(v)表示0阶有符号指数哥伦布编码过程

1.1、无符号指数哥伦布编码

用来表示无符号整数k阶指数哥伦布编码的生成步骤如下:

1、将数字以二进制形式写出,去掉最低位的k个比特位,之后加1

2、计算留下的比特数位数,将此数减1,即是需要增加的前导0的个数

3、将第一步中去掉的最低个比特位补回到比特串尾部

1.1.1、0阶无符号指数哥伦布编码过程

0阶无符号指数哥伦布编码最后生成的比特串格式为"前缀1后缀",前缀和后缀的长度是相同的。

假如我们待编码的数字codeNum = 4,0阶无符号指数哥伦布编码的步骤如下:

1、将数字以二进制写出,4的二进制为100,因为0阶指数哥伦布编码所有不用去掉低位

2、将上面的二进制+1,100加1为101,留下的比特数为3,3-1=2,所以需要增加前导0的个数为2

3、因为第一步没有去掉,所有这一步不进行任何操作,最终生成的比特串为00101

0阶指数哥伦布编码可以简化为如下步骤

1、将codeNum+1,4+1=5

2、将加1后的数字用二进制表示,5的二进制位101,1后缀=101,后缀为01,长度2

3、前缀与后缀长度相同,在前面加上2个0

下面对不同codeNum进行编码结果

codeNum codeNum+1 codeNum+1的二进制 需补前缀0的个数 编码后的比特串(红色表示补的前缀0)
0 1 1 0 1
1 2 10 1(0) 010
2 3 11 1(0) 011
3 4 100 2(00) 00100
4 5 101 2(00) 00101
5 6 110 2(00) 00110
6 7 111 2(00) 00111

0阶无符号指数哥伦布编码的解析过程如下

1、找到第一个不为0的bit,并记录总共找到了0的个数(num),这个时候读到的这个bit肯定是1

2、然后读num个后缀

3、1后缀转变成十进制就是原来的codeNum,codeNum = (1 <<i) + 后缀(十进制) - 1;

比如比特串的二进制为:00111,首先找到第一个不为0的比特,前面0的个数为2,然后再读2个后缀11,11十进制为3,这个时候codeNum = (1 << 2) + 3 - 1 = 4 + 3 - 1 = 6

1.1.2、k阶无符号指数哥伦布编码过程

1、将codeNum加上2^k

2、将加上2^k的数字用二进制表示

3、计算二进制的长度len,然后再前面加上len-1-k个0

比特串的格式位"前缀1后缀"。前缀 = 后缀 - k

codeNum k=1 len-1-k 编码后比特串 k=2 len-1-k 编码后比特串
1 1+2^1=3(11) 2-1-1=0 11 1+2^2=5(101) 3-1-2=0 101
2 2+2^1=4(100) 3-1-1=1 0100 2+2^2=6(110) 3-1-2=0 110
3 3+2^1=5(101) 3-1-1=1 0101 3+2^2=7(111) 3-1-2=0 111
4 4+2^1=6(110) 3-1-1=1 0110 4+2^2=8(1000) 4-1-2=1 01000

1.2、有符号指数哥伦布编码

有符号指数哥伦布编码一般使用se(v)表示,输出可能是负数,有符号指数哥伦布编码解析的过程是在无符号指数哥伦布编码解析过程之上进行的,遇到se(v)需要先调用ue(v)得到codeNum,然后再调用se(v)的过程.

value = (-1)^(codeNum+1) * (codeNum+1)/2;(-1)^(codeNum+1):表示如果codeNum为奇数那么是1,偶数为-1

二、SPS字节位简介


2.1、h264 profile

h264 分为三种profile

基准档次:baseline profile;

主要档次:main profile;

扩展档次:extended profile(常见无high profile);

在H.264的SPS中,第一个字节表示profile_idc,根据profile_idc的值可以确定码流符合哪一种档次。判断规律为:

profile_idc = 66 → baseline profile;

profile_idc = 77 → main profile;

profile_idc = 88 → extended profile;

  1. constrained_set0_flag
  2. constraint_set1_flag
  3. constraint_set2_flag
  4. constraint_set3_flag
  5. reserved_zero_4bits

2.2、profile_idc

编码等级 有以下取值

Options:
66 Baseline(直播)
77 Main(一般场景)
88 Extended
100 High (FRExt)
110 High 10 (FRExt)
122 High 4:2:2 (FRExt)
144 High 4:4:4 (FRExt)

2.3、level_idc

最大支持码流范围

标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。

2.4、seq_parameter_set_id

表示当前的序列参数集的id。通过该id值,图像参数集pps可以引用其代表的sps中的参数。

seq_parameter_set_id指定了由图像参数集指明的序列参数集。seq_parameter_set_id值应该是从0到31,包括0和31

注意: 当可用的情况下,编码器应该在sps值不同的情况下使用不同的seq_parameter_set_id值,而不是变化某一特定值的

2.5、log2_max_frame_num_minus4

这个句法元素主要是为读取另一个句法元素 frame_num 服务的,frame_num 是最重要的句法元素之一,它标识所属图像的解码顺序。

2.6、chroma_format_idc

与亮度取样对应的色度取样

chroma_format_idc 的值应该在 0到 3的范围内(包括 0和 3)。当 chroma_format_idc不存在时,应推断其值为 1(4:2:0的色度格式)。

色度采样结构

2.7、bit_depth_luma_minus8

表示视频位深

如图: YUV420 8bit

2.8、pic_order_cnt_type

指明了 poc (picture order count) 的编码方法, poc 标识图像的播放顺序。由于H.264 使用了 B 帧预测,使得图像的解码顺序并不一定等于播放顺序,但它们之间存在一定的映射关系。 poc 可以由 frame-num 通过映射关系计算得来,也可以索性由编码器显式地传送。 H.264 中一共定义了三种 poc 的编码方法,这个句法元素就是用来通知解码器该用哪种方法来计算 poc。

2.9、log2_max_pic_order_cnt_lsb_minus4

指明了变量 MaxPicOrderCntLsb 的值:
MaxPicOrderCntLsb = 2( log2_max_pic_order_cnt_lsb_minus4 + 4 )
该变量在 pic_order_cnt_type = 0 时使用。

2.10、max_num_ref_frames

指定参考帧队列可能达到的最大长度,解码器依照这个句法元素的值开辟存储区,这个存储区用于存放已解码的参考帧, H.264 规定最多可用 16 个参考帧。

2.11、pic_width_in_mbs_minus1

本句法元素加 1 后指明图像宽度,以宏块为单位:

frame_width = 16 × (pic_width_in_mbs_minus1 + 1);

2.12、pic_height_in_map_units_minus1

本句法元素加 1 后指明图像高度,以宏块为单位:

frame_height = 16 × (pic_height_in_map_units_minus1 + 1);

2.13、frame_mbs_only_flag

标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。

2.14、mb_adaptive_frame_field_flag

指明本序列是否属于帧场自适应模式。 mb_adaptive_frame_field_flag等于1时表明在本序列中的图像如果不是场模式就是帧场自适应模式,等于0时表示本序列中的图如果不是场模式就是帧模式。。表 列举了一个序列中可能出现的编码模式:

a. 全部是帧,对应于 frame_mbs_only_flag =1 的情况。
b. 帧和场共存。 frame_mbs_only_flag =0, mb_adaptive_frame_field_flag =0
c. 帧场自适应和场共存。 frame_mbs_only_flag =0, mb_adaptive_frame_field_flag =1
值得注意的是,帧和帧场自适应不能共存在一个序列中。

2.15、direct_8x8_inference_flag

标识位,用于B_Skip、B_Direct模式运动矢量的推导计算。

2.16、vui_parameters_present_flag

指明 vui 子结构是否出现在码流中,用以表征视频格式等额外信息。

三、解码sps中宽高信息


视频文件可以通过这篇文章获取

下面是手动解析sps中的宽高信息,通过这些可以知道sps中有哪些信息,为后续深入学习音视频做准备。

public class Main {public void Ue() {int nStartBit = 3;byte data = 5 & 0xFF;//字节上的5  0000 0101//统计0 的个数int nZeroNum = 0;while (nStartBit < 8) {if ((data & (0x80 >> (nStartBit))) != 0){break;}nZeroNum++;nStartBit++;}nStartBit++;int dwRet = 0;//1  0for (int i = 0; i < nZeroNum; i++) {dwRet <<= 1;//0 <<1   1*2=2 11  0   3*2=6if ((data & (0x80 >> (nStartBit % 8))) != 0){dwRet += 1;//6+0 dwRet=6}nStartBit++;}int value = (1 << nZeroNum) -1+ dwRet;System.out.println(value);}static  int nStartBit = 0;public static  int Ue(byte[] pBuff) {//统计0 的个数int nZeroNum = 0;while (nStartBit < pBuff.length * 8) {if ((pBuff[nStartBit / 8] & (0x80 >> (nStartBit%8))) != 0){break;}nZeroNum++;nStartBit++;}nStartBit++;int dwRet = 0;//1  0for (int i = 0; i < nZeroNum; i++) {dwRet <<= 1;//0 <<1   1*2=2 11  0   3*2=6if ((pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) != 0){dwRet += 1;//6+0 dwRet=6}nStartBit++;}int value = (1 << nZeroNum) -1+ dwRet;System.out.println(value);return value;}public static byte[] hexStringToByteArray(String s) {//十六进制转byte数组int len = s.length();byte[] bs = new byte[len/2];for(int i = 0;i < len;i+=2) {bs[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));}return bs;}private static int u(int bitIndex, byte[] h264){int dwRet = 0;for (int i = 0; i < bitIndex; i++) {dwRet <<= 1;if ((h264[nStartBit / 8] & (0x80 >> (nStartBit % 8))) != 0){dwRet += 1;}nStartBit++;}return dwRet;}public static void main1(String[] args) {//这里面数据是sps信息byte[] h264=hexStringToByteArray("00 00 00 01 67 42 00 0A 8D 8D 40 28 02 AD 35 05 02 02 07 84 42 29 C0".replace(" ", ""));//        byte[] h264=hexStringToByteArray("00 00 00 01 67 64 00 15 AC D9 41 70 C6 84 00 00 03 00 04 00 00 03 00 F0 3C 58 B6 58".replace(" ", ""));nStartBit = 83;int widthTemp =Ue(h264);nStartBit = 92;int heigthTmp =Ue(h264);int width = (widthTemp + 1) * 16;int height = (heigthTmp + 1) * 16;System.out.println("width:  " + width);System.out.println("height:  " + height);}public static void main(String[] args) {//        0  A-67byte[] h264=hexStringToByteArray("00 00 00 01 67 64 00 15 AC D9 41 70 C6 84 00 00 03 00 04 00 00 03 00 F0 3C 58 B6 58".replace(" ", ""));
//        byte[] h264=hexStringToByteArray("00 00 00 01 67 42 00 0A 8D 8D 40 28 02 AD 35 05 02 02 07 84 42 29 C0".replace(" ", ""));nStartBit = 4*8;int forbidden_zero_bit=u(1, h264);int nal_ref_idc       =u(2, h264);//int nal_unit_type =u(5, h264);//if(nal_unit_type==7) {System.out.println("----------");int profile_idc = u(8, h264);
//            当constrained_set0_flag值为1的时候,就说明码流应该遵循基线profile(Baseline profile)的所有约束.constrained_set0_flag值为0时,说明码流不一定要遵循基线profile的所有约束。int constraint_set0_flag = u(1, h264);//(h264[1] & 0x80)>>7;// 当constrained_set1_flag值为1的时候,就说明码流应该遵循主profile(Main profile)的所有约束.constrained_set1_flag值为0时,说明码流不一定要遵int constraint_set1_flag = u(1, h264);//(h264[1] & 0x40)>>6;//当constrained_set2_flag值为1的时候,就说明码流应该遵循扩展profile(Extended profile)的所有约束.constrained_set2_flag值为0时,说明码流不一定要遵循扩展profile的所有约束。int constraint_set2_flag = u(1, h264);//(h264[1] & 0x20)>>5;
//            注意:当constraint_set0_flag,constraint_set1_flag或constraint_set2_flag中不只一个值为1的话,那么码流必须满足所有相应指明的profile约束。int constraint_set3_flag = u(1, h264);//(h264[1] & 0x10)>>4;
//            4个零位int reserved_zero_4bits = u(4, h264);
//            它指的是码流对应的level级int level_idc = u(8, h264);
//        0int seq_parameter_set_id = Ue(h264);
//System.out.println("----------");if (profile_idc == 100) {// chroma_format_idc 的值应该在 0到 3的范围内(包括 0和 3)  yuv420  yuv422 yuv 444int chroma_format_idc=Ue(h264);
//                bit_depth_luma_minus8   视频位深   0 八位   1 代表10位int bit_depth_luma_minus8   =Ue(h264);int bit_depth_chroma_minus8  =Ue(h264);int qpprime_y_zero_transform_bypass_flag=u(1, h264);
//               缩放换标志位int seq_scaling_matrix_present_flag     =u(1, h264);}
//            最大帧率int log2_max_frame_num_minus4=Ue(h264);
//确定播放顺序和解码顺序的映射int pic_order_cnt_type       =Ue(h264);int log2_max_pic_order_cnt_lsb_minus4=Ue(h264);
//编码索引  码流顺序int num_ref_frames                      =Ue(h264);
//int gaps_in_frame_num_value_allowed_flag=u(1,     h264);int pic_width_in_mbs_minus1             =Ue(h264);int pic_height_in_map_units_minus1      =Ue(h264);//sps宽高信息int width=(pic_width_in_mbs_minus1       +1)*16;int height=(pic_height_in_map_units_minus1+1)*16;System.out.println("width :  "+width+"   height: "+height);}}
}

音视频系列--哥伦布编码和H264片段sps解析宽高信息相关推荐

  1. (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264

    (一)音视频:解码H264文件流程 渲染和拿到解码后源数据YUV 完整Demo] (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264 (三)音视频:解析H264 SPS ...

  2. 音视频系列2:ffmpeg将H.264解码为RGB

    音视频系列2:ffmpeg将H.264解码为RGB 前言 源码 前言 喜大普奔,终于更新啦,上期说到,如何使用ffmpeg+rtmp进行拉流,不熟悉的小伙伴们,可以先看上一期.今天我们要实现的是使用f ...

  3. FFmpeg基础:获取音视频的各种编码参数

    文章目录 获取视频编码参数 获取音频编码参数 上一篇文章中介绍了音视频的各种编码参数的概念,这里介绍一下如何通过ffmpeg库获取一个视频文件的各种音视频编码参数.在对视频文件进行处理和转码的时候这些 ...

  4. 音视频编解码 -- 编码参数 CRF

    之前多多少少接触过一些编解码参数,CRF 参数也用过,但是最近在和朋友们聊天时,说到使用 FFMPEG 过程中碰到 CRF 参数,以及具体作用流程,这个之前一直没有跟踪过,也没有详细记录过,所以吊起了 ...

  5. 将视频 YUV 格式编码成 H264

    首先开始的时候我们借用一张雷神的图帮助大家理解一下我们今天的操作究竟属于那一步. 从上图可以看出我们要做的,就是将像素层的 YUV 格式,编码出编码层的 h264数据. 首先熟悉一下今天我们要用到的 ...

  6. datagrid 重载本地数据_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  7. ffmpeg rtmp 不清晰_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  8. ffmpeg rtmp 花屏_音视频系列6:ffmpeg多线程拉流

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/105302384,欢迎关注,点赞,评论. 前言 本篇博客是音视频系列的续集与改 ...

  9. 视频知识点(20)- H264码流如何在SPS中获取宽高信息?

    <音视频开发>系列-总览 前沿 了解H264视频编码格式的小伙伴都知道,H264编码中存在两个非常重要的参数集.没错,它们就是序列参数集(SPS)和图像参数集(PPS),而且通常情况下,P ...

最新文章

  1. 蜜蜂实训平台c语言考试答案,北理c语言上机答案(全)
  2. jQuery 选择器汇总-思维导图-选择器
  3. 源码分析系列1:HashMap源码分析(基于JDK1.8)
  4. Mysql安装和常用命令及问题汇总
  5. uni-app实现微信小程序本地图片转为base64
  6. lwip+freeRTOS 实现热插拔功能
  7. iap如何初始化_IAP在线升级模块详细设计说明
  8. pytorch学习笔记(八):softmax回归的从零开始实现
  9. [USACO12OPEN]Unlocking Block【BFS / 广搜】
  10. 放置江湖html5源码,「放置江湖」——经典文字类放置武侠手游
  11. 计算机usb端口没反应,usb接口没反应,小编教你电脑usb接口没反应怎么解决
  12. Inno Setup 6.0.3+ 简体中文语言包
  13. override overload
  14. 做了五套登录页,晒一下 自己比较满意的~ oh yeh~
  15. 【数据库内核】数据库核心技术演进之路
  16. 不带电脑看-吃货联盟集合
  17. 一篇 CVPR 2022顶会论文是如何炼成的,顶会一作亲述
  18. C 语言 结构体_finddata_t _findfirst, _findnext, _findclose 函数讲解
  19. Python——变量和简单类型(下篇)
  20. 第26届中学生计算机大赛,南京中学生自编APP获全国高校计算机大赛一等奖

热门文章

  1. HP-UX双机-安装ORACLE10g
  2. Django之MTV
  3. HOW TO TURN CONSUMER HOBBIES INTO PRODUCTION HOBBIES -- 怎样把“消费型爱好”转换“生产型爱好”
  4. sqlserver 2008 r2 误删数据库还原方法
  5. mysql字符集乱码问题_解决mysql字符集乱码问题
  6. 跨考 深大计算机,深圳大学!一所比985还要热门的双非!还很壕气!
  7. H5播放器内置播放视频(兼容绝大多数安卓和ios)
  8. windows2003 事件ID 10016 DCOM错误
  9. 计算机 智能化 论文范文大全集,智能论文范文
  10. STC8h1k28六个基本实验