第一章 前言
第三章 有关 SPS 和 PPS 的一切
第四章 有关 Slice 的一切
第七章 帧间编码
第八章 残差的熵编码: CAVLC 和 CABAC
首先,我们需要对我们现有的代码做一些改造。我们现在会从码流里面读出来一个一个 NALU 对象,然后从 NALU 中读出来 EBSP,然后再从 EBSP 中读出来 RBSP。
这样做没有问题,但是我们其实暴露了太多的细节出来。我们其实更希望读出来一个一个的 NALU,然后直接在 NALU 内部做 RBSP 的提取。
此外,因为 NALU 有很多个类型,每种类型中有不同的结构。所以,我们可以把 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);
}
除了 RBSP 之外,我们还需要一个解析 Nalu Header 的函数,这个函数可以解析 Nalu 的第一个字节,并将解析出来的 forbidden_bit, nal_ref_idc, nal_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();
}
在解析完 RBSP 和 Nalu Header 之后,我们就可以得到每一个 Nalu 的 nal_unit_type 是什么的了。而不同 type 的 Nalu 有着完全不同的解析方式。因此,我们需要将 Nalu 进行一次抽象,将其作为一个父类,然后根据 type 的不同,创建不用的子类,分别进行解析。
我们可以为 Nalu 类添加一个虚函数,子类可以重写这个函数,在这个函数中实现具体的解析。
virtual int Parse();
之后,我们可以先创建两个 Nalu 的子类,分别用来解析 SPS 和 PPS。
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