【HEVC学习与研究】31、HM编码器的基本结构

简介: 通过解码器代码的研究,已经对HEVC的编解码技术有了一个初步的认识。现在我们就对照着编码器的代码进一步理解HEVC视频编码算法的各个技术细节。 编码器在整个HM解决方案中的工程名为TAppEncoder,入口点函数位于encmain.

通过解码器代码的研究,已经对HEVC的编解码技术有了一个初步的认识。现在我们就对照着编码器的代码进一步理解HEVC视频编码算法的各个技术细节。

编码器在整个HM解决方案中的工程名为TAppEncoder,入口点函数位于encmain.cpp文件中:

int main(int argc, char* argv[])
{
	TAppEncTop  cTAppEncTop;

	// print information
	fprintf( stdout, "\n" );
	fprintf( stdout, "HM software: Encoder Version [%s]", NV_VERSION );
	fprintf( stdout, NVM_ONOS );
	fprintf( stdout, NVM_COMPILEDBY );
	fprintf( stdout, NVM_BITS );
	fprintf( stdout, "\n" );

	// create application encoder class
	cTAppEncTop.create();

	// parse configuration
	try
	{
		if(!cTAppEncTop.parseCfg( argc, argv ))
		{
			cTAppEncTop.destroy();
			return 1;
		}
	}
	catch (po::ParseFailure& e)
	{
		cerr << "Error parsing option \""<< e.arg <<"\" with argument \""<< e.val <<"\"." << endl;
		return 1;
	}

	// starting time
	double dResult;
	long lBefore = clock();

	// call encoding function
	cTAppEncTop.encode();

	// ending time
	dResult = (double)(clock()-lBefore) / CLOCKS_PER_SEC;
	printf("\n Total Time: %12.3f sec.\n", dResult);

	// destroy application encoder class
	cTAppEncTop.destroy();

	return 0;
}

可以很清楚地看到,整个main函数非常简洁清晰,主要可以分为几大部分,分别是输入软件信息、创建编码器类的实例、解析配置文件、获取开始时间、编码数据、计算耗费时间和销毁编码器类的实例几大部分。我们主要关心的编码过程仅通过调用编码器实例的一个方法实现:
// call encoding function
cTAppEncTop.encode();
该函数的实现如下:

Void TAppEncTop::encode()
{
	fstream bitstreamFile(m_pchBitstreamFile, fstream::binary | fstream::out);
	if (!bitstreamFile)
	{
		fprintf(stderr, "\nfailed to open bitstream file `%s' for writing\n", m_pchBitstreamFile);
		exit(EXIT_FAILURE);
	}

	TComPicYuv*       pcPicYuvOrg = new TComPicYuv;
	TComPicYuv*       pcPicYuvRec = NULL;

	// initialize internal class & member variables
	xInitLibCfg();
	xCreateLib();
	xInitLib();

	// main encoder loop
	Int   iNumEncoded = 0;
	Bool  bEos = false;

	list<AccessUnit> outputAccessUnits; ///< list of access units to write out.  is populated by the encoding process

	// allocate original YUV buffer
	pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeight, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth );

	while ( !bEos )
	{
		// get buffers
		xGetBuffer(pcPicYuvRec);

		// read input YUV file
		m_cTVideoIOYuvInputFile.read( pcPicYuvOrg, m_aiPad );

		// increase number of received frames
		m_iFrameRcvd++;

		bEos = (m_iFrameRcvd == m_framesToBeEncoded);

		Bool flush = 0;
		// if end of file (which is only detected on a read failure) flush the encoder of any queued pictures
		if (m_cTVideoIOYuvInputFile.isEof())
		{
			flush = true;
			bEos = true;
			m_iFrameRcvd--;
			m_cTEncTop.setFramesToBeEncoded(m_iFrameRcvd);
		}

		// call encoding function for one frame
		m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, m_cListPicYuvRec, outputAccessUnits, iNumEncoded );

		// write bistream to file if necessary
		if ( iNumEncoded > 0 )
		{
			xWriteOutput(bitstreamFile, iNumEncoded, outputAccessUnits);
			outputAccessUnits.clear();
		}
	}

	m_cTEncTop.printSummary();

	// delete original YUV buffer
	pcPicYuvOrg->destroy();
	delete pcPicYuvOrg;
	pcPicYuvOrg = NULL;

	// delete used buffers in encoder class
	m_cTEncTop.deletePicBuffer();

	// delete buffers & classes
	xDeleteBuffer();
	xDestroyLib();

	printRateSummary();

	return;
}

