
前言:
为什么需要编码呢?比如当前屏幕是1280*720.一秒24张图片.那么我们一秒的视频数据是
1280*720(位像素)*24(张) / 8(1字节8位)(结果:B) / 1024(结果:KB) / 1024 (结果:MB) = 2.64MB
一秒的数据有2.64MB数据量。1分钟就会有100多MB。这对用户来说真心是灾难。所以现在我们需要一种压缩方式减小数据的大小.在更低 比特率(bps)的情况下依然提供清晰的视频。
H264: H264/AVC是广泛采用的一种编码方式。我们这边会带大家了解。从大到小排序依次是 序列,图像,NALU,片,宏块,亚宏块,块,像素。
问题背景:
前面在讲封装格式过程中,都有一个章节讲解如何将H.264的NALU单元如何打包到TS、FLV、RTP中,解装刚好相反,怎么从这些封装格式里面解析出一个个NALU单元。NALU即是编码器的输出数据又是解码器的输入数据,所以在封装和传输时,我们一般处理对象就是NALU,至于NALU内部到底是什么则很少关心。甚至我们在编解码时,我们只需要初始化好x264编码库,然后输入YUV数据,它就会给你经过一系列压缩算法后输出NALU,或者将NALU输入到x264解码库就会输出YUV数据。
这篇文章就初步带你看下NALU能传输那些数据,NALU的类型和结构以及H264码流的层次,最后通过分析工具分析下裸码流记性验证,你可以选择感兴趣章节阅读。
NALU结构:
H.264的基本流(elementary stream),也叫裸流(没有加格式封装),就是一系列NALU的集合,如下图所示:
用Notepad十六进制形式打开,以annexb格式存储的h264裸流文件内容:
NALU结构分为两层,包含了视频编码层(VCL)和网络适配层(NAL):
视频编码层(VCL即Video Coding Layer) :负责高效的视频内容表示,这是核心算法引擎,其中对宏块、片的处理都包含在这个层级上,它输出的数据是SODB
网络适配层(NAL即Network Abstraction Layer) :以网络所要求的恰当方式对数据进行打包和发送,比较简单,先报VCL吐出来的数据SODB进行字节对齐,形成RBSP,最后再RBSP数据前面加上NAL头则组成一个NALU单元。
分层目的:
这样做的目的:VCL只负责视频的信号处理,包含压缩,量化等处理,NAL解决编码后数据的网络传输,这样可以将VCL和NAL的处理放到不同平台来处理,可以减少因为网络环境不同对VCL的比特流进行重构和重编码;
NLAU结构:
其实NALU的承载数据真实并不是RBSP(Raw Byte Sequence Playload)而是EBSP即(Extent Byte Sequence Payload),EBSP和RBSP的区别就是在 RBSP里面加入防伪起始码字节(0x03),因为H.264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置,那么RBSP数据内部是有可能含有这种字节序列的,为了防止解析错误,所以在RBSP数据流里面碰到0x 00 00 00 01的0x01前面就会加上0x03,解码时将NALU的EBSP中的0x03去掉成为RBSP,称为脱壳 *** 作。
原始字节序列负载 RBSP即Raw Byte Sequence Playload,因为VCL输出的 原始数据比特流 SODB即String Of Data Bits,其长度不一定是8bit的整数倍,为了凑成整数个字节,往往需要对SODB最后一个字节进行填充形成RBSP,所以从SODB到RBSP的示意图如下:
填充方式就是对VCL的输出数据进行8bit进行切分,最后一个不满8bit的字节第一bit位置1,然后后面缺省的bit置0即可
具体填充语法见下文:
原来文档中的解释:
主要的意思我的理解如下:
其中H.264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置。
所以H.264编码器输出的码流中每个帧开头3-4字节的start code起始码为0x00 00 01或者0x00 00 00 01。
上面我们分析了NALU的结构以及每层输出数据的处理方法,但是对于NALU的RBSP数据二进制表示的什么含义并不清楚,下面分析下NALU的类型。
1. NALU Header
头信息协议如上图。
举例说明:
这其中NALU的RBSP除了能承载真实的视频压缩数据,还能传输编码器的配置信息,其中能传输视频压缩数据的为slice。
那么如果NLAU传输视频压缩数据时,编码器没有开启DP(数据分割)机制,则一个片就是一个NALU,一个 NALU 也就是一个片。否则,一个片由三个 NALU 组成,即DPA、DPB和DPC,对应的nal_unit_type 类型为 2、3和4。
通常情况我们看到的NLAU类型就是SPS、PPS、SEI、IDR的slice、非IDR这几种。
上面站在NALU的角度看了NALU的类型、结构、数据来源、分层处理的原因等,其中NLAU最主要的目的就是传输视频数据压缩结果。那么站在对数据本身的理解上,我们看下H.264码流的层次结构。
H.264层次结构:
其实为了理解H.264是如何看待视频数据,先要了解下视频的形成过程。其实你把多副连续的有关联图像连续播就可以形成视频,这主要利用了人视觉系统的暂留效应,当把连续的图片以每秒25张的速度播放,人眼基本就感觉是连续的视频了。动画片就是这个原理:一张图像里面相邻的区域或者一段时间内连续图像的相同位置,像素、亮度、色温差别比较小,所以视频压缩本质就是利于这种空间冗余和时间上冗余进行编码,我们可以选取一段时间第一幅图像的YUV值,后面的只需要记录和这个的完整图像的差别即可,同时即使记录一副图像的YUV值,当有镜头完全切换时,我们又选取切换后的第一张作为基本图像,后面有一篇文章回讲述下目前视频压缩的基本原理。
所以从这里面就可以引申以下几个概念:
所以视频流分析的对象可以用下面的图片描述:
如果站在数据的角度分析NALU的层次关系,如下图:
这里视频帧被划分为一个片或者多个片,其中slice数据主要就是通过NLAU进行传输,其中slice数据又是由:
一个Slice = Silce + Slice Data
一帧图片跟 NALU 的关联 :
一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了,我们可以来看看 NALU 跟片的关系(slice)。
引用自: https://www.jianshu.com/p/9522c4a7818d
Slice片类型:
设置片的目的是限制误码的扩散和传输,也就是一帧图像中它们的编码片是互相独立的,这样假设其中一张图像的某一个片有问题导致解码花屏,但是这个影响范围就控制在这个片中,这就是我们平时看视频发现只有局部花屏和绿屏的原因。
Slice Data里面传输的是一个个宏块,宏块中的数据承载各个像素点YUV的压缩数据。一个图像通常被我们划分成宏块来研究,通常有16 16、16 8等格式。我们解码的过程也就是恢复这些像素阵列的过程,如果知道了每个像素点的亮度和色度,就能渲染出一张完整的图像,图像的快速播放即是视频。
刚才提到了宏块.那么什么是宏块呢?
宏块是视频信息的主要承载者。一个编码图像通常划分为多个宏块组成.包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中像素阵列。
一个宏块 = 一个16*16的亮度像素 + 一个8×8Cb + 一个8×8Cr彩色像素块组成。(YCbCr 是属于 YUV 家族的一员,在YCbCr 中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量)
其中宏块MB的类型:
宏块的结构:
H.264码流示例分析:
这里我们分析一下H.264的NLAU数据,其中包括了非VCL的NALU数据和VCL的NALU。
H.264码流的NLAU单元:
4. 这里由于没有数据分割机制,所以一个NALU承载一个片,同时一个片就是一个视频帧;
4.至于NALU的非VCL数据SPS、PPS、SEI各个字段的含义具体解析放到下篇文章,这个信息对于解码器进行播放视频很重要,很多播放问题都是这个数据有问题导致的;
上面看了视频的GOP序列,视频帧信息和片的组成,下面分析片中的宏块信息;
H.264的层次结构:
总结:
本文主要讲述了平时研究和分析视频流对象的层次,然后这些视频数据通过NALU传输时,NALU的类型和层次关系,以及NALU数据在不同层次的输出。最后用视频分析工具分析了H.264裸码流验证了上述层次关系。
所以对H.264数据分析时,一定要了解你现在分析的层次和框架,因为每个层次我们关心的数据处理对象是不一样的,这个非常重要。
一般H.264的分析工具都是收费的,也有一些免费和裁剪版本供大家学习和使用。推荐几个:Elecard StreamEye、CodecVisa、VideoEye、H264Analyzer、H264Visa等,有时需要交叉使用才能完成对你关心信息的分析,这些都放到我的Git上了,大家获取使用即可。
引用原文
25fps i帧间隔50 用分析软件查看可以看出每50帧一个i帧,并发送sps、pps、sei
sps、pps、sei、I帧绑在一起发送
原文出自 http://blog.csdn.net/season_hangzhou/article/details/51123979
h264常见的帧头数据为:
00 00 00 01 67 (SPS) :SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。所谓的编码视频序列即原始视频的一帧一帧的像素数据经过编码之后的结构组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集中。一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:
1:解码器需要在码流中间开始解码;
2:编码器在编码的过程中改变了码流的参数(如图像分辨率等);
00 00 00 01 68 (PPS) :除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater Set(PPS)。通常情况下,PPS类似于SPS,在H.264的裸码流中单独保存在一个NAL Unit中,只是PPS NAL Unit的nal_unit_type值为8;而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。
00 00 00 01 65 ( IDR 帧) : I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)
00 00 00 01 61 (P帧) :P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
start code有两种,四个字节的“00 00 00 01”和三个字节的“00 00 01”都是。
H264帧由NALU头和NALU主体组成。
NALU头由一个字节组成,它的语法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F: 1个比特.
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2个比特.
nal_ref_idc. 取00~11,似乎指示这个NALU的重要性,如00的NALU解码器可以丢弃它而不影响图像的回放,0~3,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。
Type: 5个比特.
nal_unit_type. 这个NALU单元的类型,1~12由H.264使用,24~31由H.264以外的应用使用,简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包
1 不分区,非IDR图像的片
2 片分区A
3 片分区B
4 片分区C
5 IDR图像中的片
6 补充增强信息单元(SEI)
7 SPS
8 PPS
9 序列结束
10 序列结束
11 码流借宿
12 填充
13-23 保留
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)