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

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

AVPacket结构体和其相关的函数分析:

FFmpeg存放压缩后的音视频数据的结构体:AVPacket简介

FFmpeg源码:av_init_packet、get_packet_defaults、av_packet_alloc函数分析

FFmpeg源码:av_packet_free_side_data、av_packet_unref、av_packet_free函数分析

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

FFmpeg源码:av_packet_move_ref、av_packet_make_refcounted函数分析

FFmpeg源码:PacketList结构体、avpriv_packet_list_put、avpriv_packet_list_get、avpriv_packet_list_free函数分析

FFmpeg源码:append_packet_chunked、av_get_packet函数分析

FFmpeg中调用av_read_frame函数导致的内存泄漏问题

使用vs诊断工具检测FFmpeg的内存泄漏问题

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

一、packet_alloc函数

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

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;
}

该函数作用是:如果(*buf)为空,分配一个新的内存块给(*buf)指向的AVBufferRef对象,给(*buf)->buffer分配内存。无论(*buf)是否为空,给(*buf)->data重新分配大小为(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。可以看到其内部调用了av_buffer_realloc函数,关于av_buffer_realloc函数的用法可以参考:《FFmpeg源码:av_buffer_is_writable、av_buffer_realloc函数分析》。

形参buf:既是输入型参数也是输出型参数。指针的指针,*buf指向某个AVPacket对象的AVBufferRef成员。

形参size:输入型参数。给(*buf)->data重新分配的大小是(size + AV_INPUT_BUFFER_PADDING_SIZE)个字节。

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

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

/**
 * @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中:

/**
 * 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中:

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中:

/**
 * 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中:

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函数的声明

/**
 * 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)字节。返回0表示成功,返回负数表示失败。

(二)av_grow_packet函数的定义

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

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;
}

将该函数化简,就是:

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函数一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值