该函数中首先调用pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeight, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth )分配YUV数据缓存,然后再while循环中逐帧读取YUV数据、设置当前以编码的帧数、编码当前帧、写出码流,随后做其他清理工作。核心功能实现在m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, m_cListPicYuvRec, outputAccessUnits, iNumEncoded )函数中。在该函数中调用m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, accessUnitsOut)进行编码一个GOP的操作。这个函数奇长无比,用了接近1500行代码,看来实现了很多很多很多的功能。这个碉堡了的函数究竟做了些啥事儿呢?这个函数中大部分内容就是在为了编码当前slice做准备,以及编码完成之后一些辅助操作。实际编码过程的操作由以下函数m_pcSliceEncoder->compressSlice( pcPic )实现。

这又是一个碉堡了的函数,占了将近400行……代码就不贴了,会死人的……简单看下好了。

首先还是各种编码的配置,包括配置熵编码器、初始化CU编码器等。在完成了一长串的设置之后,在compressCU函数中实现对一个CU的编码:

      m_pcCuEncoder->compressCU( pcCU );
具体的细节,且待下文。
目录
相关文章
|
8月前
|
存储 运维 安全
阿里云弹性裸金属服务器是什么?产品规格及适用场景介绍
阿里云服务器ECS包括众多产品,其中弹性裸金属服务器(ECS Bare Metal Server)是一种可弹性伸缩的高性能计算服务,计算性能与传统物理机无差别,具有安全物理隔离的特点。分钟级的交付周期将提供给您实时的业务响应能力,助力您的核心业务飞速成长。本文为大家详细介绍弹性裸金属服务器的特点、优势以及与云服务器的对比等内容。
759 23
|
6月前
|
存储 设计模式 Java
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
220 5
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
631 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
机器学习/深度学习 人工智能 搜索推荐
AI在市场营销技术中的崛起:转变数字营销策略
AI在市场营销技术中的崛起:转变数字营销策略
|
Python
python 股票数据分析、绘制K线图、价格走势图、收益率计算 完整代码+数据 可直接运行
python 股票数据分析、绘制K线图、价格走势图、收益率计算 完整代码+数据 可直接运行
407 0
python 股票数据分析、绘制K线图、价格走势图、收益率计算 完整代码+数据 可直接运行
|
编解码 IDE 测试技术
HEVC编码之HM学习
HM是HEVC的官方标准测试模型,想要对HEVC进行了解,需要对HM源码进行一定解读才能够更加深入。
858 0
|
自然语言处理 索引
技术写作最佳实践与策略指南
作为一名技术写作者,遵守既定的最佳实践有助于确保您的工作的一致性、清晰性和整体质量。一些常见的最佳实践包括: 始终考虑受众: 牢记用户视角编写内容。确保技术术语、语言和复杂程度与您的目标读者相匹配。 逻辑地组织内容: 将材料分为章节、子章节、项目符号列表和表格。使用标题帮助读者浏览内容。 必要时使用图表和图像: 视觉辅助工具通常可以提高对复杂概念或过程的理解。 写出清晰简洁的句子: 避免使用读者可能不明白的模糊信息和术语。始终追求可读性。 编辑、编辑、编辑: 校对您的工作,纠正语法和拼写错误,并确保信息准确且最新。 遵循这些最佳实践可以提高您的技术写作效率,并确保您的受众能够轻松理
1365 0
|
计算机视觉 Python
OpenCV中图像的平移、旋转、倾斜、透视的讲解与实战(附Python源码)
OpenCV中图像的平移、旋转、倾斜、透视的讲解与实战(附Python源码)
976 0
|
机器学习/深度学习 人工智能 自然语言处理
AIGC领航计划系列-快速入门指南-科普篇
“开启人工智能之旅,与AIGC领航计划共创未来”!科普篇:什么是AIGC?AIGC的行业现状?AIGC的应用场景?
1266 2