ZigZag Sin
登 陆
上一篇:EBSP, RBSP, SODB 下一篇:NALU 的那些事

NALU 的第一个字节

乔红
2020-11-30 19:21 阅读 161

读取 NALU 的第一个字节

我们之前的章节里读取和分离到了 AnnexB 格式码流中的 NALU,我们这节课来看一下 NALU 中到底存放了些什么数据。

我们先查阅一下 H.264 描述 NALU 的语法表,我们先看看前三项。

nal_unit( NumBytesInNALunit )CDescriptor
    forbidden_zero_bitAllf(1)
    nal_ref_idcAllu(2)
    nal_unit_typeAllu(5)
    .........

表格的第一列表示句法元素,第二列表示句法元素的分类,第三列表示句法元素的格式,又叫描述子。

我们可以看到,forbidden_zero_bit 占用 1 位,nal_ref_idc 占用 2 位,nal_unit_type 占用 5 位。三个元素一共占用 8 位,也就是一个字节。

forbidden_zero_bit

禁止位,正常情况下为 0。在某些情况下,如果 NALU 发生丢失数据的情况,可以将这一位置为 1,以便接收方纠错或丢掉该单元。

nal_ref_idc

该元素表示这个 NALU 的重要性。可能的值有 4 个,详情如下表。

nal_ref_idc重要性
3HIGHEST
2HIGH
1LOW
0DISPOSABLE

越重要的 NALU 越不能丢弃。

nal_unit_type

NALU 的类型。详情如下表。

nal_unit_typeNALU 类型
0未定义
1非 IDR SLICEslice_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( )
5IDR SLICEslice_layer_without_partitioning_rbsp( )
6补充增强信息 SEIsei_rbsp( )
7序列参数集 SPSseq_parameter_set_rbsp( )
8图像参数集 PPSpic_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未指定

用代码提取 forbidden_zero_bit,nal_ref_idc,nal_unit_type

我们先看一下上一章节结束时候的代码

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();
}
上一篇:EBSP, RBSP, SODB 下一篇:NALU 的那些事
给我买个键盘吧。。。求打赏。。。
欢迎加群,一起交流~~~