解码后视频渲染并测试多线程帧率

解码后视频渲染并测试多线程帧率,第1张

118_test_decode_view.cpp

#include 
#include 
#include "XVideoView.h"
#include "XSDL.h"

using namespace std;

extern "C"  // 指定函数是 C 语言函数,函数目标名不包含重载标识,C++ 中调用 C 函数需要使用 extern "C"
{
	// 引用 ffmpeg 头文件
	#include "libavcodec/avcodec.h"
}

// 预处理指令导入库
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")

int main()
{
	//1 分割h264 存入AVPacket
	// ffmpeg -i v1080.mp4 -s 400x300 test.h264
	string file_name = "test.h264";
	ifstream ifs;
	AVCodec* codec = nullptr;
	AVCodecID codec_id = AV_CODEC_ID_H264;
	AVCodecContext* context = nullptr;
	AVCodecParserContext* parser = nullptr;
	AVPacket* packet = nullptr;
	AVFrame* frame = nullptr;
	unsigned char inbuf[4096] = { 0 };
	int inbuf_len = 0;
	int ret = 0;
	long long begin = 0;
	int cnt = 0;  // 解码的帧率
	XVideoView* view = XVideoView::Create();
	bool first_init = false;

	ifs.open(file_name, ios::in | ios::binary);

	if (!ifs)
	{
		return -1;
	}

	//1 找解码器
	codec = avcodec_find_decoder(codec_id);

	if (codec == nullptr)
	{
		cerr << "avcodec_find_decoder failed!" << endl;

		return -1;
	}

	//2 创建上下文
	context = avcodec_alloc_context3(codec);

	if (context == nullptr)
	{
		cerr << "avcodec_alloc_context3 failed!" << endl;

		return -1;
	}

	context->thread_count = 16;

	//3 打开上下文
	ret = avcodec_open2(context, nullptr, nullptr);

	if (ret < 0)
	{
		cerr << "avcodec_open2 failed!" << endl;

		return -1;
	}

	// 分割上下文
	parser = av_parser_init(codec_id);

	if (parser == nullptr)
	{
		cerr << "av_parser_init failed!" << endl;

		return -1;
	}

	packet = av_packet_alloc();

	if (packet == nullptr)
	{
		cerr << "av_packet_alloc failed!" << endl;

		return -1;
	}

	frame = av_frame_alloc();

	if (frame == nullptr)
	{
		cerr << "av_frame_alloc failed!" << endl;

		return -1;
	}

	begin = NowMs();

	while (!ifs.eof())
	{
		int len = 0;
		unsigned char* data = inbuf;

		ifs.read((char*)inbuf, sizeof(inbuf));
		len = ifs.gcount();  // 读取的字节数

		while (len > 0)
		{
			// 通过 0001 截断输出到AVPacket 如果满足一个 AVPackret 则返回这个 AVPacket 的大小 不足一个AVPactet 则返回余下的数据大小 不足一个AVPactet的数据会被缓存起来
			int in_len = av_parser_parse2(parser, context,
				&packet->data, &packet->size,  // 输出
				data, len,					   // 输入
				AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0
			);

			data += in_len;
			len -= in_len;

			if (packet->size > 0)
			{
				// cout << packet->size << " " << flush;

				//发送packet到解码线程
				ret = avcodec_send_packet(context, packet);

				if (ret != 0)
				{
					cerr << "avcodec_send_packet failed!" << endl;
					break;
				}

				/* 获取多帧解码数据 */ 
				ret = avcodec_receive_frame(context, frame);

				while (ret == 0)
				{
					long long curr = NowMs();

					cnt++;

					if (curr - begin >= 100)
					{
						cout << "FPS = " << cnt * 10 << endl;
						cnt = 0;
						begin = curr;
					}

					if (!first_init)
					{
						view->init(frame->width, frame->height, static_cast(frame->format));
						first_init = true;
					}

					view->drawAVFrame(frame);  // 渲染

					//每次会调用av_frame_unref 
					ret = avcodec_receive_frame(context, frame);
				}
			}
		}
	}

	/* 取出缓存数据 */
	avcodec_send_packet(context, nullptr);

	ret = avcodec_receive_frame(context, frame);

	while (ret == 0)
	{
		view->drawAVFrame(frame);  // 渲染
		ret = avcodec_receive_frame(context, frame);
	}

	av_frame_free(&frame);
	av_packet_free(&packet);
	av_parser_close(parser);
	avcodec_free_context(&context);
	ifs.close();
	view->close();
	delete view;

	return 0;
}

我们对解码后的原始帧数据进行渲染。由于我们不知道原始帧的分辨率和像素格式,所以我们需要在解码出第一帧,在得到原始帧的分辨率和像素格式后再初始化窗口。

原始帧的分辨率为 1920x1080,下面进行解码帧率测试。

单线程进行解码的帧率如下图所示

使用16个线程进行解码的帧率如下图所示

可以得出结论:使用多线程进行解码的效率要高于单线程进行解码的效率。 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/707310.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存