ffmpeg 0阶哥伦布算法的详细注释及简单测试程序

/* ffmpeg 0阶哥伦布算法的详细注释及简单测试程序
 * author: hjjdebug
 * date:   2023年 07月 14日 星期五 15:18:11 CST
 * 博客即是代码,copy 编译通过即可运行
 */

#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wsign-compare"

#define SUINT   unsigned
#include <libavcodec/get_bits.h>
#include <libavcodec/golomb.h>

/*
 * 0价哥伦布码表
 *         1                     代表0
 *       0 1 x                    代表1,2
 *     0 0 1 x x                 代表3,4,5,6
 *   0 0 0 1 x x x                代表7,8,9,10,11,12,13,14
 * 0 0 0 0 1 x x x x            代表15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
 * ................... 忽略后面码子
 * 总之,代表的实际数字是0b1x..x-1, x的个数是前导0的个数
 *这个编码是哥伦布发明的,所以叫哥伦布编码表

在FFmpeg中采用了查表和计算相结合的方法来解码. 为的是加快速度
对码长不超过9比特的码字制作了ff_golomb_vlc_len和ff_ue_golomb_vlc_code直接获取码长和码值,
对码长超过9bits的码字要计算得到码值,看后面代码.
表格解释: 以读到的9bits 数据为索引, 数值也在这9bits之间(当索引大于16时)
1. 索引1nnnnnnnn 范围[256,511],码长为1,code为0            因为后面的n我们是don't care的,长度只有1
2. 索引01xnnnnnn 范围[128,255],码长为3,code为0b1x-1
3. 索引001xxnnnn 范围[64,127], 码长为5,code为0b1xx-1
4. 索引0001xxxnn 范围[32,64],  码长为7,code为0b1xxx-1
5. 索引00001xxxx 范围[16,31],  码长为9,code为0b1xxxx-1 9bits码长,解码数值在15-30之间
6. 索引000001xxx 范围[8,15],   码长为11,code值较大,本9bits不能容纳,给最大值32代替
7. 索引0000001xx 范围[4,7],    码长为13,code值较大,本9bits不能容纳,给最大值32代替
8. 索引00000001x 范围[2,3],    码长为15,code值较大,本9bits不能容纳,给最大值32代替
9. 索引000000001 范围[1],      码长为17,code值较大,本9bits不能容纳,给最大值32代替
10.索引000000000 范围[0],      码长不确定,至少大于19位,code值较大,本9bits不能容纳,给最大值32代替
*/
//因为读取的是9bits, 后面的bit可能不属于本编码,只有前面有限个bit属于本编码,
//所以由读取的9bits为索引判定golomb编码长度最长为2*8+1=17bits, 19是无效值,
//在512个空间装19个长度值,知很多索引它们对应同一个长度
//

const uint8_t ff_golomb_vlc_len[512]={
19,17,15,15,13,13,13,13,11,11,11,11,11,11,11,11,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};

