流媒体开发18FFmpeg内存模型
Posted 努力努力再努力~~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了流媒体开发18FFmpeg内存模型相关的知识,希望对你有一定的参考价值。
本文介绍了AVPacket和AVFrame两块buffer是怎么管理的,并梳理了所有相关的接口,介绍这些接口内部 具体实现了什么。
一、引出
问题:
1、从av_read_frame读取到一个AVPacket后怎么放入队列?
2、从avcodec_recevice_frame读取到一个AVFrame后又怎么放入队列?
二、拷贝
从现有的Packet拷贝一个新Packet的时候,有两种情况:
- 浅拷贝:两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题;
- 深拷贝:两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy;
三、引用计数与结构体层级
本节我们只讨论浅拷贝,packet1和和packet2都不使用时才能释放,增加引用计数的概念,每次拷贝都会将计数器加1,释放时引用计数减1,代码中的定义如下:
结构体AVPacket和AVFrame都包含结构体AVBufferRef,AVBufferRef中包含指针AVBuffer,AVBuffer的定义如下:
struct AVBuffer
uint8_t *data; /**< 实际的内存指针地址 */
size_t size; /**< 申请的内存大小 */
atomic_uint refcount; /* 引用计数,用于管理内存的申请和释放 */
/**
* a callback for freeing the data
*/
void (*free)(void *opaque, uint8_t *data);
/**
* an opaque pointer, to be used by the freeing callback
*/
void *opaque;
/**
* A combination of AV_BUFFER_FLAG_*
*/
int flags;
/**
* A combination of BUFFER_FLAG_*
*/
int flags_internal;
;
- 对于多个AVPacket共享同一个缓存空间,FFmpeg使用的引用计数的机制(reference-count):
- 初始化引用计数为0,只有真正分配AVBuffer的时候,引用计数初始化为1;
- 当有新的Packet引用共享的缓存空间时,就将引用计数+1;
- 当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存空间AVBuffer。
- AVFrame也是采用同样的机制
四、常用API
4.1AVPacket常用API
av_packet_ref和 av_packet_move_ref的区别:
av_packet_ref最终指向同一个AVBuffer,av_packet_move_ref则是删除AVPacket1中对AVBufferRef的引用(AVBufferRef会置为NULL),而是AVPacket2会指向到AVPacket1的AVBufferRef
av_read_frame读取数据时也会将引用计数+1,使用完之后需要unref,av_read_frame不会去释放pkt的buf,如果我们外部不去释放,就会出现内存泄露
4.2AVFrame常用API
五、demo级验证
1、av_packet_free内部会调用av_packet_unref,av_packet_unref重复调用也没关系 ,每判空,av_packet_unref中是将AVPacket的内容清空
void av_packet_test1()
AVPacket *pkt = NULL;
int ret = 0;
pkt = av_packet_alloc();
ret = av_new_packet(pkt, MEM_ITEM_SIZE); // 引用计数初始化为1
memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
av_packet_unref(pkt); // 要不要调用
av_packet_free(&pkt); // 如果不free将会发生内存泄漏,内部调用了 av_packet_unref
2、av_init_packet会将AVPacket清空,不能随意调用,可能造成内存的泄露
void av_packet_test2()
AVPacket *pkt = NULL;
int ret = 0;
pkt = av_packet_alloc();
ret = av_new_packet(pkt, MEM_ITEM_SIZE);
memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
// av_init_packet(pkt); // 这个时候init就会导致内存无法释放
av_packet_free(&pkt);
3、av_packet_clone等价于av_packet_alloc()+av_packet_ref()
void av_packet_test4()
AVPacket *pkt = NULL;
// av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_alloc
AVPacket *pkt2 = NULL;
int ret = 0;
pkt = av_packet_alloc();
ret = av_new_packet(pkt, MEM_ITEM_SIZE);
memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
pkt2 = av_packet_clone(pkt); // av_packet_alloc()+av_packet_ref()
av_init_packet(pkt);
av_packet_free(&pkt);
av_packet_free(&pkt2);
4、多次ref到同一个AVPacket中会导致后面释放时内存泄露
av_packet_ref(pkt, pkt2);
av_packet_ref(pkt, pkt2); // 多次ref如果没有对应多次unref将会内存泄漏
av_packet_unref(pkt); //调过一次之后,AVPacket会设置为0,后面再次调用发现为0,就不会减引用了
以上是关于流媒体开发18FFmpeg内存模型的主要内容,如果未能解决你的问题,请参考以下文章
nginx+ffmpeg搭建rtmp转播rtsp流的flash服务器