ZigZag Sin
登 陆
上一篇:学会读文档中的描述子 下一篇:3-6 *代码-开始读取 SPS

3-5 *代码-正式解析开始前的一些抽象

乔红
2021-1-23 17:39 阅读 756

引言

事先准备

首先,我们需要对我们现有的代码做一些改造。我们现在会从码流里面读出来一个一个 NALU 对象,然后从 NALU 中读出来 EBSP,然后再从 EBSP 中读出来 RBSP。

这样做没有问题,但是我们其实暴露了太多的细节出来。我们其实更希望读出来一个一个的 NALU,然后直接在 NALU 内部做 RBSP 的提取。

此外,因为 NALU 有很多个类型,每种类型中有不同的结构。所以,我们可以把 Nalu 做成一个父类,然后根据不同的具体类型做出不同的子类出来。

将提取 RBSP 放到 Nalu 中

首先,我们可以为 Nalu 中添加一个成员函数,这个函数可以直接将 Nalu 的 EBSP 数据转换成 RBSP 数据,而解析出来的 RBSP,我们则将其存成 Nalu 类的一个成员。

int ParseRBSP();

实现很简单,我们就把之前的实现调用一下就好了(我们现阶段不太在乎性能),如下:

int Nalu::ParseRBSP()
{
    EBSP ebsp;
    int ret = GetEBSP(ebsp);
    if(ret){
        return -1;
    }

    // 此处的 rbsp 是成员变量
    return ebsp.GetRBSP(rbsp);
}

为 Nalu 提供获取 Nalu Header 的函数

除了 RBSP 之外,我们还需要一个解析 Nalu Header 的函数,这个函数可以解析 Nalu 的第一个字节,并将解析出来的 forbidden_bitnal_ref_idcnal_unit_type 存储成为成员变量。我们将其称之为 ParseHeader 函数。

int ParseHeader();

他的实现我们照搬之前的就可以了

int Nalu::ParseHeader()
{
    uint8_t naluHead    = rbsp.buf[0];
    forbidden_bit       = (naluHead >> 7) & 1;
    nal_ref_idc         = (naluHead >> 5) & 3;
    nal_unit_type       = (naluHead >> 0) & 0x1f;
    return 0;
}

使用的时候,我们需要先调用 ParseRBSP 函数将 RBSP 提取出来,之后再调用 ParseHeader 函数将 Nalu Header 中的内容提取出来。

while(1){
    Nalu nalu;
    ret = reader.ReadNalu(nalu);
    if(ret){
        break;
    }

    nalu.ParseRBSP();
    nalu.ParseHeader();
}

将 Nalu 进行抽象

在解析完 RBSPNalu Header 之后,我们就可以得到每一个 Nalunal_unit_type 是什么的了。而不同 typeNalu 有着完全不同的解析方式。因此,我们需要将 Nalu 进行一次抽象,将其作为一个父类,然后根据 type 的不同,创建不用的子类,分别进行解析。

我们可以为 Nalu 类添加一个虚函数,子类可以重写这个函数,在这个函数中实现具体的解析。

virtual int Parse();

之后,我们可以先创建两个 Nalu 的子类,分别用来解析 SPSPPS

NaluSPS.hpp:

class NaluSPS : public Nalu
{
public:
    virtual int Parse() override;
};

NaluPPS.hpp:

class NaluPPS : public Nalu
{
public:
    virtual int Parse() override;
};

父类转换为子类

我们在 main 函数中读取文件的时候,读出来的是一个一个的 Nalu。在 ParseHeader 之后,我们可以得到 Nalu 的类型,这时候,我们就可以把 Nalu 转换成相应类型的子类。这种转换,我们通常情况下是利用子类的拷贝构造函数,将父类拷贝给子类。拷贝的逻辑大家可以具体参阅一下详细代码,这里就不赘述了。(我给 RBSP,EBSP,Nalu 都提供了拷贝构造函数和赋值运算符重载)。

在这个拷贝构造函数中,我们可以将 Nalu 的数据拷贝给 NaluSPS。这样在使用的时候,我们就可以直接使用 Nalu 的一个父类对象构建出来一个 NaluSPS 的子类对象。

while(1){
    Nalu nalu;
    ret = reader.ReadNalu(nalu);
    if(ret){
        break;
    }

    nalu.ParseRBSP();
    nalu.ParseHeader();

    if(nalu.GetNaluType() == 7){
        NaluSPS sps = nalu;
    }
    if(nalu.GetNaluType() == 8){
        NaluPPS pps = nalu;
    }
}

NaluPPS 也是同理

完整代码获取

https://github.com/redknotmiaoyuqiao/EyerH264Decoder/tree/main/Lesson_3_5_SPS_PPS

上一篇:学会读文档中的描述子 下一篇:3-6 *代码-开始读取 SPS
给我买个键盘吧。。。求打赏。。。
欢迎加群,一起交流~~~