FFmpeg源码:av_packet_move_ref、av_packet_make_refcounted函数分析

一、av_packet_move_ref函数

(一)av_packet_move_ref函数的声明

av_packet_move_ref函数声明在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavcodec/packet.h中:

cpp 复制代码
/**
 * Move every field in src to dst and reset src.
 *
 * @see av_packet_unref
 *
 * @param src Source packet, will be reset
 * @param dst Destination packet
 */
void av_packet_move_ref(AVPacket *dst, AVPacket *src);

该函数作用是:类似C++11中的移动语义,将形参src指向的AVPacket对象的所有属性(包括其成员变量,src->buf、src->data、src->side_data指向的缓冲区中的数据)移动到形参dst指向的AVPacket对象中。执行该函数后,src指向的AVPacket对象会被初始化(成员变量pts、dts、pos、time_base赋默认值,其它成员变量包括指针赋值为0或NULL)。

(二)av_packet_move_ref函数的定义

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

cpp 复制代码
void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{
    *dst = *src;
    get_packet_defaults(src);
}

该函数内部,首先通过下面语句,将形参src指向的AVPacket对象(以下简称原对象)浅拷贝(这里就是模仿C++的浅拷贝)到形参dst指向的AVPacket对象(以下简称目标对象)中,通过浅拷贝只对目标对象赋值原对象的成员变量的值,并不复制原对象的动态分配内存(src->buf、src->data、src->side_data指向的缓冲区)等外部资源:

cpp 复制代码
*dst = *src;

为了避免原对象修改自己指向的外部资源时,可能会影响到目标对象,通过下面语句给原对象的成员变量pts、dts、pos、time_base赋默认值,给原对象的其它成员变量包括指针值赋值为0或NULL:

cpp 复制代码
get_packet_defaults(src);

可以看到,FFmpeg通过上面两个语句实现了类似C++11中的移动语义效果,实现AVPacket的所有权从原对象转移到目标对象,只是转移,没有内存的搬迁或者内存拷贝,从而极大地提高代码运行效率。

二、av_packet_make_refcounted函数

(一)av_packet_make_refcounted函数的声明

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

cpp 复制代码
/**
 * Ensure the data described by a given packet is reference counted.
 *
 * @note This function does not ensure that the reference will be writable.
 *       Use av_packet_make_writable instead for that purpose.
 *
 * @see av_packet_ref
 * @see av_packet_make_writable
 *
 * @param pkt packet whose data should be made reference counted.
 *
 * @return 0 on success, a negative AVERROR on error. On failure, the
 *         packet is unchanged.
 */
int av_packet_make_refcounted(AVPacket *pkt);

该函数作用是:确保给定数据包(pkt指向的对象)描述的数据是引用计数的。

1.如果pkt->buf不为空,表示给定数据包描述的数据已经是引用计数了。该函数返回0,不执行也不需要执行其它操作。

2.如果pkt->buf为空,给pkt->buf和pkt->buf->buffer分配内存,给pkt->buf->data重新分配大小为(pkt->size + AV_INPUT_BUFFER_PADDING_SIZE)个字节内存。然后把pkt->data中的数据拷贝pkt->size个字节到pkt->buf->data中。执行上述操作(情况"2"的操作)后,pkt->buf->buffer->refcount的值会是1,所以可以保证数据已经是引用计数了。

返回值:返回0表示成功,返回一个负数表示失败。

(二)av_packet_make_refcounted函数的定义

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

cpp 复制代码
int av_packet_make_refcounted(AVPacket *pkt)
{
    int ret;

    if (pkt->buf)
        return 0;

    ret = packet_alloc(&pkt->buf, pkt->size);
    if (ret < 0)
        return ret;
    av_assert1(!pkt->size || pkt->data);
    if (pkt->size)
        memcpy(pkt->buf->data, pkt->data, pkt->size);

    pkt->data = pkt->buf->data;

    return 0;
}

当pkt->buf为空时,av_packet_make_refcounted函数内部会执行packet_alloc函数,而packet_alloc函数内部会执行av_buffer_realloc函数,av_buffer_realloc函数内部又会执行av_buffer_create函数,从《FFmpeg源码:buffer_create、av_buffer_create、av_buffer_default_free、av_buffer_alloc、av_buffer_allocz函数分析》中可以知道,av_buffer_create函数会让refcount的值被初始化为1,所以可以保证数据是引用计数的。

相关推荐
韩曙亮6 小时前
【FFmpeg】FFmpeg 内存结构 ③ ( AVPacket 函数简介 | av_packet_ref 函数 | av_packet_clone 函数 )
ffmpeg·音视频·avpacket·av_packet_clone·av_packet_ref·ffmpeg内存结构
oushaojun210 小时前
ubuntu中使用ffmpeg和nginx推流rtmp视频
nginx·ubuntu·ffmpeg·rtmp
莫固执,朋友11 小时前
网络抓包工具tcpdump 在海思平台上的编译使用
网络·ffmpeg·音视频·tcpdump
lxkj_202411 小时前
修改ffmpeg实现https-flv内容加密
网络协议·https·ffmpeg
cuijiecheng201811 小时前
音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
ffmpeg·音视频
VisionX Lab16 小时前
数据脱敏工具:基于 FFmpeg 的视频批量裁剪
python·ffmpeg·音视频
柳鲲鹏1 天前
全网首发:Ubuntu编译跨平台嵌入式支持ffmpeg的OpenCV
linux·ubuntu·ffmpeg
冰山一脚20131 天前
ffplay音频SDL播放处理
ffmpeg
cuijiecheng20182 天前
音视频入门基础:MPEG2-TS专题(7)——FFmpeg源码中,读取出一个transport packet数据的实现
ffmpeg·音视频
Maxwellhang2 天前
【java-ffmpeg】java 内存测试和集成
java·ffmpeg·jni