文章目录
H264
1. NALU
H264码流可以分为两层,NAL层(网络抽象层)和VCL层(视频编码层)。
VCL层负责有效表示视频数据的内容,而NAL层则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。
构成H264码流的基本单位是NAL单元,NAL单元简称为NALU,它包含一个字节的头信息(NALU Header) 和 **原始字节序列载荷(RBSP)**的字节流。
NALU结构如图:
1.1 NALU Header
以H264的NALU Header为例
NALU头由一个字节组成,它的语法如下:
字段 | 大小 | 含义 |
---|---|---|
F | 1bit | forbidden_zero_bit,H264定义此位必须为0 |
NRI | 2bit | nal_ref_idc,0~3,标识这个NALU的重要性(3最高) |
Type | 5bit | nal_unit_type,NALU的类型 |
NALU Header分为3个部分:
- forbidden_zero_bit:禁止位,必须为0。
- nal_ref_idc:用来表示当前NALU的优先级,数值越大优先级越高。如00的NALU解码器可以丢弃它而不影响图像的显示,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片(Slice),序列参数集(SPS),或是图像参数集(PPS)这些重要的单位时,nal_ref_idc必需大于0。
- nal_unit_type :表示NALU类型。
1.1.1 nal_unit_type
nal_unit_type数值 | 含义 |
---|---|
0 | 未使用 |
1 | 不分区,非IDR图像的Slice |
2 | 片分区A |
3 | 片分区B |
4 | 片分区C |
5 | IRD图像的Slice(IDR) |
6 | 补充增强信息单元(SEI) |
7 | 序列参数集(SPS) |
8 | 图像参数集(PPS) |
9 | 分隔符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13~23 | 保留 |
24~25 | STAP-A/STAP-B 单一时间的组合包 |
26~27 | MTAP16/MTAP24 多个时间的组合包 |
28~29 | FU-A/FU-B 分片的单元 |
30~31 | 没有定义 |
nal_unit_type为1, 2, 3, 4, 5的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。此外1~12由H.264使用,24~31由H.264以外的应用使用。
2. 码流格式
一个NALU包中的数据并不包含它的大小(长度)信息,因此不能简单的连接NALU包来建立一个流,因为你不知道一个包从哪里结束,另一个包从哪里开始。
H264有两种码流格式解决上述问题:
- Annex B格式
- AVCC格式
2.1 Annex B格式
以起始码(0x00000001)作为H264/H265作为NALU边界。用起始码定位NALU边界存在一个问题,即NALU中可能存在与起始码相同的数据,这里引入防竞争字节。
防竞争字节:为了使NALU主体中不包括与开始码相冲突的,在编码时,就插入一个字节的0x03;解码时将0x03去掉。
如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据。
2.2 AVCC格式
使用NALU长度,固定字节,通常为4字节,分隔NALU;一般在每个NALU头部为4字节大端格式的长度字节,在一组GOP的头部包含extradata结构,用于存储sequence-header、SPS、PPS数据。
虽然AVCC格式不使用起始码,防竞争字节仍然存在。
2.2.1 extradata结构
name | length | commnet |
---|---|---|
version | 8bit | 0x01 |
avc profile | 8bit | 0x64 |
avc compatibility | 8bit | 0x00 |
avc level | 8bit | 0x0A |
NALULengthSizeMinusOne | 8bit | 0xFF,高6位保留,默认为1,低2位为11,表示NALU长度用3+1=4字节表示 |
number of SPS NALUs | 8bit | 0xE1,高3位保留,低5位表示有几个SPS,通常只有1个SPS |
SPS size | 16bit | 大端格式的SPS长度,0x0019,表示25字节SPS |
SPS NALU data | SPS size × 8bit | 0x67到0x80,表示SPS数据 |
number of PPS NALUs | 8bit | 0x01PPS个数,通常只有1个PPS |
PPS size | 8bit | 0x07,表示PPS的数据长度 |
PPS NALU data | PPS size × 8bit | 0x68到0x30,表示PPS数据 |
NALULengthSizeMinusOne:用几个字节来存储NALU的长度,默认为3,表示使用4字节表示NALU长度。
2.3 H264 Annexb与AVCC格式转换
H264 Annexb转为AVCC
- 1、对于一个GOP的开始,根据start-code,分离出SPS、PPS帧,并分别计算出长度
- 2、根据SPS, PPS创建出extradata,附加到GOP的头部
- 3、从IDR帧开始,搜索start-code,分离出每一个NALU,计算长度,然后将start-code转为4字节的NALU长度
AVCC 转为 H264 Annexb
- 1、对于一个GOP的开始,首先检索出extradata部分的数据
- 2、根据extradata数据创建出SPS帧,并用4字节的start-code:0x00000001附加在SPS数据的头部
- 3、根据extradata数据创建出PPS帧,并用4字节的start-code:0x00000001附加在PPS数据的头部,并将PPS数据连接到SPS后面
- 4、根据NALU的长度字段,分离出每一个NALU,然后用用4字节的start-code:0x00000001替换长度字段
- 5、在以上过程中需要计算每一个NALU的长度,尤其是GOP的IDR帧,一般在IDR帧前还有SPS与PPS帧,其长度需要一起计算
3. 视频编码帧
3.1 压缩方式
帧内压缩与帧间压缩
- 帧内压缩(I帧,JPEG的压缩算法)
- 帧间压缩( P帧与B帧的压缩算法)
有损压缩与无损压缩
- 有损压缩: 解压缩后的数据与压缩前的数据不一致.在压缩的过程中要丢失一些人眼和人耳所不敏感的图像或音频信息,而且丢失的信息不可恢复
- 无损压缩: 压缩前和解压缩后的数据完全一致.优化数据的排列等
3.2 编码帧
常见的H264的编码帧有四种:
- I帧(关键帧)
- IDR帧(即时刷新帧)
- P帧(前向预测参考帧)
- B帧(双向预测参考帧)
I帧
I帧是帧内编码帧,I帧又称关键帧。
特点:
- I帧不需要参考P帧/B帧生成一个完整图像。
- I帧是P帧和B帧的参考帧。
- I帧通常是每个GOP的第一个帧,采用的帧内压缩,经过适度压缩,作为随机访问的参考点。
IDR帧
IDR为即时解码刷新帧,I帧和IDR帧都是使用帧内预测编码,IDR帧是I帧,但I帧不一定是IDR帧。
I帧(非IDR帧)和IDR帧,只有IDR帧才有SPS和PPS。当解码器收到IDR帧时,将参考帧队列清空;而收到I帧(非IDR帧)不会清空参考帧队列。也就是说,对某个IDR帧之后的帧,解码器不会参考这个IDR帧之前的任何帧做解码。对某个I帧(非IDR帧)之后的帧,解码器可能会参考这个I帧之前的帧做解码。
P帧
P帧是前向预测编码帧,以I帧为参考帧。
它存储与前面的I帧/P帧的预测误差及运动矢量。
特点:
- P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧。
- P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧。
- 由于P帧是参考帧,它可能造成解码错误的扩散。
- P帧的压缩率比I帧要高。
B帧
B帧是双向预测内插编码帧,以前面的I/P帧和后面的P帧为参考帧。B帧存储的是它与前面的I/P帧,和后面的P帧之间的预测误差及运动矢量。
特点:
- B帧压缩比最高。因为它只反映前后两个参考帧间运动主体的变化情况,预测比较准确。
- B帧不是参考帧,不会造成解码错误的扩散 。
I帧,B帧,P帧之间的关系:
3.3 丢帧
播放器变速中如何丢帧?
- GOP间的丢帧,只播放关键帧,也就是seek操作。
- GOP内的丢帧,需要先解码后丢帧播放。
- 非参考帧的丢帧处理。
针对非参考帧的丢帧处理:
方式:可以判断它的 nal_ref_idc 值来决定是否要丢弃。如果为 0 ,说明其他帧不需要参考该帧,可以直接丢弃不发送给解码器,而不是解码后再丢帧。
nalu type | nalu header val | 重要性 | 可否丢帧 |
---|---|---|---|
SPS | 0x67(0 11 00111) | 非常重要 | 不可丢帧 |
PPS | 0x68(0 11 01000) | 非常重要 | 不可丢帧 |
IDR帧 | 0x65(0 11 00101) | 非常重要 | 不可丢帧 |
I帧 | 0x61(0 11 00001) | 非常重要 | 不可丢帧 |
P帧 | 0x61(0 10 00001) | 重要 | 可丢帧,但后面同GOP组内的P帧,B帧都要丢 |
B帧 | 0x01(0 00 00001) | 不重要 | 可丢帧 |
SEI | 0x06(0 00 00110) | 不重要 | 可丢帧 |
参考链接:
https://www.cnblogs.com/hankfu/p/13968625.html
https://www.sohu.com/a/432293002_465219
https://cloud.tencent.com/developer/article/1619203
https://blog.csdn.net/u010925568/article/details/75040492
4. PTS与DTS
4.1 概念
DTS是解码时间戳,表示解码器应该什么时候解码数据帧。
PTS是显示时间戳,表示渲染器应该什么时候渲染显示数据帧。
一般来说,数据帧经过解码之后,就立即用于渲染显示,为什么还需要两个时间戳?
PTS与DTS是对于视频而言,音频只是一个时间戳DTS,也就是音频帧经过解码就立即播放。对于视频,当视频只有I帧和P帧,PTS和DTS是相同的,只有视频有B帧,PTS与DTS才有可能不一样。
4.2 为什么需要PTS,DTS?
以一个例子说明:
- 视频采集到的一组数据帧,经过传输解码,最终播放顺序按照:
I(1) -> B(2) -> B(3) -> P(4) -> B(5) -> B(6) -> P(7)
PTS: I(1) < B(2) < B(3) < P(4) < B(5) < B(6) < P(7)
同时也知道采集顺序和显示顺序是一致的。
- 由于B帧的存在,B帧是前后双向预测帧,必须先缓存并解码其前后两个参考帧才能解码B帧,如B(2),B(3)要解码,必须先缓存解码I(1)和P(4)才行,由此可知:
DTS:I(1) < P(4) < B(2) < B(3)
依此类推得出:
DTS:I(1) < P(4) < B(2) < B(3) < P(7) < B(5) < B(6)
可看出由于B帧的存在,视频帧的DTS和PTS可能不同。
采集到原始视频帧经过编码后得到编码帧,就会被传输,播放端接收到编码帧就进行解码,所以编码顺序、传输顺序和解码顺序是一致的。
H265
1.NALU
H265的NALU结构和H264是一致的,都是Nalu Header + RBSP的结构。
1.1 Nalu Header
Nalu Header由两个字节组成:
字段 | 大小 | 含义 |
---|---|---|
forbidden_zero_bit | 1bit | 禁止位,用以检查传输过程中是否发生错误,0表示正常,1表示违反语法 |
nal_unit_type | 6bit | 用来指示NALU类型 |
nuh_reserved_zero_6bits | 6bit | 预留位,要求为0 |
nuh_temporal_id_plus1 | 3bit | 表示NALU所在的时间层ID |
1.1.1 nal_unit_type
来保存视频流的编解码参数,其格式定义如下:
nal_unit_type | NALU类型 | 备注 |
---|---|---|
0 | NAL_UNIT_CODE_SLICE_TRAIL_N | 非关键帧 |
1 | NAL_UNIT_CODED_SLICE_TRAIL_R | |
2 | NAL_UNIT_CODED_SLICE_TSA_N | |
3 | NAL_UINT_CODED_SLICE_TSA_R | |
4 | NAL_UINT_CODED_SLICE_STSA_N | |
5 | NAL_UINT_CODED_SLICE_STSA_R | |
6 | NAL_UNIT_CODED_SLICE_RADL_N | |
7 | NAL_UNIT_CODED_SLICE_RADL_R | |
8 | NAL_UNIT_CODED_SLICE_RASL_N | |
9 | NAL_UNIT_CODE_SLICE_RASL_R | |
10 ~ 15 | NAL_UNIT_RESERVED_X | 保留 |
16 | NAL_UNIT_CODED_SLICE_BLA_W_LP | 关键帧 |
17 | NAL_UNIT_CODE_SLICE_BLA_W_RADL | |
18 | NAL_UNIT_CODE_SLICE_BLA_N_LP | |
19 | NAL_UNIT_CODE_SLICE_IDR_W_RADL | |
20 | NAL_UNIT_CODE_SLICE_IDR_N_LP | |
21 | NAL_UNIT_CODE_SLICE_CRA | |
22 | NAL_RSV_IRAP_VCL22 | |
23 | NAL_RSV_IRAP_VCL23 | |
24 ~ 31 | NAL_UNIT_RESERVED_X | 保留 |
32 | NAL_UNIT_VPS | VPS(Video Paramater Set) |
33 | NAL_UNIT_SPS | SPS |
34 | NAL_UNIT_PPS | PPS |
35 | NAL_UNIT_ACCESS_UNIT_DELIMITER | |
36 | NAL_UNIT_EOS | |
37 | NAL_UNIT_EOB | |
38 | NAL_UNIT_FILLER_DATA | |
39 | NAL_UNIT_SEI_PREFIX | Prefix SEI |
40 | NAL_UNIT_SEI_SUFFIX | Suffix SEI |
41 ~ 47 | NAL_UNIT_RESERVED_X | 保留 |
48 ~ 63 | NAL_UNIT_UNSPECIFIED_X | 未规定 |
64 | NAL_UNIT_INVALID |
- NAL_AUD(35),NAL_SEI_PREFIX(39),NAL_SEI_SUFFIX(40)为可丢弃的视频帧。
- nalu_type:0 ~ 23(不包含保留的nalu_type)用于解码的视频帧。