ffmpeg内存模型
## AVPacket和AVFrame内存模型
从现有的Packet拷贝到一个新Packet的时候,有两种情况:
-
两个Packet的buf引用的是同一个数据缓存空间,这时候就要注意数据缓存空间的释放问题;
-
两个Packet的buf是引用的不同的数据缓存空间,每个Packet都有数据缓存空间的copy;
第一种方式可以理解为引用计数,有多个对象对一段内存进行操作。
第二个就是两个对象有各自的数据域,不会互相影响。
数据域对象AVBuffer
中是有引用计数相关的成员的,UML如下:
对于多个AVPacket共享同一个缓存空间,FFmpeg使用的引用计数的机制:
- 初始化引用计数为0,只有真正分配AVBuffer的时候,引用计数初始化为1;
- 当有新的Packet引用共享的缓存空间的时候,就将引用计数+1;
- 当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放了引用的缓存空间AVBuffer。
AVFrame也是使用相同的机制。
AVPacket常用API
AVFrame常用API
## 测试代码
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>#define MEM_ITEM_SIZE (1024*20*102)
#define AVPACKET_LOOP_COUNT 1000void av_packet_test1() {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_packet_unref(pkt); // 不需要这一步,av_packet_free中会调用av_packet_unrefav_packet_free(pkt);
}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); // 只会初始化结构体,不会分配内存,将data=NULL,但是size!=0,不会被判断为空包av_packet_free(pkt); // 会释放data的内存,如果data没有被分配,则会导致内存泄漏
}void av_packet_test3() {AVPacket* pktsrc = NULL;AVPacket* pktdst = NULL;int ret = 0;pktsrc = av_packet_alloc();ret = av_new_packet(pktsrc, MEM_ITEM_SIZE);memccpy(pktsrc->data, (void*)av_packet_test1, 1, MEM_ITEM_SIZE);pktdst = av_packet_alloc();av_packet_move_ref(pktdst, pktsrc); // 执行完后pktsrc会变成空包,即data为NULL,size=0av_init_packet(pktsrc); // 没有必要执行这一步,因为av_packet_move_ref中会执行av_init_packet,av_packet_free(pktsrc); // 由于data=NULL size=0 所以判断是空包,只会释放结构体,不会释放dataav_packet_free(pktdst);
}void av_packet_test4()
{AVPacket *pkt = NULL;// av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_allocAVPacket *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);
}void av_packet_test5()
{AVPacket *pkt = NULL;AVPacket *pkt2 = NULL;int ret = 0;pkt = av_packet_alloc(); //if(pkt->buf) // 打印referenc-counted,必须保证传入的是有效指针{ printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt->buf));}ret = av_new_packet(pkt, MEM_ITEM_SIZE);if(pkt->buf) // 打印referenc-counted,必须保证传入的是有效指针{ printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt->buf));}memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);pkt2 = av_packet_alloc(); // 必须先allocav_packet_move_ref(pkt2, pkt); // av_packet_move_ref
// av_init_packet(pkt); //av_packet_move_refav_packet_ref(pkt, pkt2);av_packet_ref(pkt, pkt2); // 多次ref如果没有对应多次unref将会内存泄漏if(pkt->buf) // 打印referenc-counted,必须保证传入的是有效指针{ printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt->buf));}if(pkt2->buf) // 打印referenc-counted,必须保证传入的是有效指针{ printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt2->buf));}av_packet_unref(pkt); // 将为2av_packet_unref(pkt); // 做第二次是没有用的if(pkt->buf)printf("pkt->buf没有被置NULL\n");elseprintf("pkt->buf已经被置NULL\n");if(pkt2->buf) // 打印referenc-counted,必须保证传入的是有效指针{ printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt2->buf));}av_packet_unref(pkt2);av_packet_free(&pkt);av_packet_free(&pkt2);
}int main() {// av_packet_test1();// av_packet_test2();// av_packet_test3();av_packet_test5();return 0;
}
参考资料:https://github.com/0voice