1、码流总体结构:

h264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)。H.264 的编码视频序列包括一系列的NAL 单元,每个NAL 单元包含一个RBSP。一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU 单元的开始,必须是"00 00 00 01" 或"00 00 01"。

其中RBPS有分为几种类型:

NAL的解码单元的流程如下:

2、 NAL Header:

占一个字节,由三部分组成forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)。

forbidden_bit:禁止位。

nal_reference_bit:当前NAL的优先级,值越大,该NAL越重要。

nal_unit_type :NAL类型。参见下表

几个例子:

3、 ffmpeg解析H264流程分析

这是一段实际的码流

在上面的图片中,共有三个起始码:0x00 0000 01

const uint8_t*ff_h264_decode_nal(H264Context*h, const uint8_t *src,int *dst_length, int*consumed, int length)中分析过程为:

h->nal_ref_idc= src[0] >> 5;

h->nal_unit_type= src[0] & 0x1F;

此处src[0]即为06,写成二进制位0000 0110,则h->nal_ref_idc = 0,h->nal_unit_type = 6

可以判断这个NALU类型为SEI,重要性优先级为0。

src++;src向后移动一个位置,此时src指向图中第一行第五列的数据05

length--;未处理数据长度减1

#defineSTARTCODE_TEST                                                 \

if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){     \

if(src[i + 2] != 3){                                     \

/* startcode, so we must bepast the end*/             \

length =i;                                            \

}                                                          \

break;                                                     \

}

for(i = 0; i + 1 < length; i += 2) {

if(src[i])

continue;

if(i > 0 && src[i - 1] == 0)

i--;

STARTCODE_TEST;

}

上述分析:

1)如果src[i] !=0 ,则继续下一次循环,直到src[i] == 0,即等于下一个起始码的第二个00为止,即图中地址为000001a0h行的第3列,地址为0x00 00 01 a3(十进制为419),此时i为414,因为是从0x00 00 00 05地址开始的,此时则第一次执行continue下面的if语句,而且src[i-1]==0,则i--,此时i=413

2)执行宏定义STARTCODE_TEST,进行起始码检测:src[i+ 1] =src[414]=0,src[i+2]=src[415]=0;

继续执行,src[i+2] != 3,这里是进行竞争检测(前面有讲述,如果在编码的过程中出现连续00 00 00时,将会在第三个00前插入一个03,避免和起始码造成冲突),既然没有发生竞争现象,则表明这个确实是下一个NALU的起始码

3)length是指当前NALU单元长度,这里不包括nalu头信息长度,即一个字节,

length = i =413

后面的SEI解析不再赘述

(2)第二个 0x00 00 00 01:

h->nal_ref_idc= src[0] >> 5;

h->nal_unit_type= src[0] & 0x1F;

此时src[0]是指起始码后面第一个数据67(地址为0x00 00 01a6h),写成二进制为0110 0111,则h->nal_unit_type = 7,h->nal_ref_idc =3

则这一个NALU单元类型为SPS

下面开始分析SPS的长度,这一段数据很巧,包含了竞争检测

依然是下面这段代码:

src++;src向后移动一个位置,此时src指向图中地址为00 0001 a7h的数据

length--;未处理数据长度减1

#defineSTARTCODE_TEST                                                 \

if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){     \

if(src[i + 2] != 3){                                     \

/* startcode, so we must bepast the end*/             \

length =i;                                            \

}                                                          \

break;                                                     \

}

for(i = 0; i + 1 < length; i += 2) {

if(src[i])

continue;

if(i > 0 && src[i - 1] == 0)

i--;

STARTCODE_TEST;

}

分析:

1)自地址0x00 00 01 a7h开始查找src[i] ==0,此时src[i]的地址为0x00 00 01 b1h,此时src[i]==0,而src[i-1]=80 != 0,此时i = 10 ;

2)执行宏定义STARTCODE_TEST:if(i+2 <lengthn&&src[i+1]==0&&src[i+2]<=3)其中src[i+1]=src[11]=0,且src[i+2]=src[12]=3,继续执行,if(src[i+2] !=3),显然不满足,执行break,跳出循环,此时 i=10

