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

相关推荐
-Mr_X-1 小时前
windows下srs流媒体服务器使用ffmpeg推流
ffmpeg
dvlinker3 小时前
C++开源项目 VLC 源代码的交叉编译以及库的裁剪方法详解
ffmpeg·mingw-w64·msys2·cygwin·开源vlc·vlc编译·vlc裁剪
因我你好久不见17 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
cuijiecheng20181 天前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频
cuijiecheng20181 天前
音视频入门基础:AAC专题(13)——FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现
ffmpeg·音视频·aac
流氓也是种气质 _Cookie2 天前
uniapp blob格式转换为video .mp4文件使用ffmpeg工具
ffmpeg·uni-app
网络安全queen2 天前
网络安全-企业环境渗透2-wordpress任意文件读&&FFmpeg任意文件读
安全·web安全·ffmpeg
yerennuo2 天前
FFmpeg库之ffmpeg
qt·ffmpeg
韩曙亮2 天前
【FFmpeg】解封装 ① ( 封装与解封装流程 | 解封装函数简介 | 查找码流标号和码流参数信息 | 使用 MediaInfo 分析视频文件 )
ffmpeg·音视频·视频流·mediainfo·解封装·码流
yerennuo2 天前
FFmpeg 框架简介和文件解复用
ffmpeg