//因为读取的是9bits, 后面的bit可能不属于本编码,只有前面有限个bit属于本编码,所以不同的索引对应相同的码值.
//读取前9bits,直接对应其码值
const uint8_t ff_ue_golomb_vlc_code[512]={
32,32,32,32,32,32,32,32,31,32,32,32,32,32,32,32,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14,
 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// 照搬ffmpeg 中的写法, 有宏,不容易搞清运算关系,不过理解了也就无所谓了.
int my_get_ue_golomb(GetBitContext *gb)
{

    unsigned int data32;
    OPEN_READER(re, gb);  //准备unsigned int re_index = (gb)->index; unsigned int re_cache
//拿到后面的32bits, re_index 取走一位要左移一位
端序调整,获取反转后的值,大端在前,
    UPDATE_CACHE(re, gb); //
    data32 = GET_CACHE(re, gb); //只处理32bits, 多于32bits(例前16bits全为0)为非法

    if (data32 >= (1 << 27)) { //当前5bits 不全为0时,意味着码长最多9bits,用512表处理很安全
        data32 >>= 32 - 9;  //取前9bits, 可以查512表
        LAST_SKIP_BITS(re, gb, ff_golomb_vlc_len[data32]);  //更新gb中的re_index
        CLOSE_READER(re, gb); //关闭

        return ff_ue_golomb_vlc_code[data32];    //返回解码数值.
    } else {
        int log = 2 * av_log2(data32) - 31; //得到不属于哥伦布编码的位长,参考注1证明
        LAST_SKIP_BITS(re, gb, 32 - log); //更新gb中的re_index, 32-log为码长
        CLOSE_READER(re, gb); //关闭
        if (log < 7)
            return AVERROR_INVALIDDATA; //返回非法数据错误
        data32 >>= log;   //移除不属于哥伦布编码的位
        data32--;  //-1得到码值(golomb的数值就等于二进制编码-1)

        return data32;
    }
}
//注1:
//av_log2(data32)=log2(data32)=w-1, 把数值取以2为底的对数,可以得到二进制bit位的长度w(简称位宽)
//因为位宽1,最小数为0,最大数为2^0,
//位宽2,最小数为2^1, 最大数为2^2-1,
//位宽3,最小数为2^2, 最大数为2^3-1,
//位宽32,最小数为2^31,最大数为2^32-1
//令32bits最大数(2^32-1)=max32, 则log2(max32)=31 (max32由于是2^32-1,所以取整后只能是31,而位宽是32)
//一般例子: 0x2f=47 , 其二进制位宽是多少? 0b101111, 一看就是6bits, 也可以这样算 log2(47)= 5,则宽为6bit

//采用32bit, 已知,前导0的bits + w = 32, w 认为是去掉前导0的后32bits 的长度,也叫位宽
//前导0=32-w
//golomb 编码规则: 前导0+1bit+信息长+无用bit=32,
//故 (32-w)+1+(32-w)+n=32 =>
//32-(w-1)-(w-1)-1+n=0 =>
//n = 2(w-1) - 31 =>
//n = 2*av_log2(data32)-31

//去掉宏的函数,帮助搞清运算关系
int my_get_ue_golomb2(GetBitContext *gb)
{

    unsigned int data32;
    unsigned int re_index = (gb)->index;
    unsigned int re_size_plus8 = (gb)->size_in_bits_plus8;
    unsigned int re_cache = av_bswap32((((const union unaligned_32 *) ((gb)->buffer + (re_index >> 3)))->l)) << (re_index & 7);
    data32 = ((uint32_t) re_cache); //拿到后面的32bits, re_index 取走一位要左移一位

    if (data32 >= (1 << 27)) {
        data32 >>= 32 - 9;
        re_index = ((re_size_plus8) > (re_index + (ff_golomb_vlc_len[data32])) ? (re_index + (ff_golomb_vlc_len[data32])) : (re_size_plus8));
        (gb)->index = re_index;

        return ff_ue_golomb_vlc_code[data32];
    } else {
        int log = 2 * av_log2(data32) - 31;
        re_index = ((re_size_plus8) > (re_index + (32 - log)) ? (re_index + (32 - log)) : (re_size_plus8));
        (gb)->index = re_index;
        if (log < 7)
            return (-(int)(('I') | (('N') << 8) | (('D') << 16) | ((unsigned)('A') << 24)));
        data32 >>= log;
        data32--;

        return data32;
    }
}

int main()
{
    //一个PPS 数据集 (picture parameter set)
    unsigned char data[]={
        0x68,
        0xeb,
        0xec,
        0xb2,
        0x2c
    };
    GetBitContext gb;
    int ret = init_get_bits8(&gb, data, sizeof(data)); //初始化gb, 使指向data,大小5个bytes(40bits)
    if(ret!=0) return 1;
    get_bits1(&gb); //拿到0x68的第1bit
    int ref_idc = get_bits(&gb, 2); //拿到0x68的后续2bit,获取到idc值为3
    int type    = get_bits(&gb, 5); //拿到0x68的后续5bit,获取到type为8

    printf("%x %x\n",ref_idc,type);
    unsigned int pps_id = my_get_ue_golomb2(&gb); // 对哥伦布编码的调用,解出pps_id
//     ff_h264_decode_picture_parameter_set(&nal.gb, avctx, &p->ps, nal.size_bits);
    printf("id:%d\n",pps_id);

    return 0;
}

/* 当我再看代码时,表示看不懂, 只好再进一步标注一下 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值