针对H264码流格式说明,网上已经有很多介绍了,最近也在看这个,这里根据自己理解,做个记录。
1、H264的功能分为两层:视频编码层(VLC,Video Coding Layer)和网络提取层(NAL, Network Abstraction Layer)。VLC数据即
编码处理的输出,它表示被压缩编码后的视频数据序列。在VCL数据传输或存储之前,这些编码的VCL数据先被映射或封装进NAL单
元。每个NAL单元包括一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)、一组对应于视频编码的NAL头信息。
这里说明下RBSP,EBSP,SODB。
SODB:(String of Data Bits)最原始的编码数据,无任何附加数据。
RBSP:在SODB的基础上增加了rbsp_stop_ont_bit(bit值为1)并用0按字节补位对齐。
EBSP:(Encapsulation Byte Sequence Packets)在RBSP的基础上增加了防止伪起始码字节(0x03)。
RBSP的基本结构是在原始编码数据的后面添加了结尾比特。一个bit"1",若干比特"0",以便字节对齐。
NAL单元序列如下:
图1
每个NAL单元包括NAL头+RBSP。
典型的RBSP单元如下:
现在来具体的说明下NAL头和RBSP。
NAL头结构为:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
NAL头说明:
针对Type的说明为:
然后我们根据一个实际的H264文件内容看下:
我们可以看到NAL的单元有SPS、PPS、SEI、IDR_SLICE等。NAL单元里面的头也显示了,头结构里面的nal_unit_type表示了RBSP是什么类型。这里要说明一点NAL_Size的大小是不包括startcode大小的。
接下来说明下起始码即刚刚上面提到的startcode。
起始码:如果NALU对应的Slice为一帧的开始,则用4字节表示,即0x00000001;否则用3个字节表示,即0x000001。
另外这里要用到上面提到的EBSP。为了使NALU的主体不包括起始码,在编码的时候每遇到两个字节(连续)的0,就插入一个字节0x03,以便和起始码相区别,解码时,则将相应的0x03删除掉。所以有时候NAL单元有可能是NAL头+EBSP组成。
NALU主体 编码时插入0x03
0x000000 >>>>>> 0x00000300
0x000001 >>>>>> 0x00000301
0x000002 >>>>>> 0x00000302
0x000003 >>>>>> 0x00000303
接下来对应刚刚的H264,其文件数据表示如下:
前面两个0x00000001对应PPS和SPS,第三个0x000001对应SEI。
2、接下来说说SPS、PPS和SEI。
SPS和PPS是用来初始化解码器的,没这些数据,视频数据是无法解析出来的。另外如果我们分析单独的H264文件,可以发现有的文件每个IDR帧前面都有PPS和SPS,有的只是开头才有。针对SPS和PPS,一般来说:
1)、如果是在直播的话,每个IDR帧前面都应该加上SPS和PPS,因为有的观众会中途进来观看。
2)、如果是本地稳定文件,可以在开头加上SPS和PPS,或者都加上,这个根据具体需要来。
另外说明下SEI,有的H264文件有SEI,有的则没有,这说明SEI对文件的播放并无太大影响。
SEI(Supplemental Enhancement Information):辅助增强信息。这里面可以存放一些影片简介,版权信息或者作者自己添加的一些信息。
参考文章:
http://www.cnblogs.com/skyseraph/archive/2012/04/01/2429384.html
http://blog.csdn.net/mandagod/article/details/51174680
---------------------
原文:https://blog.csdn.net/H514434485/article/details/52064945
版权声明:本文为博主原创文章,转载请附上博文链接!
原文地址:http://blog.csdn.net/yangzhongxuan/article/details/8003494
名词解释
场和帧 : 视频的一场或一帧可用来产生一个编码图像。在电视中,为减少大面积闪烁现象,把一帧分成两个隔行的场。
片: 每个图象中,若干宏块被排列成片的形式。片分为I片、B片、P片和其他一些片。
I片只包含I宏块,P片可包含P和I宏块,而B片可包含B和I宏块。
I宏块利用从当前片中已解码的像素作为参考进行帧内预测。
P宏块利用前面已编码图象作为参考图象进行帧内预测。
B宏块则利用双向的参考图象(前一帧和后一帧)进行帧内预测。
片的目的是为了限制误码的扩散和传输,使编码片相互间是独立的。
某片的预测不能以其它片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其它片中去。
宏块 : 一个编码图像通常划分成若干宏块组成,一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个8×8 Cr彩色像素块组成。
数据之间的关系:
H264结构中,一个视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成,一个宏块由16x16的yuv数据组成。宏块作为H264编码的基本单位。
H264编码过程中的三种不同的数据形式:
SODB 数据比特串 ---->最原始的编码数据,即VCL数据;
RBSP 原始字节序列载荷 ---->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐;
EBSP 扩展字节序列载荷 ---- > 在RBSP基础上填加了仿校验字节(0X03)它的原因是: 在NALU加到Annexb上时,需要添加每组NALU之前的开始码StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示ox000001(是一帧的一部分)。另外,为了使NALU主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉。也称为脱壳操作。
H264/AVC 的分层结构
H.264的主要目标是:
1.高的视频压缩比;
2.良好的网络亲和性;
为了完成这些目标H264的解决方案是:
1.VCL video coding layer 视频编码层;
2.NAL network abstraction layer 网络提取层;
其中,VCL层是对核心算法引擎,块,宏块及片的语法级别的定义,他最终输出编码完的数据 SODB;
NAL层定义片级以上的语法级别(如序列参数集和图像参数集,针对网络传输),
同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传送,NAL层将SODB打包成RBSP然后加上NAL头,组成一个NALU(NAL单元);
H264网络传输的结构
H264在网络传输的是NALU,NALU的结构是:NAL头+RBSP,实际传输中的数据流如图所示:
NALU头用来标识后面的RBSP是什么类型的数据,他是否会被其他帧参考以及网络传输是否有错误。
NALU头结构
长度:1byte
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
1.forbidden_bit: 禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
2.nal_reference_bit: nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。
不同类型的NALU的重要性指示如下表所示。
nal_unit_type |
NAL类型 |
nal_reference_bit |
0 |
未使用 |
0 |
1 |
非IDR的片 |
此片属于参考帧,则不等于0, 不属于参考帧,则等与0 |
2 |
片数据A分区 |
同上 |
3 |
片数据B分区 |
同上 |
4 |
片数据C分区 |
同上 |
5 |
IDR图像的片 |
5 |
6 |
补充增强信息单元(SEI) |
0 |
7 |
序列参数集 |
非0 |
8 |
图像参数集 |
非0 |
9 |
分界符 |
0 |
10 |
序列结束 |
0 |
11 |
码流结束 |
0 |
12 |
填充 |
0 |
13..23 |
保留 |
0 |
24..31 |
不保留 |
0 |
所谓参考帧,就是在其他帧解码时需要参照的帧。比如一个I帧可能被一个或多个B帧参考,一个B帧可能被某个P帧参考。
从这个表我们也可以看出来,DIR的I帧是非常重要的,他一丢,那么这个序列的所有帧都没办法解码了;
序列参数集和图像参数集也很重要,没有序列参数集,这个序列的帧就没法解;
没有图像参数集,那用到这个图像参数集的帧都没法解。
3.nal_unit_type:NALU类型取值如下表所示。
nal_unit_type |
NAL类型 |
C |
0 |
未使用 |
|
1 |
非IDR图像中不采用数据划分的片段 |
2,3,4 |
2 |
非IDR图像中A类数据划分片段 |
2 |
3 |
非IDR图像中B类数据划分片段 |
3 |
4 |
非IDR图像中C类数据划分片段 |
4 |
5 |
IDR图像的片 |
2,3 |
6 |
补充增强信息单元(SEI) |
5 |
7 |
序列参数集 |
0 |
8 |
图像参数集 |
1 |
9 |
分界符 |
6 |
10 |
序列结束 |
7 |
11 |
码流结束 |
8 |
12 |
填充 |
9 |
13..23 |
保留 |
|
24..31 |
不保留(RTP打包时会用到) |
|
RTP 打包时的扩展类型
24 | STAP-A | Single-time aggregation packet |
25 | STAP-B | Single-time aggregation packet |
26 | MTAP16 | Multi-time aggregation packet |
27 | MTAP24 | Multi-time aggregation packet |
28 | FU-A | Fragmentation unit |
29 | FU-B | Fragmentation unit |
30-31 | undefined |
RBSP
RBSP数据是下表中的一种
RBSP类型 | 所写 | 描述 |
参数集 | PS | 序列的全局信息,如图像尺寸,视频格式等 |
增强信息 | SEI | 视频序列解码的增强信息 |
图像界定符 | PD | 视频图像的边界 |
编码片 | SLICE | 编码片的头信息和数据 |
数据分割 | DP片层的数据,用于错误恢复解码 | |
序列结束符 | 表明一个序列的结束,下一个图像为IDR图像 | |
流结束符 | 表明该流中已没有图像 | |
填充数据 | 亚元数据,用于填充字节 |
从前面的分析我们知道,VCL层出来的是编码完的视频帧数据,
这些帧可能是I、B、P帧,而且这些帧可能属于不同的序列,再者同一个序列还有相对应的一套序列参数集和图片参数集等等,
所以要完成视频的解码,不仅需要传输VCL层编码出来的视频帧数据,还需要传输序列参数集、图像参数集等数据。
参数集:包括序列参数集 SPS 和图像参数集 PPS
SPS 包含的是针对一连续编码视频序列的参数,如标识符 seq_parameter_set_id、帧数及 POC 的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等。
PPS对应的是一个序列中某一幅图像或者某几幅图像,
其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。
数据分割:组成片的编码数据存放在 3 个独立的 DP(数据分割,A、B、C)中,各自包含一个编码片的子集。
分割A包含片头和片中每个宏块头数据。
分割B包含帧内和 SI 片宏块的编码残差数据。
分割 C包含帧间宏块的编码残差数据。
每个分割可放在独立的 NAL 单元并独立传输。
NAL的开始和结束
编码器将每个NAL各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL的分界,并依次取出NAL进行解码。
每个NAL前有一个起始码 0x00 00 01(或者0x00 00 00 01),解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。
同时H.264规定,当检测到0x000000时,也可以表征当前NAL的结束。那么NAL中数据出现0x000001或0x000000时怎么办?H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据(脱壳操作)。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。
NALU的顺序要求
H.264/AVC标准对送到解码器的NAL单元顺序是有严格要求的,如果NAL单元的顺序是混乱的,必须将其重新依照规范组织后送入解码器,否则解码器不能够正确解码。
1.序列参数集NAL单元
必须在传送所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的序列参数集NAL单元。
所谓重复的详细解释为:序列参数集NAL单元都有其专门的标识,如果两个序列参数集NAL单元的标识相同,就可以认为后一个只不过是前一个的拷贝,而非新的序列参数集。
2.图像参数集NAL单元
必须在所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的图像参数集NAL单元,这一点与上述的序列参数集NAL单元是相同的。
3.不同基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元在顺序上不可以相互交叉,即不允许属于某一基本编码图像的一系列片段(slice)单元和数据划分片段(data partition)单元中忽然出现另一个基本编码图像的片段(slice)单元片段和数据划分片段(data partition)单元。
4.参考图像的影响:如果一幅图像以另一幅图像为参考,则属于前者的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于后者的片段和数据划分片段之后,无论是基本编码图像还是冗余编码图像都必须遵守这个规则。
5.基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于相应冗余编码图像的片段(slice)单元和数据划分片段(data partition)单元之前。
6.如果数据流中出现了连续的无参考基本编码图像,则图像序号小的在前面。
7.如果arbitrary_slice_order_allowed_flag置为1,一个基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元的顺序是任意的,如果arbitrary_slice_order_allowed_flag置为零,则要按照片段中第一个宏块的位置来确定片段的顺序,若使用数据划分,则A类数据划分片段在B类数据划分片段之前,B类数据划分片段在C类数据划分片段之前,而且对应不同片段的数据划分片段不能相互交叉,也不能与没有数据划分的片段相互交叉。
8.如果存在SEI(补充增强信息)单元的话,它必须在它所对应的基本编码图像的片段(slice)单元和数据划分片段(data partition)单元之前,并同时必须紧接在上一个基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元后边。假如SEI属于多个基本编码图像,其顺序仅以第一个基本编码图像为参照。
9.如果存在图像分割符的话,它必须在所有SEI 单元、基本编码图像的所有片段slice)单元和数据划分片段(data partition)单元之前,并且紧接着上一个基本编码图像那些NAL单元。
10.如果存在序列结束符,且序列结束符后还有图像,则该图像必须是IDR(即时解码器刷新)图像。序列结束符的位置应当在属于这个IDR图像的分割符、SEI 单元等数据之前,且紧接着前面那些图像的NAL单元。如果序列结束符后没有图像了,那么它的就在比特流中所有图像数据之后。
11.流结束符在比特流中的最后。
h264有两种封装,
一种是annexb模式,传统模式,有startcode,SPS和PPS是在ES中
一种是mp4模式,一般mp4 mkv会有,没有startcode,SPS和PPS以及其它信息被封装在container中,每一个frame前面是这个frame的长度
很多解码器只支持annexb这种模式,因此需要将mp4做转换:
在ffmpeg中用h264_mp4toannexb_filter可以做转换
实现:
注册filter
avcbsfc = av_bitstream_filter_init("h264_mp4toannexb");
转换bitstream
av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc,
AVCodecContext *avctx, const char *args,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size, int keyframe)
H264元素的分层结构
H.264编码器输出的Bit流中,每个Bit都隶属于某个句法元素。句法元素被组织成有层次的结构,分别描述各个层次的信息。
在H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次。在这样的结构中,每一层的头部和它的数据部分形成管理与被管理的强依赖关系,头部的句法元素是该层数据的核心,而一旦头部丢失,数据部分的信息几乎不可能再被正确解码出来,尤其在序列层及图像层。
在 H.264 中,分层结构最大的不同是取消了序列层和图像层,并将原本属于序列和图像头部的大部分句法元素游离出来形成序列和图像两级参数集,其余的部分则放入片层。
参数集是一个独立的数据单位,不依赖于参数集外的其他句法元素。一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用,同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发出新的参数集。
复杂通信中的码流中可能出现的数据单位:
IDR: 在H.264中,图像以序列为单位进行组织。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。 IDR 图像一定是 I 图像,但 I 图像不一定是 IDR 图像。I帧之后的图像有可能会使用I帧之前的图像做运动参考。
H264码流结构
1. H264分层结构
H.263定义的码流结构是分级结构,共四层。自上而下分别为:图像层(picturelayer)、块组层(GOB layer)、宏块层(macroblock layer)和块层(block layer)。而与H.263相比,H.264的码流结构和H.263的有很大的区别,它采用的不再是严格的分级结构。
H.264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)VCL数据即被压缩编码后的视频数据序列。在VCL数据要封装到NAL单元中之后,才可以用来传输或存储。
NAL单元格式[2] 表1所示:
表1 NAL单元格式 |
|||
NAL头 |
RBSP |
NAL头 |
RBSP |
RBSP:封装于网络抽象单元的数据称之为原始字节序列载荷RBSP,它是NAL的基本传输单元。其中,RBSP又分为视频编码数据和控制数据。其基本结构是:在原始编码数据的后面填加了结尾比特。一个bit“1”若干比特“0”,以便字节对齐。
RBSP的类型:
RBSP 类型之一 PS: 包括序列参数集 SPS 和 图像参数集 PPS
SPS 包含的是针对一连续编码视频序列的参数,如标识符 seq_parameter_set_id、帧数及 POC 的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等。
PPS对应的是一个序列中某一幅图像或者某几幅图像,其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。
NALU类型
标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5及12的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。
0:未规定
1:非IDR图像中不采用数据划分的片段
2:非IDR图像中A类数据划分片段
3:非IDR图像中B类数据划分片段
4:非IDR图像中C类数据划分片段
5:IDR图像的片段
6:补充增强信息 (SEI)
7:序列参数集
8:图像参数集
9:分割符
10:序列结束符
11:流结束符
12:填充数据
13 – 23:保留
24 – 31:未规定
2. H.264码流结构图
通过相关知识的查阅,概括出H.264的码流结构图[2]如图1所示:
图1 H.264的码流结构 |
3 H.264码流分析的应用
在有些时候,需要从H.264码流中直接取得相关信息(如:图像的宽度和图像的高度等等信息)。下面介绍下取得相关信息的方法:
图像的相关信息存储在网络提取层(NAL)的RBSP结构中,要取得图像的相关信息,既要获得图像的相关位。需依据RBSP结构,获得pic_width_in_mbs_minus1和pic_height_in_map_units_minus1两个值,那么宽度为(pic_width_in_mbs_minus1+1)*16,高度为(pic_height_in_map_units_minus1+1)*16,但是有些情况还得考虑nNum_Ref_Frames的值,一般为1。
3.1获得试验数据
设备:SUNNIC(IP Cam)
名字:ST100factory
Firmware版本:p8b8
视频格式:H.264
(1)将设备分辨率设成176*144,使用Ethereal等抓包工具抓得一组数据,并去掉相应的RTP头后,该数据为0x00,0x00,0x00,0x01,0x67,0x42,0x00,0x1E,0x99,0xA0,0xB1,0x31。
(2)将设备分辨率设成720*240,使用Ethereal等抓包工具抓得一组数据,并去掉相应的RTP头后,该数据为0x00,0x00,0x00,0x01,0x67,0x42,0xE0,0x1E,0xDA,0x82,0xD1,0xF1。
(3)将设备分辨率设成720*480,使用Ethereal等抓包工具抓得一组数据,并去掉相应的RTP头后,该数据为0x00,0x00,0x00,0x01,0x67,0x42,0xE0,0x1E,0xDB,0x82,0xD1,0xF1。
简介
视频编解码网上介绍很多,整理了不少笔记,经过近一年的学习,把整理的笔记反馈到社区,也加深一遍自己的理解。欢迎指正错误和讨论学习,谢谢浏览。
H264是一种视频压缩标准。
根据不同类别,编码器会使用不同类型的帧,I帧、P帧和B帧。
这里写图片描述
(图出自H.264视频压缩标准白皮书)
I帧:自身可以通过视频解压算法解压成一张单独的完整的图片。P帧只需要参考前面的I帧或P帧,而B帧则需要同时参考前面和后面的I帧或P帧。
在H.264基准类中,仅使用I帧和P帧以实现低延时,因此是网络摄像机和视频编码器的理想选择。
网络提取层(NAL network abstraction layer )和视频编码层(VCL video coding layer)
H264有严格的规范,以00 00 00 01分割之后的下一个字节就是NALU类型,用来描述此帧的意义。
NALU类型:
(1)第1位禁止位,值为1表示语法出错
(2)第2~3位为参考级别
(3)第4~8为是nal单元类型
以通常的首帧来看:
这里写图片描述
打印出来为103(用十六进制软件分析的话值为67),转为二进制为0110 0111,4-8为00111,转为十进制为7,释义见下:
1
0,未使用
1,不分区,非IDR图像的片
2,片分区A 3,片分区B 4,片分区C
5,IDR图像的片段
6,补充增强信息单元(SEI)
7,序列参数集sps
8,图像参数集pps
9,分界符 10,序列结束符 11,流结束符 12填充 13..23保留 24..31未使用
即是sps,仔细看还有第二个分隔符,为104释义为8,即pps。这一帧内包含了H264的大部分精华,在使用中把此帧喂入解码器是很关键的,简单介绍一下:
SPS:包括了一个图像序列的所有信息(包含的是针对一连续编码视频序列的参数,如标识符seq_parameter_set_id、帧数及POC的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等)
PPS:包括了一个图像所有片的信息(对应的是一个序列中某一副图像或者某几幅图像,参数如标识符pic_parameter_set_id、可选的seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等)
在后面的实践Demo中会观察到此帧的重要性。
接下来的第二帧是实际意义上的关键帧(I帧):
这里写图片描述
101释义是5,代码表示的话是:
int value = buf[4] & 0x0f;//nalu, 5是I帧, 7是sps 8是pps.
1
VCL即编码处理的输出,它表示被压缩编码后的视频数据序列,这里有几个知识点,差分编码、运动补偿、帧内预测,有心的朋友可以去详细搜索学习,知识点这里用笔记里的记录做一个简单介绍:
差分编码:包括H.264在内的大多数视频压缩标准都采用这种方法,只有第一个图像(I帧)是将全帧图像信息进行编码。在后面的两个图像(P帧)中,其静态部分将参考第一个图像,而仅对运动部分使用运动矢量进行编码,从而减少发送和存储的信息量。
运动补偿:将一个帧分为一系列的宏块。然后,通过在参考帧中查找匹配块的方式,逐块地构建一个新帧。
帧内预测:对帧中每个宏块内较小的像素块进行连续预测。
码率、帧率、分辨率
这三个参数直观的影响到视频流的大小,视频清晰度等观看体验。码率一定的情况下,分辨率与清晰度成反比关系:分辨率越高,图像越不清晰,分辨率越低,图像越清晰。分辨率一定的情况下,码率与清晰度成正比关系,码率越高,图像越清晰;码率越低,图像越不清晰。
下面是一张参考图:
这里写图片描述
抱歉,出处忘记了…
分辨率常用的有:
720*480(标清480p)
1280*720(高清720p)
1920*1080(全高清1080p, 1920*2 * 1080*2 = 4k)
3840×2160(八百万像素的超高清4k,往上再4倍就是8k超高清)
其实含480p以下为标清,480p以上为高清,经典的有720p、1080i、1080p,1080p又称全高清,1080p以上都为超清。(i隔行扫描多用于电视机信号,p逐行扫描)
帧率:
在1秒钟时间里传输的图片的帧数,影响画面流畅度,常说的24帧电影、在游戏上的FPS,都是这个概念,通常手机上20帧左右即可。
ps:Gop是指多少秒一个I帧。
码率:
右键看一个视频的属性,详细里有一个总比特率,计算方式是:
【码率】(kbps)=【文件大小】(字节 )X8 /【时间】(秒)
码率单位是kbps即千位每秒。也就是取样率(和音频的采样率有区别,采样率单位是Hz,表示每秒采样次数),单位时间内取样率越大,精度就越高,处理出来的文件就越接近原始文件,但是文件体积与取样率是成正比的,所以几乎所有的编码格式重视的都是如何用最低的码率达到最少的失真。码率越高越清晰,反之则画面粗糙而多马赛克,1080P不一定比720P清晰,打开看码率对比最实际~
其它
IDR帧和I帧:首个I帧叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像,不过I帧不一定是IDR图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空(DPB,DecodedPictureBuffer 参考帧列表),将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。
H265和H264
同样的画质和同样的码率,H.265比H.264 占用的存储空间要少理论50%;如果存储空间一样大,那么意味着,在一样的码率下H.265会比H2.64 画质要高一些理论值是30%~40%,但是因为部分专利价太高(部分公司创建联盟,收取专利费),计算复杂度(编码要求的硬件性能较高) 普及难度直线上升。
逐行扫描和隔行扫描:详细画面效果参见下
http://blog.csdn.net/syb773849846/article/details/52120030
带宽,码流,在线人数之间的关系:
带宽 / (码流 * 8) = 同时在线人数
文件大小 = 时间×码率/8
一个视频文件的大小为5.86M,播放时长为3分7秒:
1:该文件对应的码流就是
5.86 * 1024 * 1024 * 8 / (3 * 60 + 7) =262872.95657754bps
2:10M独享带宽能支撑的同时在线人数
10* 1024 * 1024 / 262872.95657754 =39.889078498294
3:支撑1000人同时在线的系统最少需要的带宽数为
262872* 1000 / (1024 * 1024) = 250.69427490234M
场频:画面刷新次数。
行频:屏幕的水平扫描频率,以Hz为单位。它越大就意味着显示器可以提供的分辨率越高,稳定性越好。
行频 = height(垂直分辨率) * 1.04~1.08 * 场频
YUV格式:一种颜色编码方式,可以转化成我们常用的RGB色值。Y代表亮度,UV代表色值,Y可以显示一张完整的黑白图像。这样的设计解决了黑白电视和彩色电视的兼容,并且YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。
基本概念整理:
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。在没有B帧存在的情况下DTS的顺序和PTS的顺序应该是一样的。DTS主要用于视频的解码,在解码阶段使用.PTS主要用于视频的同步和输出.在display的时候使用.在没有B frame的情况下.DTS和PTS的输出顺序是一样的.
SPS(序列的参数集):包括了一个图像序列的所有信息(包含的是针对一连续编码视频序列的参数,如标识符seq_parameter_set_id、帧数及POC的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等)
PPS(图像的参数集):包括了一个图像所有片的信息(对应的是一个序列中某一副图像或者某几幅图像,参数如标识符pic_parameter_set_id、可选的seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等)
流地址:HLT、RTMP等视频流地址的话,开源的很多,分享一个从直播流程到搭建服务器的良心博客:
http://blog.csdn.net/huaxun66/article/details/53427771
总结
音视频解决方案很多,ffmpeg是绕不过去的开源技术,ndk开发在这一块的重要性可想而知。当然,无论是视频流地址还是各种开源的播放器,也都离不开协议这一基础概念。了解H264,窥一斑而知全豹,渐进学习慢慢就能看到整个音视频领域的大致规模。共勉~
录屏用udp发送到其它屏幕显示
---------------------
原文:https://blog.csdn.net/pds574834424/article/details/78150474
版权声明:本文为博主原创文章,转载请附上博文链接!
这段时间一直在进行编写H264文件的解析类,因此对于H264文件的格式有了初步的了解,官方文档也看了个大概。这篇文章主要是总结了一些为解码H264文件而需要的一些前期知识,话不多说,下面是干货,有些是自己在wiki上翻译过来的,有些是看官方文档后发现的一些关键部分。
首先了解一下视频文件中的一些知识:
Video compression picture types(视频压缩图像类型)
1. 视频帧的压缩使用了不同的算法,这些应用于视频帧的不同算法被称为图像类型或者帧类型。
a. I - 该帧可压缩程度最低,也不需要通过其他视频帧解码。
b. P - 该帧可以引用前面的帧的数据来解压缩并且相对于I帧来说,该帧可以压缩程度更高。
c. B - 该帧可以引用前面的帧和后面的帧的数据,从而压缩程度最高。
1
2
3
4
这里写图片描述
(图为一段视频帧,由2个关键帧(I-frame),1个向前预测帧(P-frame)和1个双向预测帧(B-frame)构成)
这里共有三种类型的帧被用在视频压缩技术中:I-帧,P-帧 和 B-帧。
I-帧是一种“内编码图片”,实际上是一种信息十分详细的图片,跟传统的静态图片文件相似。P-帧 和 B-帧 只存储了部分的图片信息,因此相比于I-帧来说,他们需要更少的存储空间,也因此而提高了压缩速率。
P-帧(“预测图片”)只保存图片中与前一帧的不同的地方。比如,在一个场景中,汽车行驶在静止的背景中,只有汽车的运动需要被编码。编码器无需存储未改变的背景像素信息到P-帧中,因此而节省了空间,P-帧 也同样被称为 delta-帧。
B-帧(“双向预测图片”)通过比较现在的帧和前后的帧的不同来指定具体存储的内容,从而节省了更多的空间。
Slices(条带)
一个条带是一个帧中比较独特的区域,不同于该帧中的其他区域。在最新的国际标准中,这里已经存在 I-slices(I-条带),P-slices(P-条带)和 B-slices(B-条带)。
Macroblocks(宏块)
普遍来说,图片(帧)被分割成许多宏块和个体预测类型能够在宏块层被挑选出来,并且整个图片(帧)中的宏块与宏块并不相同,如下:
1. I-帧 只能包含内部宏块
2. P-帧 既能包含内部宏块,也能包含预测宏块
3. B-帧 能包含内部宏块,预测宏块和双向预测宏块
进一步说,在视频编解码H.264中,帧能够被分割成宏块序列,该宏块序列被称为条带(slices)。编解码过程不是使用I,B和P 帧类型作为选集,而是通过每个条带明确无误的选择出预测类型。在H.264文件中也能找到几个额外的 帧/条带 类型:
1. SI-帧/条带(Switching I);使编码流之间的转换变得更加容易;包含了 SI-宏块(一种内部编码宏块的特殊类型)
2. SP-帧/条带(Switching P);使编码流之间的转换变得更加容易;包含了 P-宏块 和/或 I-宏块。
3. muti-帧 动态预测(包括16个引用帧,或32个引用域)
Intra coded frames/slices(I-frames/slices or Key frames)(内编码帧/条带(I帧/条带 或者 关键帧))
1. I-帧 编码不依赖于其他帧
2. 可能被编码器生成并创建于一个随机访问点(来允许解码器在图片的该位置开始解码)
3. 当区分图片细节时也可能为了避免产生有效的 P-帧 或 B-帧 而生成
4. 普遍来说比其他类型的帧需要更多的位数来编码
1
2
3
4
通常,I-帧 被用来随机访问和作为其他图片(帧)解码时的引用。内部刷新周期通常为半秒(在数字电视广播和DVD存储介质中很常见)。在一些环境下,也可能需要更长的时间周期。比如,在视频会议系统中,通常会间歇的发送I-帧(不频繁的发送)。
Predicted frames/slices(P-frames/slices)(预测帧/条带(P-帧/条带))
1. 为了解码的需要,要优先解码一些其他图片(帧)
2. 可能包含图像数据或运动向量偏移量或者两者的组合
3. 能够按照解码顺序引用前面的图片
4. 旧的设计标准在解码时只使用一张前面的解码图片作为引用,并且需要该图片也要在P-帧图片之前显示。
5. 在H.264文件中,在解码过程中能够使用多个先前解码的图片作为引用,并且相对于被用来做预测的图片帧,该帧能够以任意顺序显示。
6. 通常,相对于编码I-帧来说,编码预测帧需要更少的比特位数。
1
2
3
4
5
6
Bi-directional predicted frames/slices(B-frames/slices)(双向预测帧/条带(B-帧/条带))
1. 为了解码的需要,要优先解码一些其他图片(帧)
2. 可能包含图像数据或运动向量偏移量或者两者的组合
a. 旧的标准有一个关于整个帧的全局运动补偿向量
b. 一些标准关于每个宏块都有一个运动补偿向量
3. 一些标准允许每个宏块使用两个运动补偿向量(双向预测)
4. 在旧的设计标准中(比如MPEG-2),B-帧从不用来作为其他图片(帧)的预测引用。因此,B-帧可以用一个稍低质量的编码(编码量少的)来完成,因为一些细节的丢失不会对预测后续图片(帧)的质量带来影响。
5. 在H.264文件中,有可能被作为解码其他图片(帧)的引用(由编码器决定)
6. 在旧的设计标准中(比如MPEG-2),在解码过程中会使用前面的2帧解码图片作为引用,并且需要其中一帧在B-帧之前显示,另外一帧在B-帧之后显示。
7. 在H.264文件中,能够使用1,2或者更多先前解码的图片(帧)作为解码过程中的引用,并且相对于被用来做预测的图片帧,该帧能够以任意顺序显示。
8. 通常,相对于编码I-帧 和 P-帧来说,编码该帧需要更少的比特位数。
1
2
3
4
5
6
7
8
9
10
有了已上的基本知识以后,我们已经了解到I-帧是关键帧,并且是占空间最多的帧,因为很多数据都是内编码的,而P-帧是预测帧,需要借助前面的帧来作为引用,从而得到完整的数据,而B-帧是双向预测帧,需要借助前面和后面的帧来作为引用。因此一般来说占用空间排序:I-帧 > P-帧 > B-帧 (I-帧占用空间最多)。
对于如何解析帧类型(I,P还是B帧等等),需要用到nal_unit_type这一字段,在Nalu中它占据第一个字节的3~8位,Nalu的第一个字节(忽略起始码startcode:0x000001 or 0x00000001)如下:
forbidden_zero_bit: 1 bit
nal_ref_idc: 2 bit
nal_unit_type: 5 bit
官方文档也给出了语义解析,以伪代码的形式给出的:
这里写图片描述
后面的f(1),u(2),u(5)是代表要使用的解析方法,前面加深的粗体forbidden_zero_bit,nal_ref_idc,nal_unit_type称为syntax_element,语法元素,即:解析这些语法元素要用到后面的f(1),u(2),u(5)方法,下面是官方文档中给出的解析方法:
这里写图片描述
这里写图片描述
这里面的解析方法,个人感觉只有Exp-Golomb-coded的解析有些麻烦(解析具体是什么帧需要用到该方法),其他的用简单的几个位操作就能轻松应对了吧,下面讲讲指数哥伦布编码的相关信息:
Exponential-Golomb(universal code):指数哥伦布编码
以下是wiki上给出的解释(给翻译过来的,英文水平不好,请轻喷,thanks)
1. (默认k=0的形式)
a. 将原数加1写成二进制形式
b. 将1中的二进制的位数减一并记下,添加这么多个0到1中二进制位数的前面
例子:
0 ⇒ 1 ⇒ 1
1 ⇒ 10 ⇒ 010
2 ⇒ 11 ⇒ 011
3 ⇒ 100 ⇒ 00100
4 ⇒ 101 ⇒ 00101
5 ⇒ 110 ⇒ 00110
6 ⇒ 111 ⇒ 00111
7 ⇒ 1000 ⇒ 0001000
8 ⇒ 1001 ⇒ 0001001
将其推广到负数范围表示,方法如下:
(1) 非负整数x≤0映射到偶数-2x上
(2) 正整数x>0映射到奇数2x - 1上
例子:
0 ⇒ 0 ⇒ 1 ⇒ 1
1 ⇒ 1 ⇒ 10 ⇒ 010
−1 ⇒ 2 ⇒ 11 ⇒ 011
2 ⇒ 3 ⇒ 100 ⇒ 00100
−2 ⇒ 4 ⇒ 101 ⇒ 00101
3 ⇒ 5 ⇒ 110 ⇒ 00110
−3 ⇒ 6 ⇒ 111 ⇒ 00111
4 ⇒ 7 ⇒ 1000 ⇒ 0001000
−4 ⇒ 8 ⇒ 1001 ⇒ 0001001
2. 衍生到k阶(k≥0)形式
a. 用于编码更大的数,编码后形成更少的二进制位(编码比较小的数需要使用更多的二进制位数)
b. 要以k阶形式编码一个非负整数x,方法如下:
i. 以0阶方式编码⌊x/2^k ⌋
ii. 将x取模2^k
等价方式:
i. 使用0阶exp-Golomb编码方式编码x+2k−1
ii. 从编码结构中删除前面的k个0位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
这里写图片描述
官方文档有关于解析哥伦布指数编码的方法,of course,仍然是以伪代码的形式给出: )
这里写图片描述
这里写图片描述
这里写图片描述
再继续说解析帧类型的方法…
依然是官方文档….
发现了这么一个东东
这里写图片描述
这应该就是我们要分析该Nalu是什么帧类型的关键吧!于是找找它在哪里出现过
这里写图片描述
很明显,第3行出现了,再找找slice_header出现的地方
这里写图片描述
这里写图片描述
这里写图片描述
然后再继续逆向查找它们分别出现在哪里,结果最后都在一张表中找到了
这里写图片描述
于是开始分析:
对于slice_layer_without_partitioning_rbsp:
nal_unit_type = 1,5,19
对于slice_data_partition_a_layer_rbsp:
nal_unit_type = 2
对于slice_layer_extension_rbsp:
nal_unit_type = 20
于是下面就是具体分析nal_unit_type的值来进行相应处理的过程:
首先,在代码中枚举如下NAL单元类型(参考了下ffmpeg)
这里写图片描述
相应的,枚举如下Slice类型,根据Slice类型即能得出帧类型
这里写图片描述
(具体为什么要都定义2种,因为根据官方文档来写的,其实每个第二种类型数值-5即为第一种类型,没有仔细研究)
具体的解析代码在我的GitHub上,欢迎参观:yijiazhen的GitHub
刚刚接触H264文件解析不久,如有分析错误或是不到位的地方,恳请各路好汉批评指正,希望可以互相交流,共同提高。
---------------------
原文:https://blog.csdn.net/yijiazhen/article/details/51447510?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!