ZigZag Sin
登 陆
上一篇:指数哥伦布熵编码 下一篇:学会读文档中的描述子

*代码-指数哥伦布熵编码的解码实现

乔红
2022-2-28 16:12 阅读 3278

引言

上一节,我们已经了解了 H.264 中会用到的指数哥伦布熵编码,并且也知道了,SPS 和 PPS 就是通过指数哥伦布熵编码进行压缩的。这一小节,我们就来尝试自己动手写代码来做指数哥伦布熵编码的解码。

本小节主要介绍无符号指数哥伦布熵编码和有符号指数哥伦布熵编码的解码,对于映射指数哥伦布熵编码和截断指数哥伦布熵编码,因为涉及到一些前置知识还没有讲到,本节先不做实现,后续遇到的时候会实现。

BitStream

我们之前就说过,在 H.264 码流中的操作单位是 bit,所以,用普通的指针是无法达到这样的操作颗粒度。

我们现在需要一个以 bit 为操作单位的流对象,我们把他叫做 BitStream。

Golomb

代码结构

我们可以建立这样一个工具类,叫做 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;
};

各个属性的说明图

Golomb

函数的设计

  • 从 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

是我理解问题,按照-5 编码的结果 0001011 按照无符号读出来值应该是 3 + (1 << 3) - 1 = 10 , 10 /2 = 5

2024-04-28 15:46:47

yunxiyaoyue@163.com

ReadSE() 看来看去还是不太对。就以-5为例: 先转为正数 5 = 101 , 然后在末尾加上符号位 1011,前面补3个0, 0001011, 假设这个是我们读出来无符号数,r = 8 + 2 + 1 = 11, 带入到ReadSe里面,if(r & 0x01)里面,r= (r + 1)/2 得到 r = (11 + 1 )/2 = 6? . 我不确定是我立即不对还是作者计算出的问题

2024-04-28 15:41:42

liskyman@qq.com

抱歉了,是我理解错误,乔老师ReadSE() 是正确的

2023-06-20 11:17:58

liskyman@qq.com

你好,乔老师, ReadSE()应修改如下: int ReadSE() { int r = ReadUE(); if (r & 0x01) { r = -(r/2); } else { r = (r+1)/2; } return r; }

2023-06-20 11:05:51