此时尚不能确定下一个nalu单元的起始码在何处,因此当前nalu单元的长度也是未定的,

bufidx =h->nal_unit_type == NAL_DPC ? 1: 0;bufidx=0

si =h->rbsp_buffer_size[bufidx];

av_fast_padded_malloc(&h->rbsp_buffer[bufidx],&h->rbsp_buffer_size[bufidx],length+MAX_MBPAIR_SIZE);

dst =h->rbsp_buffer[bufidx];

以上是为当前NALU分配缓存,并清零

memcpy(dst,src, i);将当前确定的nalu内容拷贝到dst缓存中

si = di = i;目的缓存长度和源缓存长度=i=10

while (si + 2< length) {

//remove escapes (very rare 1:2^22)

if(src[si + 2] > 3) {

dst[di++]= src[si++];

dst[di++]= src[si++];

}else if (src[si] == 0 && src[si + 1] == 0) {

if(src[si + 2] == 3) { // escape

dst[di++]  = 0;

dst[di++]  = 0;

si        += 3;

continue;

}else // next start code

goto nsc;

}

dst[di++]= src[si++];

}

nsc:

memset(dst+ di, 0, FF_INPUT_BUFFER_PADDING_SIZE);

*dst_length= di;

*consumed  = si + 1; // +1 forthe header

/*FIXME store exact number of bits in the getbitcontext

*(it is needed for decoding) */

returndst;

分析:src[0]=4D位于0x00 00 01a7h,src[10]=0位于0x00 00 01b1h后面的地址不再赘述,依次为起始位置

从图中可以看出:

src[10]=00,src[11]=00,src[12]=03,src[13]=00,src[14]=80,src[15]=00,src[16]=00,src[17]=19,src[18]=47,src[19]=8C,src[20]=19,src[21]=50,src[22]=00,src[23]=00,src[24]=00

1)进入while循环,

此时si=10:

src[si+2] =src[12]=03且src[11]=00,src[10]=00,则执行:

dst[di++]=0:即dst[10]=0,di=11,dst[11]=0,di=12;

si+=3:si=13,然后continue,开始下一次循环

注:此处的03即为竞争检测,最后将03跳过了

此时si=13:

src[15]=00,src[13]=00,src[14]=80,则执行

dst[di++]=src[si++];

即:dst[12]=src[13]=00,di=13,si=14;

此时si=14:

src[16]=00,src[15]=00,src[14]=80

则执行dst[di++]=src[si++];

即:dst[13]=src[14]=00,di=14,si=15

此时si=15:

src[15]=00,src[16]=00,src[17]=19

则执行:dst[di++] = src[si++];

dst[di++]= src[si++];

dst[di++]= src[si++]

即:dst[14]=src[15]=00,di=15,si=16;

dst[15]=src[16]=00,di=16,si=17;

dst[16]=src[17]=19,di=17,si=18;

此时si=18:

src[18]=47,src[19]=8C,src[20]=19

执行:

dst[di++] = src[si++];

dst[di++]= src[si++];

dst[di++] =src[si++]

即:dst[17]=src[18]=47,di=18,si=19

dst[18]=src[19]=8C,di=19,si=20

dst[19]=src[20]=19,di=20,si=21

此时si=21:

src[21]=50,src[22]=00,src[23]=00

执行:

dst[di++]= src[si++];

即dst[20]=src[21]=50,di=21,si=22

此时si=22:

src[22]=00,src[23]=00,src[24]=00

执行:

goto nsc;

那就看一下nsc:

nsc:

memset(dst+ di, 0, FF_INPUT_BUFFER_PADDING_SIZE);

*dst_length= di;

*consumed  = si + 1; // +1 forthe header

/*FIXME store exact number of bits in the getbitcontext

*(it is needed for decoding) */

returndst;

此时di=21,si=22

memset()函数是向SPS单元尾部补零

dst_length=di=21,;即SPS单元的RBSP长度为21,不包括起始码

consumed=si+1=23;表示SPS单元共消耗的字节数,加1表示SPS单元信息头占一个字节,注意consumed比dst_length大1,这是由于竞争检测时,将编码时添加的一个字节的03过滤掉造成的。

