frame

视频的一张图片就叫一帧,H264分为I帧,P帧,B帧。

slice

一个frame可以切成多个slice,slice之间在解码过程中是相对独立的,能够限制产生花屏时的影响范围

NALU

NALU = NAL HEADER + NAL Payload(slice data);

NAL HEADER

NAL HEADER = Forbidden_zero_bit(1bit)必须为0 + nal_ref_idc(2bit)重要程度,越大越重要 + nal_unit_type(5bit)NALU类型[0,31];

nal_unit_type = {6(SEI), 7(SPS), 8(PPS), 5(IDR帧), 1(非IDR帧), 28(FU-A分片)}

NAL Payload

SODB

编码器原始输出的比特流(非字节对齐)

RBSP

SODB + 补齐字节,凑齐到8bit整数倍(停止位1 + n个0)

EBSP

RBSP中的 0x00 00 XX (XX = {00, 01, 02, 03}), 替换为 0x00 00 03 XX

NALU交界

NALU之间的边界标识方式有两种

  • Annex B : 用起始码 0x00 00 010x00 00 00 01 标识一个新的NALU的开始
  • AVCC : 在 NALU 之前添加4字节表示该NALU的实际长度

Annex B适合网络传输,AVCC适合本地存储

FU-A分包

如果一个NALU长度超过了MTU,此时就需要FU-A分包

1
2
3
4
5
struct NAUL_HEADER {
uint8_t Forbidden_zero_bit : 1;
uint8_t Nal_ref_idc : 2;
uint8_t Type : 5;
};

假设原始IDR帧 NAUL_HEADER idr_header = {0, 11, 00101} = 0x65;

FU-A分包会构造两个结构,FU identifier 和 FU Header

1
2
3
4
5
6
7
8
9
10
11
12
struct FU_identifier {
uint8_t Forbidden_zero_bit : 1;
uint8_t Nal_ref_idc : 2;
uint8_t Type : 5;
};

struct FU_Header {
uint8_t Start_bit : 1;
uint8_t End_bit : 1;
uint8_t Forbidden_bit : 1;
uint8_t Nal_unit_type : 5;
};

则有第1片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FU_identifier fi1;
fi1.Forbidden_zero_bit = 0;
fi1.Nal_ref_idc = idr_header.Nal_ref_idc;
fi1.Type = 28;

// fi1 = 0x7c

FU_Header fh1;
fh1.Start_bit = 1;
fh1.End_bit = 0;
fh1.Forbidden_bit = 0;
fh1.Nal_unit_type = idr_header.Type;

// fh1 = 0x85

中间n片 Start_bit = 0, End_bit = 0;

最后1片 Start_bit = 0, End_bit = 1;

还原时即将收到的第一个分片的2byte还原为1byte NAUL_HEADER,后面的分片前2byte直接丢弃