FFmpeg中AVPacket、AVFrame结构的基本使用

本文详细介绍了AVPacket和AVFrame结构中内存分配、释放以及引用计数的处理,包括正确和错误的使用方法,避免内存泄漏的关键点。
摘要由CSDN通过智能技术生成

 AVPacket结构以及一些API的使用

#include <iostream>

extern "C"
{
#include <libavcodec\packet.h>
}

// 1. 测试仅alloc不free会不会导致内存泄漏
void test01()
{
	AVPacket* pkt = NULL;
	while (true)
	{
		pkt = av_packet_alloc();	// 内存会一直增长(由此可见内部不会自己释放)
		_sleep(1);
	}
}

// 2. 测试alloc与free配合使用
void test02()
{
	AVPacket* pkt = NULL;
	while (true)
	{
		pkt = av_packet_alloc();		// 分配后释放,不会导致内存泄漏
		_sleep(1);
		av_packet_free(&pkt);
	}
}

// 3. 使用new为pkt里的buff(AVBufferRef)分配内存,查看引用计数
void test03()
{
	AVPacket* pkt = NULL;
	pkt = av_packet_alloc();	// 申请packet内存(bufferRef 0x00)
	av_new_packet(pkt, 1024);	// 申请AVBufferRef内存 (引用计数初始化为1)
	std::cout << "buffRef Cnt: " << av_buffer_get_ref_count(pkt->buf) << std::endl;	// 1
	// av_packet_unref(pkt);	// 这里不需要调(av_packet_free调用一次av_packet_unref)
	av_packet_free(&pkt);
}

// 4. 误使用init函数导致buffRef指向的内存无法被释放
void test04()
{
	AVPacket* pkt = NULL;
	while (true)
	{
		pkt = av_packet_alloc();
		av_new_packet(pkt, 1024);
		av_init_packet(pkt);		// 指向AVBuffRef的buf指针被置空,导致内存咔咔漏
		av_packet_free(&pkt);		// AVBuffRef的buf内存无法释放
		_sleep(1);
	}
	// 注意: 还有一个av_free_packet函数
	// av_packet_free 清空buf,然后将pkt置空
	// av_free_packet 清空buf,并不会将pkt置空
}

// 5. av_packet_move_ref的使用
void test05()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	pkt1 = av_packet_alloc();
	av_new_packet(pkt1, 1024);
	pkt2 = av_packet_alloc();			// 必须先给pkt2分配内存
	av_packet_move_ref(pkt2, pkt1);		// 将pkt1的AVBuffRef以及其它参数移交给pkt2,将pkt1参数初始化init
	av_packet_free(&pkt1);				// 这里均要将pkt的内存释放掉
	av_packet_free(&pkt2);				// 因为此时引用计数为1,内部一次uref就能释放buf内存
}

// 6. av_packet_clone使用
void test06()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);
		pkt2 = av_packet_clone(pkt1);	// av_packet_alloc + av_packet_ref

		av_packet_free(&pkt1);			// 两次free会将引用计数为2的buf清除(不会造成内存泄漏)
		av_packet_free(&pkt2);
	}
}

// 7. av_packet_clone 后使用  av_init_packet 会导致buf内存无法释放
void test07()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);
		pkt2 = av_packet_clone(pkt1);	

		av_init_packet(pkt1);			// 导致av_packet_free(pkt1) 引用计数不减(导致无法释放) 内存咔咔漏

		av_packet_free(&pkt1);			
		av_packet_free(&pkt2);
	}
}

// 8. AVPacket整个结构体赋值, 和av_packet_move_ref类似
void test08()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);

		pkt2 = av_packet_alloc();
		*pkt2 = *pkt1;
		
		av_init_packet(pkt1);
		av_packet_free(&pkt1);	// 需要将pkt1的内存释放掉(否则会导致内存泄漏)
		av_packet_free(&pkt2);
	}
}

// 9. 多次使用 av_packet_ref 导致内存无法释放的问题
void test09()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);	// 引用计数加一

		pkt2 = av_packet_alloc();

		av_packet_ref(pkt2, pkt1);
		av_packet_ref(pkt2, pkt1);	// 此时引用计数为3

		av_packet_free(&pkt1);		// 但是仅仅引用计数减两次(导致内存泄漏)
		av_packet_free(&pkt2);
	}
}