H264码流结构分析相关推荐

  1. H264视频码流结构分析

    目录 前言 H264码流结构 H264帧结构 H264档次介绍 RTSP实时音视频开发实战课程:<RTSP实时音视频开发实战> <YUV编码为H264视频流代码实现>链接: h ...

  2. h264码流及h265码流结构分析,NAL头类型分析

    视频编码标准规定了编码后码流的语法语义,也就阐明了从比特流提取语法元素并进行解释的方法,也就是视频的解码过程.   1.h264码流结构解析:     H.264/AVC(Advanced Video ...

  3. H265视频码流结构分析

    目录 前言 H265码流结构 H265帧结构 H265档次介绍 前言 在音视频开发入门基础知识(视频入门篇)中介绍了H265的一些编码基础,本文会对H265编码后的视频流做一个详细的介绍.H264视频 ...

  4. H264码流打包分析(精华)

    H264码流打包分析 SODB 数据比特串-->最原始的编码数据 RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit" ...

  5. RTP协议全解(H264码流和PS流)

    1 视频编码的原理 1.1 一个图像或者一个视频序列进行压缩,产生码流. 对图像的处理即是:帧内预测编码 其预测值P,是由已编码的图像做参考,经运动补偿得到的.预测图像P和当前帧Fn相减,得到两图像的 ...

  6. RTP协议全解析(H264码流和PS流)(转)

    源: RTP协议全解析(H264码流和PS流) 转载于:https://www.cnblogs.com/LittleTiger/p/10489247.html

  7. H264码流打包分析

    H264码流打包分析 SODB 数据比特串-->最原始的编码数据 RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit" ...

  8. 【H.264/AVC视频编解码技术】第二章【H264码流分析】

    H264码流分层 NAL层,视频数据网络抽象层,作用是控制二进制数据的传输,主要用于网络传输.  VCL层,视频数据编码层. VCL结构关系 NALU NAL Header (1B)+ RBSP H2 ...

  9. RTP协议解析和H264码流提取

    一. h264基础概念 SODB: 数据比特串-->最原始的编码数据 RBSP: 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit&q ...

最新文章

  1. spring 测试demo乱码_spring框架的入门学习:AOP和面向切面的事务
  2. 8个步骤成功拖垮新业务线!
  3. 二代测序组装PK三代测序组装
  4. 美国专利商标局发布人工智能专利扩散分析报告
  5. python爬虫从入门到精通
  6. java输出set中的元素_老师,为什么遍历Set集合里的元素,一直都是有序的输出呢?...
  7. python变量的作用域及生命周期_Python——变量的作用域
  8. php header 无法跳转,PHP利用header跳转失效解决方法
  9. 送书!60 本签名书!
  10. 直接下载Google Play上APP的安装包
  11. 文件后缀名批量修改工具
  12. 致远OA漏洞学习——帆软组件 ReportServer 目录遍历漏洞
  13. iOS开发- 实现类似于陌陌点点和探探首页切换效果类似
  14. Fragment 可见性监听方案 - 完美兼容多种 case
  15. Hybrid App开发实战
  16. CentOS7 挂载新加硬盘(大于2T)操作说明
  17. android pm命令不可用,adb命令pm工具讲解
  18. 新出生的机器狗,打滚1小时后自己掌握走路,吴恩达开山大弟子最新成果
  19. 山东探植物园唯美规划 明年竣工成烟台“后花园”
  20. svn更换新仓库地址

热门文章

  1. wdcp导出mysql_phpmyadmin导入导出mysql(只适用WDCP系统)
  2. android arcgis 绘制圆_ArcGIS For Android 定位绘图工具 [中心点,误差圆]
  3. 【Linux】13.linux内核切换
  4. 深度学习卷积神经网络大事件一览
  5. Nginx(二)------nginx.conf 配置文件
  6. Log4j输出格式控制--log4j的PatternLayout参数含义
  7. Java 集合类图(转)
  8. Java并发编程(6):Runnable和Thread实现多线程的区别(含代码)
  9. Hadoop MapReduce的一些相关代码Code
  10. 深度学习(九)caffe预测、特征可视化python接口调用