FFmpeg源码:packet_alloc、av_new_packet、av_shrink_packet、av_grow_packet函数分析

一、packet_alloc函数

packet_alloc函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavcodec/avpacket.c中:

cpp 复制代码
static int packet_alloc(AVBufferRef **buf, int size)
{
    int ret;
    if (size < 0 || size >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
        return AVERROR(EINVAL);

    ret = av_buffer_realloc(buf, size + AV_INPUT_BUFFER_PADDING_SIZE);
    if (ret < 0)
        return ret;

    memset((*buf)->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    return 0;
}

该函数作用是:给一个AVPacket的AVBufferRef类型成员(*pbuf指向的结构体)和(*pbuf)->buffer分配内存,给(*pbuf)->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。

返回值:返回0表示成功,返回AVERROR(EINVAL)和AVERROR(ENOMEM)表示失败。

这里大家可以注意到,给(*pbuf)->data重新分配的大小不是size个字节,而是(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节。宏AV_INPUT_BUFFER_PADDING_SIZE定义在libavcodec/defs.h中:

cpp 复制代码
/**
 * @ingroup lavc_decoding
 * Required number of additionally allocated bytes at the end of the input bitstream for decoding.
 * This is mainly needed because some optimized bitstream readers read
 * 32 or 64 bit at once and could read over the end.<br>
 * Note: If the first 23 bits of the additional bytes are not 0, then damaged
 * MPEG bitstreams could cause overread and segfault.
 */
#define AV_INPUT_BUFFER_PADDING_SIZE 64

这个宏AV_INPUT_BUFFER_PADDING_SIZE值是64,是在输入比特流末端为解码而额外分配的字节数。这主要是因为一些优化的比特流读取器一次读取32或64位,并且可以超过末尾读取。所以是重新分配(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节,比size个字节要大,防止某些优化过的读取器一次性读取过多导致越界。

二、av_new_packet函数

(一)av_new_packet函数的声明

av_new_packet函数声明在头文件libavcodec/packet.h中:

cpp 复制代码
/**
 * Allocate the payload of a packet and initialize its fields with
 * default values.
 *
 * @param pkt packet
 * @param size wanted payload size
 * @return 0 if OK, AVERROR_xxx otherwise
 */
int av_new_packet(AVPacket *pkt, int size);

该函数作用是:给pkt->buf和pkt->buf->buffer分配内存,给pkt->buf->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。然后对pkt的其它成员变量进行初始化。注意:该函数不会给AVPacket本身分配内存,所以执行该函数前必须先给形参pkt指向的AVPacket类型变量分配内存,否则可能会导致程序崩溃。

返回值:返回0表示成功,返回AVERROR(EINVAL)和AVERROR(ENOMEM)表示失败。

(二)av_new_packet函数的定义

av_new_packet函数定义在源文件libavcodec/avpacket.c中:

cpp 复制代码
int av_new_packet(AVPacket *pkt, int size)
{
    AVBufferRef *buf = NULL;
    int ret = packet_alloc(&buf, size);
    if (ret < 0)
        return ret;

    get_packet_defaults(pkt);
    pkt->buf      = buf;
    pkt->data     = buf->data;
    pkt->size     = size;

    return 0;
}

可以看到其内部调用了packet_alloc函数分配内存和get_packet_defaults函数进行初始化。

三、av_shrink_packet函数

(一)av_shrink_packet函数的声明

av_shrink_packet函数声明在头文件libavcodec/packet.h中:

cpp 复制代码
/**
 * Reduce packet size, correctly zeroing padding
 *
 * @param pkt packet
 * @param size new size
 */
void av_shrink_packet(AVPacket *pkt, int size);

该函数作用是:减少数据包(pkt->data指向的缓冲区)的大小,让该大小减至size字节。让地址为(pkt->data + size)后的数据字节归零。执行该函数后,pkt->size会减至size字节。

(二)av_shrink_packet函数的定义

av_shrink_packet函数定义在源文件libavcodec/avpacket.c中:

cpp 复制代码
void av_shrink_packet(AVPacket *pkt, int size)
{
    if (pkt->size <= size)
        return;
    pkt->size = size;
    memset(pkt->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}

四、av_grow_packet函数

(一)av_grow_packet函数的声明

cpp 复制代码
/**
 * Increase packet size, correctly zeroing padding
 *
 * @param pkt packet
 * @param grow_by number of bytes by which to increase the size of the packet
 */
int av_grow_packet(AVPacket *pkt, int grow_by);

该函数作用是:增加数据包(pkt->data指向的缓冲区)的大小,让该大小增至(pkt->size + grow_by)字节。让地址为(pkt->data + pkt->size + grow_by)后的数据字节归零。执行该函数后,pkt->size会增至(pkt->size + grow_by)字节。

(二)av_grow_packet函数的定义

av_grow_packet函数定义在源文件libavcodec/avpacket.c中:

cpp 复制代码
int av_grow_packet(AVPacket *pkt, int grow_by)
{
    int new_size;
    av_assert0((unsigned)pkt->size <= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE);
    if ((unsigned)grow_by >
        INT_MAX - (pkt->size + AV_INPUT_BUFFER_PADDING_SIZE))
        return AVERROR(ENOMEM);

    new_size = pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE;
    if (pkt->buf) {
        size_t data_offset;
        uint8_t *old_data = pkt->data;
        if (pkt->data == NULL) {
            data_offset = 0;
            pkt->data = pkt->buf->data;
        } else {
            data_offset = pkt->data - pkt->buf->data;
            if (data_offset > INT_MAX - new_size)
                return AVERROR(ENOMEM);
        }

        if (new_size + data_offset > pkt->buf->size ||
            !av_buffer_is_writable(pkt->buf)) {
            int ret;

            // allocate slightly more than requested to avoid excessive
            // reallocations
            if (new_size + data_offset < INT_MAX - new_size/16)
                new_size += new_size/16;

            ret = av_buffer_realloc(&pkt->buf, new_size + data_offset);
            if (ret < 0) {
                pkt->data = old_data;
                return ret;
            }
            pkt->data = pkt->buf->data + data_offset;
        }
    } else {
        pkt->buf = av_buffer_alloc(new_size);
        if (!pkt->buf)
            return AVERROR(ENOMEM);
        if (pkt->size > 0)
            memcpy(pkt->buf->data, pkt->data, pkt->size);
        pkt->data = pkt->buf->data;
    }
    pkt->size += grow_by;
    memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    return 0;
}

将该函数化简,就是:

cpp 复制代码
int av_grow_packet(AVPacket *pkt, int grow_by)
{//...
    new_size = pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE;
    if (pkt->buf) {
    {
    //...
        ret = av_buffer_realloc(&pkt->buf, new_size + data_offset);
    //...
    }
    else
    {
    //...
        pkt->buf = av_buffer_alloc(new_size);
    //...
    }
    pkt->size += grow_by;
    memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}

av_grow_packet函数内部会判断pkt->buf是否为空。如果不为空,通过av_buffer_realloc函数给pkt->buf->data重新分配大小为(pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE+data_offset)个字节内存。如果为空,通过av_buffer_alloc函数给pkt->buf、pkt->buf->buf和pkt->buf->data分配大小为(pkt->size + grow_by + AV_INPUT_BUFFER_PADDING_SIZE)个字节的内存。然后让地址为(pkt->data + pkt->size + grow_by)后的数据字节归零。

关于av_buffer_realloc函数和av_buffer_alloc函数的用法可以参考:《FFmpeg源码:av_buffer_is_writable、av_buffer_realloc函数分析》、《FFmpeg源码:buffer_create、av_buffer_create、av_buffer_default_free、av_buffer_alloc、av_buffer_allocz函数分析

这里重新分配的大小得加上AV_INPUT_BUFFER_PADDING_SIZE个字节,理由跟上面的packet_alloc函数一致。

相关推荐
daidaidaiyu16 小时前
FFmpeg 关键的结构体
c++·ffmpeg
扶尔魔ocy1 天前
【QT window】ffmpeg实现录音功能之无损格式--PCM
ffmpeg·pcm
止礼1 天前
FFmpeg8.0.1 源代码的深入分析
ffmpeg
小曾同学.com1 天前
音视频中的“透传”与“DTS音频”
ffmpeg·音视频·透传·dts
vivo互联网技术1 天前
数字人动画云端渲染方案
前端·ffmpeg·puppeteer·web3d
止礼1 天前
FFmpeg8.0.1 编解码流程
ffmpeg
qs70161 天前
c直接调用FFmpeg命令无法执行问题
c语言·开发语言·ffmpeg
止礼1 天前
FFmpeg8.0.1 Mac环境 CMake本地调试配置
macos·ffmpeg
简鹿视频2 天前
视频转mp4格式具体作步骤
ffmpeg·php·音视频·实时音视频