第一章 前言
第三章 有关 SPS 和 PPS 的一切
第四章 有关 Slice 的一切
第七章 帧间编码
第八章 残差的熵编码: CAVLC 和 CABAC
上一节,我们已经了解了 H.264 中会用到的指数哥伦布熵编码,并且也知道了,SPS 和 PPS 就是通过指数哥伦布熵编码进行压缩的。这一小节,我们就来尝试自己动手写代码来做指数哥伦布熵编码的解码。
本小节主要介绍无符号指数哥伦布熵编码和有符号指数哥伦布熵编码的解码,对于映射指数哥伦布熵编码和截断指数哥伦布熵编码,因为涉及到一些前置知识还没有讲到,本节先不做实现,后续遇到的时候会实现。
我们之前就说过,在 H.264 码流中的操作单位是 bit,所以,用普通的指针是无法达到这样的操作颗粒度。
我们现在需要一个以 bit 为操作单位的流对象,我们把他叫做 BitStream。
我们可以建立这样一个工具类,叫做 BitStream
class BitStream {
public:
/**
* 构造函数可以传入一个 buffer,这里可以直接把 nalu 的 buffer 传入
*/
BitStream(uint8_t * buf, int size);
~BitStream();
private:
// 指向 buffer 开始的位置
uint8_t * start = nullptr;
// buffer 的长度(单位 Byte)
int size = 0;
// 当前读取到了哪个字节
uint8_t * p = nullptr;
// 当前读取到了字节中的第几位
int bits_left = 8;
};
各个属性的说明图
从 Bitstream 中读取 1 bit 的接口
在编写从数据流里读取指数哥伦布编码的数据之前,我们先来完成一个最基本的函数的封装:先读取 1 bit 的数据。
int ReadU1();
我们先定义一个 ReadU1 的成员函数,这个函数的作用是从 bitstram 读取一个 bit 的数据,并把这一个 bit 的数据转换成一个 int 类型,然后把数据指针向后移动一个 bit(通过 bits_left 减 1 实现,如果 bits_left 被减到了 0,那么 unsigned char * p 将向后移动一个字节)。虽然 1 bit 的数据只会有 0 或者 1 两种情况,但是我们为了方便还是直接用一个 int 来装。如果你觉得有些浪费空间,可以自己修改一下。
这个函数的实现如下:
int ReadU1()
{
int r = 0;
bits_left--;
r = ((*(p)) >> bits_left) & 0x01;
if (bits_left == 0) {
p++;
bits_left = 8;
}
return r;
}
从 Bitstream 中读取 n bit 的接口
接下来,我们再写一个函数,这个函数是 ReadU1 的扩增,我们可以一下从 Bitstream 中读取 n bit 的数据。
int ReadU(int n)
{
int r = 0;
int i;
for (i = 0; i < n; i++) {
r |= ( ReadU1() << ( n - i - 1 ) );
}
return r;
}
从 Bitstream 中读取一个无符号指数哥伦布熵编码的数据
这个应该是不需要做过多的解释了。我们在前一小结已经专门讲解了。
int ReadUE()
{
int r = 0;
int i = 0;
while((ReadU1() == 0) && (i < 32)){
i++;
}
r = ReadU(i);
r += (1 << i) - 1;
return r;
}
从 Bitstream 中读取一个有符号指数哥伦布熵编码的数据
我们在读取有符号的指数哥伦布熵编码的时候,实际上是先按照无符号的方式去读,然后读出来之后再解析符号。
int ReadSE()
{
int r = ReadUE();
if (r & 0x01) {
r = (r+1)/2;
}
else {
r = -(r/2);
}
return r;
}
https://github.com/redknotmiaoyuqiao/EyerH264Decoder/tree/main/Lesson_3_3_BitStream
yunxiyaoyue@163.com
2024-04-28 15:46:47
yunxiyaoyue@163.com
2024-04-28 15:41:42
liskyman@qq.com
2023-06-20 11:17:58
liskyman@qq.com
2023-06-20 11:05:51