HEVC学习与研究】11.HEVC参考解码器的设置及参数解析过程

之前已经讨论过如何运行HM的encoder工程,已经可以成功将一个yuv测试序列编码为一个.bin格式的二进制码流。这里我们再看看它的逆过程,即将二进制码流进行解码。
在整个HM的solution中很明显可以看到解码器的工程TAppDecoder,将这个工程设置为启动工程(在工程目录上右单击->set as startup project),设置工程属性,主要是命令行参数(右键—>Propertiea->configuration properties->debuging),如下图。还有就是别忘了把str.bin这个码流文件拷到指定的工作目录中去。
完成以后直接编译运行就OK了,解码完成后的输出文件默认为dec.yuv。命令行输出信息如图。
HEVC学习与研究】11.HEVC参考解码器的设置及参数解析过程_第1张图片 HEVC学习与研究】11.HEVC参考解码器的设置及参数解析过程_第2张图片
解码器的入口点函数在TAppDecoder中decmain.cpp这个文件里。由main函数进入后,新建一个顶层解码器对象cTAppDecTop之后并输出一些参考解码器版本等等信息之后,cTAppDecTop调用parseCfg()函数来解析命令行参数(在这里也就是上一步设置的“-b str.bin -o dec.yuv”这一行)。

一开始,这个参数解析函数的内容貌似有些奇怪,后来仔细一看才发现,就如同HM整个solution一样,里面应用了大量的C++面向对象的内容。由于之前一直是主攻C而且研究的也是用C写的JM/RM等代码,刚接触HM的时候感觉还是非常不习惯。单就这个函数来讲,里面用到了多个C++相对于C的扩展内容,主要有命名空间、模板类、泛型程序与标准模板库、流等。

命名空间:
命名空间namespace是标准C++引入的关键字,用于控制标识符(包括 符号常量、变量、宏、函数、结构、枚举、类和对象等)的作用域,将这些标识符进行本地化,防止命名冲突。如果没有命名空间,则所有标识符都会处于全局空间中,在大型项目的程序开发中很容易出现命名冲突,包括标识符都处于全局空间以及程序使用两个或者多个第三方库的情况等等。
比如若定义一个命名空间:
namespace myNameSpace
{
int nValue1;
int nValue2;
void GetValues(int &_nValue1, int &_nValue2)
{
_nValue1 = nValue1;
-nValue2 = nValue2;
}
Class NSClass
{
private:
int nValueInNSClass;
public:
int ShowNSClassValue()
{
cout<<nValueInNSClass<<endl;
}
}
}
可以看出,在命名空间内部,标识符可以直接使用,如nValue1,nValueInNSClass等。但是若在namespace定义范围之外使用其成员,则必须使用作用域限定符::。但是如果已经定义了该namespace类型的对象,则利用对象使用成员就不需要作用域限定符了。比如
int _tmain()
{
int a = 1, b = 2, c, d;
myNameSpace::nValue1 = a;
myNameSpace::nValue2 = b;
myNameSpace mNS;
mNS.GetValues(c,d);
}
关于命名空间进一步的讨论可以参考下面的文章:
http://blog.chinaunix.net/uid-26874138-id-3215266.html
http://blog.csdn.net/touzani/article/details/1637776

模板函数和模板类
模板函数是C++中定义的一种函数形式,这种函数不仅仅处理某一种形式的参数,而是可以兼容某一类型的多种参数。通过模板函数,可以实现参数化多态性,即使用同一段程序用于处理多种不同类型对象。与此类似的是类模板是声明的类的一种形式,该类中的数据成员、成员函数的参数和返回值可以处理多种类型数据,是一种比类更高层次的抽象,也成为参数化类。模板类的成员函数必须是模板函数。

泛型程序设计
泛型程序设计的目的,是用标准容器和标准算法,以最通用、最有效、最灵活地实现程序的设计,同时不损失效率。泛型程序设计的一个良好典范是C++所提供的标准模板库STL。因为C++模板为泛型程序设计奠定了基础,是所有版本STL的基石。

I/O流
IO流是C++使用的面向对象的输入输出软件包,是C语言的输入输出函数的一种替代产品。通过“流”这一抽象概念,将数据在产生者同使用者(包括磁盘文件和IO设备等)之间进行联系并管理其流动。

关于更详细的解释,相信大家都有各种C++的教材吧,随便一本应该都能讲清楚,自己去找吧,呵呵。

在TAppdecoder中使用了TAppCommon中定义的df,以及在df内部定义的命名空间program_options_lite。这两个命名空间中定义的多个类、结构和函数用于处理decoder的参数配置信息。TAppDecCfg::parseCfg()这个函数的实现中,先定义了一个df::program_options_lite类型的命名空间po,并且在po内定义了一个Options对象opts。随后opts调用addOptions()函数,后面跟着一串由括号引起的四元cfg结构。这里这么写的原因是因为,opts.addOptinos()所返回的是一个指向OptionSpecific类型的指针,并且OptionSpecific重载了“()”这个运算符,使其也返回一个指向OptionSpecific类型的指针。这样通过一级一级的括号调用,每次都会调用OptionSpecific的构造函数,再调用其Options成员parent_的addOption函数解析并添加括号中的四元cfg结构。完成后,这些数据都存储在opts中。
下一个函数po::setDefaults(opts)的目的仅仅是将指定的设置参数数值同制定参数进行赋值而已。后面的几个函数会进行无效参数扫描等操作,大部分这里根本没有执行到,因此暂时忽略之。

你可能感兴趣的:(编码,HEVC)