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 1000
void 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_unref
av_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=0
av_init_packet(pktsrc); // 没有必要执行这一步,因为av_packet_move_ref中会执行av_init_packet,
av_packet_free(pktsrc); // 由于data=NULL size=0 所以判断是空包,只会释放结构体,不会释放data
av_packet_free(pktdst);
}
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);
}
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(); // 必须先alloc
av_packet_move_ref(pkt2, pkt); // av_packet_move_ref
// av_init_packet(pkt); //av_packet_move_ref
av_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); // 将为2
av_packet_unref(pkt); // 做第二次是没有用的
if(pkt->buf)
printf("pkt->buf没有被置NULL\n");
else
printf("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;
}
评论