第一章 前言
第三章 有关 SPS 和 PPS 的一切
第四章 有关 Slice 的一切
第七章 帧间编码
第八章 残差的熵编码: CAVLC 和 CABAC
上一节,我们已经知道了 NALU 中 EBSP 和 RBSP 的关系。知道了一个 NALU 中,去掉 Nalu Start Code 就是 EBSP,EBSP 去掉防竞争字节序,就是 RBSP。这一小节,我们就来写代码,提取 EBSP 和 RBSP。
首先,我们创建一个类,叫做 EBSP。我们给 EBSP 设置两个成员变量。
class EBSP {
public:
EBSP();
~EBSP();
public:
uint8_t * buf = nullptr;
int len = 0;
};
用来表示 EBSP 的数据和长度。
之后,我们在 EBSP 的析构函数中,加入释放代码。
EBSP::~EBSP()
{
if(buf != nullptr){
free(buf);
buf = nullptr;
}
}
这样,我们可以将提取后的 EBSP 数据封装到一个 EBSP 的对象里面去(会涉及到一些拷贝,但是性能并不是我们这门课程最看重的)。
之后,我们就可以给 Nalu 类添加一个成员函数,用过调用这个成员函数,就可以把 Nalu 的数据进行处理,并且把结果存放到一个 EBSP 对象当中去。
int GetEBSP(EBSP & ebsp);
从 Nalu 中提取 EBSP 是很简单的,只需要把头部的 Nalu Start Code 去掉就可以了。实现如下:
int Nalu::GetEBSP(EBSP & ebsp)
{
ebsp.len = len - startCodeLen;
ebsp.buf = (uint8_t *)malloc(ebsp.len);
memcpy(ebsp.buf, buf + startCodeLen, ebsp.len);
return 0;
}
之后,我们可以在调用测试一下。
while(1){
Nalu nalu;
ret = reader.ReadNalu(nalu);
if(ret){
break;
}
EBSP ebsp;
ret = nalu.GetEBSP(ebsp);
if(ret){
break;
}
printf("=====================\n");
printf("Buffer Len: %d\n", ebsp.len);
printf("%d %d %d %d %d\n", ebsp.buf[0], ebsp.buf[1], ebsp.buf[2], ebsp.buf[3], ebsp.buf[4]);
}
接下来,我们继续从 EBSP 中提取 RBSP。所谓提取 RBSP,实际上就是把 EBSP 中的防竞争字节序给去掉了。
同样,我们还是创建一个 RBSP 的类,也是创建一个 buf,一个 len,用来表示 RBSP 的数据和数据长度,然后在析构函数里释放掉。
class RBSP {
public:
RBSP();
~RBSP();
public:
uint8_t * buf = nullptr;
int len = 0;
};
之后,我们在 EBSP 中添加一个成员函数,这个函数可以将 EBSP 的代码进行处理,并将处理后的数据存成一个 RBSP 对象。
int GetRBSP(RBSP & rbsp);
具体实现就是去掉数据中的防竞争字节序
int EBSP::GetRBSP(RBSP & rbsp)
{
rbsp.len = len;
rbsp.buf = (uint8_t *)malloc(rbsp.len);
int targetIndex = 0;
for(int i=0; i<len; i++){
if(buf[i] == 0x03){
// 查看前两个是不是 0x00 0x00
if(i > 2){
if(buf[i - 1] == 0x00 && buf[i - 2] == 0x00){
// 判断 后一个是不是 0 1 2 3
if(i < len - 1){
if(buf[i + 1] == 0x00 || buf[i + 1] == 0x01 || buf[i + 1] == 0x02 || buf[i + 1] == 0x03){
// 满足条件,该位置不进行拷贝
rbsp.len--;
continue;
}
}
}
}
}
rbsp.buf[targetIndex] = buf[i];
targetIndex++;
}
return 0;
}
https://github.com/redknotmiaoyuqiao/EyerH264Decoder/tree/main/Lesson_2_3_ReadAnnexB