我们之前的章节里读取和分离到了 AnnexB 格式码流中的 NALU,我们这节课来看一下 NALU 中到底存放了些什么数据。
我们先查阅一下 H.264 描述 NALU 的语法表,我们先看看前三项。
nal_unit( NumBytesInNALunit ) | C | Descriptor |
---|---|---|
forbidden_zero_bit | All | f(1) |
nal_ref_idc | All | u(2) |
nal_unit_type | All | u(5) |
... | ... | ... |
表格的第一列表示句法元素,第二列表示句法元素的分类,第三列表示句法元素的格式,又叫描述子。
我们可以看到,forbidden_zero_bit 占用 1 位,nal_ref_idc 占用 2 位,nal_unit_type 占用 5 位。三个元素一共占用 8 位,也就是一个字节。
禁止位,正常情况下为 0。在某些情况下,如果 NALU 发生丢失数据的情况,可以将这一位置为 1,以便接收方纠错或丢掉该单元。
该元素表示这个 NALU 的重要性。可能的值有 4 个,详情如下表。
nal_ref_idc | 重要性 |
---|---|
3 | HIGHEST |
2 | HIGH |
1 | LOW |
0 | DISPOSABLE |
越重要的 NALU 越不能丢弃。
NALU 的类型。详情如下表。
nal_unit_type | NALU 类型 | |
---|---|---|
0 | 未定义 | |
1 | 非 IDR SLICE | slice_layer_without_partitioning_rbsp( ) |
2 | 非 IDR SLICE,采用 A 类数据划分片段 | slice_data_partition_a_layer_rbsp( ) |
3 | 非 IDR SLICE,采用 B 类数据划分片段 | slice_data_partition_b_layer_rbsp( ) |
4 | 非 IDR SLICE,采用 C 类数据划分片段 | slice_data_partition_c_layer_rbsp( ) |
5 | IDR SLICE | slice_layer_without_partitioning_rbsp( ) |
6 | 补充增强信息 SEI | sei_rbsp( ) |
7 | 序列参数集 SPS | seq_parameter_set_rbsp( ) |
8 | 图像参数集 PPS | pic_parameter_set_rbsp( ) |
9 | 分隔符 | access_unit_delimiter_rbsp( ) |
10 | 序列结束符 | end_of_seq_rbsp( ) |
11 | 码流结束符 | end_of_stream_rbsp( ) |
12 | 填充数据 | filler_data_rbsp( ) |
13 | 序列参数扩展集 | seq_parameter_set_extension_rbsp( ) |
14~18 | 保留 | |
19 | 未分割的辅助编码图像的编码条带 | slice_layer_without_partitioning_rbsp( ) |
20~23 | 保留 | |
24~31 | 未指定 |
我们先看一下上一章节结束时候的代码
int main()
{
std::string filePath = "./demo_video_176x144_baseline.h264";
AnnexBReader reader(filePath);
int ret = reader.Open();
if(ret){
printf("Read Fail");
return -1;
}
while(1){
uint8_t buffer[1024 * 1024];
int bufferLen = 0;
int startcodeLen = 0;
ret = reader.ReadNalu(buffer, &bufferLen, &startcodeLen);
if(ret){
break;
}
printf("=====================\n");
printf("Buffer Len: %d\n", bufferLen);
printf("Start Code Len: %d\n", startcodeLen);
printf("%d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
}
reader.Close();
return 0;
}
我们可以看到,在 ReadNalu 成功之后,可以读到一个 buffer,一个 buffer 的长度,还有 startCode 的长度。在跳过 startCode 之后的一个字节,就是我们本章中讲解的三个元素。提取这三个元素的代码如下。
int forbidden_bit = (*(buffer + startcodeLen) >> 7) & 1;
int nal_ref_idc = (*(buffer + startcodeLen) >> 5) & 3;
int nal_unit_type = (*(buffer + startcodeLen) >> 0) & 0x1f;
完整代码如下
int main()
{
std::string filePath = "./demo_video_176x144_baseline.h264";
AnnexBReader reader(filePath);
int ret = reader.Open();
if(ret){
printf("Read Fail");
return;
}
while(1){
uint8_t buffer[1024 * 1024];
int bufferLen = 0;
int startcodeLen = 0;
ret = reader.ReadNalu(buffer, &bufferLen, &startcodeLen);
if(ret){
break;
}
printf("=====================\n");
printf("Buffer Len: %d\n", bufferLen);
printf("Start Code Len: %d\n", startcodeLen);
printf("%d %d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
int forbidden_bit = (*(buffer + startcodeLen) >> 7) & 1;
int nal_ref_idc = (*(buffer + startcodeLen) >> 5) & 3;
int nal_unit_type = (*(buffer + startcodeLen) >> 0) & 0x1f;
printf("forbidden_bit: %d\n", forbidden_bit);
printf("nal_ref_idc: %d\n", nal_ref_idc);
printf("nal_unit_type: %d\n", nal_unit_type);
}
reader.Close();
}