总结了一下AVPacket与AVFrame中拷贝和释放相关操作。这里我理解在AVFrame与AVPacket传递流转的过程中并不会去每次创建和拷贝音视频数据,音视频数据被存储在AVBuffer中,而AVFrame与AVPacket在流转时进行浅拷贝,只有调用其对应unref时,会减少AVBuffer中的引用计数,最终释放内部存储音视频数据的buffer。

目录

1.av_freep释放并置空双重指针指向的那个指针

2.av_packet_unref

3.av_packet_ref

4.av_packet_move_ref

5.av_packet_alloc与av_packet_free

6.av_free

7.av_frame_unref

8.av_frame_ref

9.av_frame_move_ref

10.av_frame_alloc与av_frame_free


1.av_freep释放并置空双重指针指向的那个指针

释放内存并将指针置空

void av_freep(void *arg)
{void *val;//记录arg值memcpy(&val, arg, sizeof(val));//arg 指向的的首地址置空 memcpy(arg, &(void *){ NULL }, sizeof(val));//真正释放内存av_free(val);
}

示例

static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{…if (src) {**dst = **src;//释放 AVBufferRef **src 中指针指向的内容并将这个指针置空//这里并没有释放AVBufferRef中的指针指向的内存av_freep(src);} …
}

2.av_packet_unref

释放pkt中在堆上的指针结构,复位内部指针,pkt中真正的音视频buffer没有被释放。结果相当于传入的这个AVPacket指针被掏空,音视频数据依然可能被其他AVPacket持有。真正释放AVPacket中音视频数据buffer的操作也应该是在这里。

void av_packet_unref(AVPacket *pkt)
{//置空packet中side_data相关指针,释放相关结构(不释放结构中指针指向的内容)av_packet_free_side_data(pkt);//释放pkt->buf结构,不释放这个结构指向的内容//buf的refcount原子减,如果AVBuffer引用计数为1则调用AVBuffer的freeav_buffer_unref(&pkt->buf);//复位packet内部指针av_init_packet(pkt);pkt->data = NULL;pkt->size = 0;
}

示例:

ffplay中释放AVPacket

static void packet_queue_flush(PacketQueue *q)
{MyAVPacketList *pkt, *pkt1;...for (pkt = q->first_pkt; pkt; pkt = pkt1) {pkt1 = pkt->next;//减少引用计数,符合要求时删除bufferav_packet_unref(&pkt->pkt);av_freep(&pkt);}...
}

3.av_packet_ref

有了av_packet_unref的经验,理解av_packet_ref会相对容易一些。这个函数最终的效果是给dst的data浅拷贝一个值,还会给当前buffer增加引用计数。AVBuffer结构中存储了真的音视频数据,并且维护一个引用计数。

struct AVBuffer {uint8_t *data; /**< data described by this buffer */int      size; /**< size of data in bytes */atomic_uint refcount;void (*free)(void *opaque, uint8_t *data);void *opaque;int flags;
};

下面看一下av_packet_ref的实现。

int av_packet_ref(AVPacket *dst, const AVPacket *src)
{int ret;//拷贝各种属性值,创建side_data指针并将src中的值赋值给它ret = av_packet_copy_props(dst, src);if (ret < 0)return ret;if (!src->buf) {//如果src的buf为空 des的buf创建一块空间 把src的data拷贝给这块空间//创建buf指针并拷贝数据内容ret = packet_alloc(&dst->buf, src->size);if (ret < 0)goto fail;if (src->size)memcpy(dst->buf->data, src->data, src->size);//数据浅拷贝给dstdst->data = dst->buf->data;} else {//否则 src的 buffer->refcount 原子+1dst->buf = av_buffer_ref(src->buf);if (!dst->buf) {ret = AVERROR(ENOMEM);goto fail;}//数据浅拷贝给dstdst->data = src->data;}dst->size = src->size;return 0;
fail:av_packet_free_side_data(dst);return ret;
}

4.av_packet_move_ref 

浅拷贝一切src的一切数据,掏空src,操作不改变AVBuffer的引用计数。ffplay在AVPacket加入队列的时候使用=给结构体赋值。

void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{*dst = *src;av_init_packet(src);src->data = NULL;src->size = 0;
}

5.av_packet_allocav_packet_free

av_packet_free清空当前AVPacket结构体指针,并减少引用计数,代表当前这个AVPacket结构体被释放了,里面的音视频数据可能还在,需要等待最后一个引用这些音视频数据的AVPacket被销毁。如果直接在栈上定义AVPaceket结构体实例,使用av_packet_move_ref或者av_packet_ref赋值,用过之后av_packet_unref一下即可。

AVPacket *av_packet_alloc(void)
{AVPacket *pkt = av_mallocz(sizeof(AVPacket));if (!pkt)return pkt;av_packet_unref(pkt);return pkt;
}void av_packet_free(AVPacket **pkt)
{if (!pkt || !*pkt)return;av_packet_unref(*pkt);av_freep(pkt);
}

区别av_free_packet

这个方法目前看已经废弃,ffplay里也没有搜到调用

#if FF_API_AVPACKET_OLD_API
FF_DISABLE_DEPRECATION_WARNINGS
void av_free_packet(AVPacket *pkt)
{if (pkt) {if (pkt->buf)av_buffer_unref(&pkt->buf);pkt->data            = NULL;pkt->size            = 0;av_packet_free_side_data(pkt);}
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif

6.av_free

对应malloc

void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC_aligned_free(ptr);
#elsefree(ptr);
#endif
}

7.av_frame_unref

这里减少了当前AVFrame中各种buffer的数据引用,同时将当前传入的frame掏空。这样看真正释放AVFrame音视频数据的地方也应该在这里。

void av_frame_unref(AVFrame *frame)
{int i;if (!frame)return;wipe_side_data(frame);for (i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++)av_buffer_unref(&frame->buf[i]);for (i = 0; i < frame->nb_extended_buf; i++)av_buffer_unref(&frame->extended_buf[i]);av_freep(&frame->extended_buf);av_dict_free(&frame->metadata);
#if FF_API_FRAME_QPav_buffer_unref(&frame->qp_table_buf);
#endifav_buffer_unref(&frame->hw_frames_ctx);av_buffer_unref(&frame->opaque_ref);//置空当前frame中的值get_frame_defaults(frame);
}

8.av_frame_ref

主要是src各种浅拷贝加buffer赋值。要想把一个现有的AVFrame复制给新建的,可以使用这个方法,相当于直接浅拷贝并增加引用计数了。

int av_frame_ref(AVFrame *dst, const AVFrame *src)
{int i, ret = 0;av_assert1(dst->width == 0 && dst->height == 0);av_assert1(dst->channels == 0);dst->format         = src->format;dst->width          = src->width;dst->height         = src->height;dst->channels       = src->channels;dst->channel_layout = src->channel_layout;dst->nb_samples     = src->nb_samples;ret = frame_copy_props(dst, src, 0);if (ret < 0)return ret;/* duplicate the frame data if it's not refcounted */if (!src->buf[0]) {ret = av_frame_get_buffer(dst, 32);if (ret < 0)return ret;ret = av_frame_copy(dst, src);if (ret < 0)av_frame_unref(dst);return ret;}/* ref the buffers */for (i = 0; i < FF_ARRAY_ELEMS(src->buf); i++) {if (!src->buf[i])continue;dst->buf[i] = av_buffer_ref(src->buf[i]);if (!dst->buf[i]) {ret = AVERROR(ENOMEM);goto fail;}}if (src->extended_buf) {dst->extended_buf = av_mallocz_array(sizeof(*dst->extended_buf),src->nb_extended_buf);if (!dst->extended_buf) {ret = AVERROR(ENOMEM);goto fail;}dst->nb_extended_buf = src->nb_extended_buf;for (i = 0; i < src->nb_extended_buf; i++) {dst->extended_buf[i] = av_buffer_ref(src->extended_buf[i]);if (!dst->extended_buf[i]) {ret = AVERROR(ENOMEM);goto fail;}}}if (src->hw_frames_ctx) {dst->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);if (!dst->hw_frames_ctx) {ret = AVERROR(ENOMEM);goto fail;}}/* duplicate extended data */if (src->extended_data != src->data) {int ch = src->channels;if (!ch) {ret = AVERROR(EINVAL);goto fail;}CHECK_CHANNELS_CONSISTENCY(src);dst->extended_data = av_malloc_array(sizeof(*dst->extended_data), ch);if (!dst->extended_data) {ret = AVERROR(ENOMEM);goto fail;}memcpy(dst->extended_data, src->extended_data, sizeof(*src->extended_data) * ch);} elsedst->extended_data = dst->data;memcpy(dst->data,     src->data,     sizeof(src->data));memcpy(dst->linesize, src->linesize, sizeof(src->linesize));return 0;fail:av_frame_unref(dst);return ret;
}

9.av_frame_move_ref

掏空一个AVFrame转移数据到另一个,不涉及引用计数变化。

void av_frame_move_ref(AVFrame *dst, AVFrame *src)
{av_assert1(dst->width == 0 && dst->height == 0);av_assert1(dst->channels == 0);*dst = *src;if (src->extended_data == src->data)dst->extended_data = dst->data;memset(src, 0, sizeof(*src));get_frame_defaults(src);
}

10.av_frame_allocav_frame_free

av_frame_alloc与av_frame_free配合使用。av_frame_alloc之后需要av_frame_get_buffer真正开辟空间,av_frame_free会减少这些空间的引用计数。

AVFrame *av_frame_alloc(void)
{AVFrame *frame = av_mallocz(sizeof(*frame));if (!frame)return NULL;frame->extended_data = NULL;get_frame_defaults(frame);return frame;
}void av_frame_free(AVFrame **frame)
{if (!frame || !*frame)return;av_frame_unref(*frame);av_freep(frame);
}

上面av_frame_alloc并没有给AVFrame中buffer增加数据,需要进行如下操作申请buffer并为其增加音视频数据。而av_frame_free中av_frame_unref会去减少这些内存的引用并在最后释放这些内存。

    frame->format = c->pix_fmt;frame->width  = c->width;frame->height = c->height;//这里初始化了AVFrame中的bufferret = av_frame_get_buffer(frame, 32);//确保当前frame可写 如果不可写的情况下会给frame搞一块新的bufferret = av_frame_make_writable(frame);if (ret < 0)exit(1);//给AVFrame中buffer赋值for (y = 0; y < c->height; y++) {for (x = 0; x < c->width; x++) {frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;}}

ffmpeg中AVPacket与AVFrame中数据的传递与释放相关推荐

  1. ffmpeg框架中如何从avframe中获取yuv数据用于测试

    avcodec_decode_video2(pstream_info->dec_ctx, pDecodeFrame, &frameFinished,&pkt); if(frame ...

  2. ffmpeg packet和frame区别与联系(AVPacket、AVFrame)

    read_thread函数的主要工作:首先,创建解复用和解码所需要的数据结构.其次,分别通过stream_component_open函数打开三种数据流.最后,通过av_read_frame将解复用后 ...

  3. 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket-解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )

    文章目录 I . FFMPEG 解码 AVPacket 数据到 AVFrame 前置操作 II . FFMPEG 解码 AVPacket 数据到 AVFrame 流程 III . FFMPEG 发送 ...

  4. 【FFMPEG】AVFrame中buffer分配的两种方式

    AVFrame在使用ffmpeg进行编解码过程中,是最基本的数据结构. 在某些场景下,需要对AVFrame的数据区域进行提前分配,有两种方法,需要根据自己的需求来使用. (1) * This func ...

  5. FFmpeg中AVFrame中width与linesize的关系

    FFmpeg中AVFrame中width与linesize的关系 比如704*576分辨率的视频,它的width=704,height=576,摄像机芯片一般会要求64或者128对齐,当128位对齐时 ...

  6. ffmpeg内存模型及AVPacket和AVFrame API基本使用

    ffmpeg内存模型及AVPacket和AVFrame API解释 目录 ffmpeg内存模型 AVPacket常用API AVPacket Demo AVFrame常用API 1. ffmpeg内存 ...

  7. 从FFmpeg 4. 2源码中提取dshow mjpeg code步骤

    之前在https://blog.csdn.net/fengbingchun/article/details/103735560 中介绍过在Windows上通过vs2017编译FFmpeg源码进行单步调 ...

  8. AVFrame中data与linesize关系

    AVFrame中data与linesize关系 2016年06月30日 11:10:40 阅读数:662 AVFrame里面有data[4]和linesize[4]其中data是一个指向指针的指针(二 ...

  9. 从MP4头信息中提取sps和pps数据

    转自:https://blog.csdn.net/tracydawn123/article/details/31773153 一.MP4格式基本概念 MP4格式对应标准MPEG-4标准(ISO/IEC ...

最新文章

  1. 有时候明明没有问题的程序为什么通不过?
  2. SQLite介绍、学习笔记、性能测试
  3. css(float浮动和clear清除)
  4. VisualNet在资源管理中的应用
  5. Mysql 客户端查询结果如何保存到本地而不是服务端?
  6. Java知识整理——远程方法调用
  7. [开源] FreeSql.AdminLTE.Tools 根据实体类生成后台管理代码
  8. html对定位图片的某一部分_某系统存任意文件上传
  9. 设置iis支持wap服务
  10. ASP.NET AJAX Advance Tips Tricks (11) 三种方法动态创建Tooltip
  11. cad道路里程桩号标注_cad桩号标注插件
  12. “蔡徐坤微博转发过亿”幕后推手一审获刑五年
  13. 计算机一级excel典型试题,最新excel计算机一级试题合集
  14. 祖师爷获新认可!图灵成为 50 英镑新钞人物
  15. 词根词缀 (一):前缀篇
  16. SSL单向、双向认证
  17. Android证书生成(android studio)
  18. ACM-ICPC 2018 南京赛区网络预赛 I.Skr(Manacher马拉车+Hash哈希/回文树)
  19. Java 多线程 | 并发知识面试问答总结
  20. aida64使用方法_AIDA64怎么用 AIDA64使用教程与方法简介

热门文章

  1. 【Python自查手册】之项目实操
  2. 【台大郭彦甫】Matlab入门教程超详细学习笔记二:基本操作与矩阵运算(附PPT链接)
  3. 安卓Okhttp3源码的简单分析
  4. linux操作系统的关机命令
  5. html怎么给段落设置背景色,css的(文字、背景、段落)样式
  6. 设计模式 - 装饰器模式
  7. 2022 七校联合NewStarCTF 公开赛赛道 WEEK2|CRYPTO
  8. YOLOv7详解:实时目标检测新标杆 | Chien-Yao Wang团队与Alexey团队倾力打造
  9. java计算年龄_java根据出生年月日精确计算年龄的算法
  10. STM32F103定时器解码PT2262