// 10. 先uref一次呢???
void test10()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);

		pkt2 = av_packet_alloc();

		av_packet_ref(pkt2, pkt1);
		av_packet_ref(pkt2, pkt1);	// 此时引用计数为3

		av_packet_unref(pkt1);		// 内存还是会泄露,目前为两个pkt指向同一个AVBuffRef
		av_packet_free(&pkt1);		// 两次unref(pkt1) 会有一次无效
		av_packet_free(&pkt2);		// 从而导致内存咔咔漏
	}
}

// 11. 正确使用
void test11()
{
	AVPacket* pkt1 = NULL;
	AVPacket* pkt2 = NULL;

	while (true)
	{
		pkt1 = av_packet_alloc();
		av_new_packet(pkt1, 1024);

		pkt2 = av_packet_alloc();

		av_packet_ref(pkt2, pkt1);	// 增加引用
		av_packet_unref(pkt1);		// 减少引用(此时AVBuffRef被pkt2持有)
		if (pkt1->buf)
		{
			std::cout << "pkt1 has buf" << std::endl;
		}
		if (pkt2->buf)
		{
			std::cout << "pkt2 has buf" << std::endl;	// 被打印
		}
		av_packet_ref(pkt2, pkt1);	// 此时引用计数为2


		av_packet_free(&pkt1);		// 两次释放将buf释放掉
		av_packet_free(&pkt2);		
	}
}

  AVFrame结构以及一些API的使用

 

#include <iostream>

extern "C"
{
#include <libavutil\frame.h>
#include <libavcodec\avcodec.h>
}

// 1. allo与free配对使用
void frame_test01()
{
	AVFrame* frame = NULL;	

	while (true)
	{
		frame = av_frame_alloc();	
		av_frame_free(&frame);	// 不配对使用会导致内存泄漏
	}
}

// 2. 根据格式分配内存
void frame_test02()
{
	AVFrame* frame = NULL;
	while (true)
	{
		frame = av_frame_alloc();
		frame->nb_samples = 1024;
		frame->format = AV_SAMPLE_FMT_S16P;					// AV_SAMPLE_FMT_S16P AV_SAMPLE_FMT_S16
		frame->channel_layout = AV_CH_LAYOUT_STEREO;        // AV_CH_LAYOUT_MONO AV_CH_LAYOUT_STEREO(立体音)
		av_frame_get_buffer(frame, 0);						// frame中的buf会根据格式自动分配内存
		// buf有多个,因为数据排列的格式不一样(yuv pcm) 
		// 当为音频帧 平面模式且双声道时(左右声道数据会分开存储)为交错模式时(数据存储在一起)
		if (frame->buf && frame->buf[0])
		{
			std::cout << "buf[0] size: " << frame->buf[0]->size << std::endl;	
		}
		if (frame->buf && frame->buf[1])
		{
			std::cout << "buf[1] size: " << frame->buf[1]->size << std::endl;
		}
		// AV_SAMPLE_FMT_S16 AV_CH_LAYOUT_STEREO  交错立体音(一个 buf[0] size 1024 * 2 * 2 = 4096)
		// AV_SAMPLE_FMT_S16P AV_CH_LAYOUT_STEREO 平面立体音(两个 buf[0] size:2048 buf[1]size:2048) 平面且双声道分开存储

		// AV_SAMPLE_FMT_S16 AV_CH_LAYOUT_MONO  交错单声道(一个 buf[0] size 1024 * 2 = 2048)
		// AV_SAMPLE_FMT_S16P AV_CH_LAYOUT_MONO 平面单声道(一个 buf[0] size:2048)


		// 2. av_frame_make_writable (当frame本声为空时不能av_frame_make_writable)
		if (frame)
		{
			av_frame_make_writable(frame);	// 分配新缓冲,赋值frame数据,防止和解码器造成冲突
		}

		// av_frame_unref(frame);			// av_frame_free内部有引用计数减一操作
		av_frame_free(&frame);	
	}
}

int main_frame()
{
	//frame_test01();
	frame_test02();

	getchar();
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

石小浪♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值