音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现

发布于:2024-06-30 ⋅ 阅读:(12) ⋅ 点赞:(0)

 =================================================================

音视频入门基础:H.264专题系列文章:

音视频入门基础:H.264专题(1)——H.264官方文档下载

音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件

音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB

音视频入门基础:H.264专题(4)——NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介

音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析

音视频入门基础:H.264专题(6)——FFmpeg源码:从H.264码流中提取NALU Header、EBSP、RBSP和SODB

音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现

音视频入门基础:H.264专题(8)——H.264官方文档的描述符

=================================================================

一、引言

由于视频的传输和存贮是十分在乎体积的,对于每一个比特(bit)都要格外珍惜,所以H.264中用到了多种熵编码来对原本的数据进行压缩。

比如Sequence Paramater Set(sps / 序列参数集)中,seq_parameter_set_id这个属性用到了无符号指数哥伦布编码ue(v):

offset_for_non_ref_pic这个属性用到了有符号指数哥伦布编码se(v):

要拿到sps中的上述属性,需要对H.264码流对应的位置进行指数哥伦布编码的解码。

二、指数哥伦布编码简介

哥伦布编码(又译作格伦布编码,英语:Golomb coding)是一种无失真资料压缩方法,由数学家所罗门·格伦布在1960年代提出。其优点为易于编码与解码,目前广泛用于无损影像压缩。它是一种变长编码。

其具体原理可以参考《百度百科:指数哥伦布码

维基百科:格伦布编码

Golomb Codes

三、FFmpeg源码中 无符号指数哥伦布编码的解码实现

FFmpeg源码中通过get_ue_golomb、get_ue_golomb_long、get_ue_golomb_31等函数实现 对无符号指数哥伦布编码的解码。下面以get_ue_golomb_31函数为例,进行讲解。

get_ue_golomb_31函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3)的头文件libavcodec/golomb.h中:

/**
 * read unsigned exp golomb code, constraint to a max of 31.
 * If the value encountered is not in 0..31, the return value
 * is outside the range 0..30.
 */
static inline int get_ue_golomb_31(GetBitContext *gb)
{
    unsigned int buf;

#if CACHED_BITSTREAM_READER
    buf = show_bits_long(gb, 32);

    buf >>= 32 - 9;
    skip_bits_long(gb, ff_golomb_vlc_len[buf]);
#else

    OPEN_READER(re, gb);
    UPDATE_CACHE(re, gb);
    buf = GET_CACHE(re, gb);

    buf >>= 32 - 9;
    LAST_SKIP_BITS(re, gb, ff_golomb_vlc_len[buf]);
    CLOSE_READER(re, gb);
#endif

    return ff_ue_golomb_vlc_code[buf];
}

形参gb:既是输入型参数也是输出型参数。指向已经被初始化的GetBitContext类型的变量。执行get_ue_golomb_31函数之前必须确保已经使用init_get_bits函数进行初始化。(关于GetBitContext结构体可以参考《FFmpeg中位操作相关的源码:GetBitContext结构体,init_get_bits函数、get_bits1函数和get_bits函数分析》)

如果是使用get_ue_golomb_31函数对某个NALU(比如sps)中的属性进行读取。

执行get_ue_golomb_31函数之前:

gb->buffer需指向存放该NALU的“NALU Header + RBSP 的缓冲区”。

gb->buffer_end需指向上述缓冲区的末尾,也就是RBSP的最后一个字节。

gb->index的值需等于:当前读取到该缓冲区的第几位了(单位为bit)。要对以该位为起始的数据进行无符号指数哥伦布编码的解码。

gb->size_in_bit 的值需等于NALU Header + SODB的位数,单位为bit。

gb->size_in_bits_plus8的值需等于 s->size_in_bit 的值 加 8。

执行get_ue_golomb_31函数后:

gb->index的值会加上 “读取到的无符号指数哥伦布编码后的位数”。gb的其它成员的值不变。

get_ue_golomb_31函数返回值为:对gb->index的位置 进行无符号指数哥伦布解码后得到的数据。

注意:get_ue_golomb_31函数有读取范围的限制!!!只能读取0到31的数据,所以如果get_ue_golomb_31函数的返回值大于31表示出错了。

所以FFmpeg源码中(源文件libavcodec/h264_ps.c)对sps进行解码的函数ff_h264_decode_seq_parameter_set中有这样的一段逻辑:

#define MAX_SPS_COUNT          32

int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,

                                     H264ParamSets *ps, int ignore_truncation)

{//...

    sps_id    = get_ue_golomb_31(gb);

    if (sps_id >= MAX_SPS_COUNT) {
        av_log(avctx, AV_LOG_ERROR, "sps_id %u out of range\n", sps_id);
        goto fail;
    }

//...
}

如果读取到的sps_id值不小于32,表示out of range了。

如果想要更大的读取范围可以用get_ue_golomb函数和get_ue_golomb_long函数。它们的用法跟get_ue_golomb_31函数一样,只是读取范围更大而已。

四、FFmpeg源码中 有符号指数哥伦布编码的解码实现

FFmpeg源码中通过get_se_golomb和get_se_golomb_long等函数实现对 有符号指数哥伦布编码的解码。它们都定义在libavcodec/golomb.h中。

其形参跟get_ue_golomb_31函数相同,不同的地方为返回值是:对gb->index的位置 进行有符号指数哥伦布解码后得到的数据。


网站公告

今日签到

点亮在社区的每一天
去签到