基于ADSP-BF561的H.264视频编码器的实现
H.264/AVC是ITU-T VCEG和ISO/IECMPEG联合制定的最新视频编码国际标准,是目前图像通信研究领域的热点技术之一。H.264的视频编码层(VCL)采用了许多新技术,因而使其编码性能有了大幅度提高。但这是以复杂度的成倍增加为代价的,这也使得H.264在实时视频编码及传输应用中面临着巨大的挑战。因此,要满足图像压缩的实时性要求,就需要对现有的H.264编解码器进行优化。本文主要讨论H.264系统的硬件平台和任务流程,并针对基于DSP硬件平台的特点,介绍了从代码级对算法进行优化,进一步提高编码算法的运算速度,实现H.264实时编码的具体方法。由于ADI Blackfin561是AD公司推出的一款高性能的数字信号处理器,它具有600MHz的主频。为此,本文选择其作为硬件平台,来探索在资源有限的DSP平台上实现H.264编码器的有效途径。
1 硬件平台
1.1 ADSP-BF561处理器
Blackfin561是Blackfin系列中的一款高性能定点DSP视频处理芯片。其主频最高可达750MHz,其内核包含2个16位乘法器MAC、2个40位累加器ALU、4个8位视频ALU,以及1个40位移位器。该芯片中的2套数据地址产生器(DAG)可为同时从存储器存取双操作数提供地址,每秒可处理1200M次乘加运算。芯片带有专用的视频信号处理指令以及100KB的片内L1存储器(16KB的指令Cache,16 KB的指令SRAM,64 KB的数据Cache/SRAM,4KB的临时数据SRAM)、128KB的片内L2存储器SRAM,同时具有动态电源管理功能。此外,Blackfin处理器还包括丰富的外设接口,包括EBIU接口(4个128MB SDRAM接口,4个1MB异步存储器接口)、3个定时/计数器、1个UART、1个SPI接口、2个同步串行接口和1路并行外设接口(支持ITU-656数据格式)等。Blackfin处理器在结构上充分体现了对媒体应用(特别是视频应用)算法的支持。
1.2 基于ADSP-BF561的视频编码器平台
Blackfin561视频编码器的硬件结构如图1所示。该硬件平台采用ADI公司的ADSP-BF561EZ-kit Lite评估板。此评估板包括1块ADSP-BF561处理器、32 MB SDRAM和4 MB Flash,板中的AD-V1836音频编解码器可外接4输入/6输出音频接口,而ADV7183视频解码器和ADV7171视频编码器则可外接3输入/3输出视频接口此外,该评估板还包括1个UART接口、1个USB调试接口和1个JTAG调试接口。在图1中,摄像头输入的模拟视频信号经视频芯片ADV7183A转化为数字信号,此信号从Blackfin561的PPI1(并行外部接口)进入Blackfin561芯片进行压缩,压缩后的码流则经ADV7179转换后从ADSP-BF561的PPI2口输出。此系统可通过Flash加载程序,并支持串口及网络传输。编码过程中的原始图像、参考帧等数据可存储在SDRAM中。
2 H.264视频压缩编码算法的主要特点
视频编解码标准主要包括两个系列:一个是MPEG系列,一个是H.26X系列。其中MPEG系列标准由ISO/IEC组织(国际标准化组织)制定,H.26X系列标准由ITU-T(国际电信联盟)制定。I-TU-T标准包括H.261、H.262、H.263、H.264等,主要用于实时视频通信,如电视会议等。
H.264视频压缩算法采用与H.263和MPEG-4类似的、基于块的混和编码方法,它采用帧内编码(Intra)和帧间编码(Inter)两种编码模式。与以往的编码标准相比,为了提高编码效率、压缩比和图像质量,H.264采用了以下全新的编码技术:
(1) H.264按功能将视频编码系统分为视频编码层(VCL,Video Coding Layer)和网络抽象层(NAL,Network AbstracTIon Layer)两个层次。其中VCL用于完成对视频序列的高效压缩,NAL则用于规范视频数据的格式,主要提供头部信息以适合各种媒体的传输和存储。
(2)先进的帧内预测,它对含有较多空域细节信息的宏块采用4×4预测,而对于较平坦的区域则采用16×16的预测模式,前者有9种预测方法,后者有4种预测方法。
(3)帧间预测采用更多的块划分种类,标准中定义了7种不同尺寸和形状的宏块分割(16×16、16×8、8×16)和子宏块分割(8×8、8×4、4×8、4×4)。由于采用更小的块和自适应编码方式,故可使得预测残差的数据量减少,从而进一步降低了码率。
(4)可进行高精度的、基于1/4像素精度的运动预测。
(5)可进行多参考帧预测。在帧间编码时,最多可选5个不同的参考帧。
(6)整数变换(DCT/IDCT)。对残差图像的4×4整数变换技术,采用定点运算来代替以往DCT变换中的浮点运算。以降低编码时间,同时也更适合到硬件平台的移植。
(7)H.264/AVC支持两种熵编码方法,即CAVLC(基于上下文的自适应可变长编码)和CABAC(基于上下文的自适应算术编码)。其中CAVLC的抗差错能力比较高,但编码效率比CABAC低;而CABAC的编码效率高,但需要的计算量和存储容量更大。
(8)采用新的环路滤波技术及熵编码技术等。
H.264的这些新技术使运动图像压缩技术向前迈进了一大步,它具有优于MPEG-4和H.263的压缩性能,可应用于因特网、数字视频、DVD及电视广播等高性能视频压缩领域。
3 H.264视频编码算法的实现
将H.264在DSP进行改进要经过以下3个步骤:PC机上的C算法优化、从PC机到DSP的程序移植、在DSP平台上的代码优化。
3.1 PC机上的C算法优化
根据系统要求,本设计选择了ITU的Jm8.5版本baseline profile作为标准算法软件。ITU的参考软件JM是基于PC机设计的,故可取得较高的编码效果。将视频编解码软件移植到DSP时,应考虑到DSP系统资源,主要应考虑的因素是系统空间(包括程序空间和数据空间),所以,需要对原始的C代码进行评估,这就需要对所移植的代码有所了解。图2所示是H.264的算法结构。
了解了算法结构以后,还需要确定在编码算法的实现过程中,运算量较大且耗时较长的部分。VC6自带的profile分析工具显示:帧内与帧间编码部分占用了整体运行时间的60%以上。其中ME(Move EsTImaTIon,运动估计)又占用了其中较多的时间。所以,移植与优化的重点应在运动估计部分,因此,应当对代码结构进行调整。
(1)大幅删减不必要的文件和函数
由于选用了baseline和单一参考帧,因此,很多文件和函数都可以删减,包括有关B帧、SI片、SP片和数据分割、分层编码、权值预测模式、CABAC编码模式等不支持特性的冗余程序代码,同时包括rtp.c、sei.c、leaky_bucket.c、In-trafresh.c文件、相关的头文件以及在global.h头文件中相应定义的全局变量和函数,此外,还可以删除top_pic、bottom_pic等与场有关的全局变量与局部变量、分层编码、多slice分割以及FMO、与场编码/帧场自适应编码/宏块自适应编码有关的预测、参考帧排序、输入输出以及解码器缓存操作等;也可以删除随机帧内宏块刷新模式和权值预测模式等相关的冗余代码(如使编码器采用NAL码流而非RTP格式),同时删除rtp.c;sei.c中包含一些辅助编码信息(并不编入码流中),如果不用,也可以删除leaky_bucket.c用于计算泄漏缓存器的参数。
(2)配置函数的改写
由于JM的系统参数配置是通过读取encoder.cfg文件来实现的,故可将参数配置由读取文件改为通过初始化集中赋值函数来实现,这样既减少了代码量,又减少了对有限内存空间的占用和读取时间,提高了编码器整体的编码速度。例如:定义为int型的变量input->img_height就可直接改写为input->img_height=288(CIF格式)。
(3)去除冗余的打印信息
为了调试与算法改进的方便,JM保留了大量的打印信息。为了提高编码速度,减少存储空间消耗,这些信息完全可以删掉,如大量的trace信息和编码数据统计文件。如果lor.dat和stat.dat仅需在PC机上调试时使用,也没必要移植到DSP平台上,跟这部分相关的代码完全可以去除。但是,调试时所需的基本信息(如码率、信噪比、编码序列等)则应保留参考。
通过调整可使得代码的结构、容量更加精简,从而为接下来在DSP上的移植做好准备。
3.2从PC机到DSP的程序移植
要将PC端精简的程序移植到ADSP-BF561的开发环境Visual DSP下,以使其能够初步运行,所需考虑的主要是语法规则和内存分配等问题。
(1)除去所有编译环境不支持的函数
主要是除去某些与时间相关的函数、将文件操作修改为读取文件数据缓存的操作、删除SNR信息收集等DSP平台实现不需要的代码。还要注意:函数的声明、数据结构的类型要符合DSP的C语言格式。
(2)添加与硬件相关的代码
该代码包括系统初始化、输出模块代码、中断服务程序和码速率控制程序等。
(3)配置LDF文件
因为刚移植的代码往往数据和程序都非常大,SRAM里面肯定是放不下的,这时链接就会有问题。刚开始的时候,最好把所有的程序和数据都放在SDRAM里,这样链接就不会有问题了。Stack和heap情况类似,都先放到SDRAM。一般在开始时,往往需要的是一个可以正确运行的程序,而速度倒在其次。
(4) Malloc问题的解决
DSP下的开发,malloc是一个需要解决的问题。如果动态申请内存,就算可以运行,其结果往往也是不对的。所以,最好进行静态分配,可用数组的形式分配。
移植完毕后,即可实现基于ADSP-BF561处理器的H_264编码,此时如果速度达不到实时编码的要求,还可以进一步进行优化。
4 DSP平台上的代码优化
在Visual DSP开发环境下对代码进行优化的主要方法有C语言级优化和汇编级优化。
4.1 C语言级优化
通过VC6的profile分析工具发现:移植与优化的重点应在运动估计部分。笔者通过比较各种算法后选择了菱形(DS)搜索法。DS算法可采用两种搜索模板,分别是有9个检索点的大模板LD-SP(Large Diamond Search Pattern)和有5个检索点的小模板SDSP(Small Diamond Search Pattern)。其菱形搜索示意图如图3所示。搜索时,先用大模板计算,当最小块误差SAD点出现在中心点处时,再将大模板LDSP换为SDSP进行匹配运算,这时,5个点中具有最小SAD者若为中心点,则该点即为最优匹配点,然后结束搜索,否则将继续以此点为搜索中心进行SPSS搜索。
经JM实验证实,采用此种方法,可以节约大约10%的运行时间,且代码量无太大增长。
针对DSP的特点和相关的硬件指令,设计时可对代码进行如下优化:
◇对程序结构进行调整。对不适合DSP执行的语句进行改写,以提高代码的并行性。
◇宏的使用。也就是将有些较短,执行单一、调用次数多的函数改为宏。
◇循环优化是将C语言中的for循环打开,排流水线,提高并行性。
◇计算表格化是将运行时计算的参数做成便于查找的表格常数数值,从而将运行计算转化为编译运算。如在量化和反量化程序中进行移位位数的处理时,可先计算出所有可能的值,而后来的运算就可以通过查表得到数值。
◇浮点数定点化。因为Blackfin561并不支持浮点运算,但原始程序代码却是浮点运算的格式,所以必须改成定点运算,而其修改后的执行速度也会加快很多。
◇尽量用逻辑运算代替乘除运算。由于乘除运算指令的执行时间要远远大于逻辑移位指令,尤其是除法指令,故应尽量用逻辑移位运算来代替乘除运算,以加快指令的运行速度。
◇尽量少进行函数调用。对一些小的函数,最好是用适当的内联函数将其直接写入主函数中进行替代,而对于一些调用不多的函数,也可以直接写入主函数内,这样可减少不必要的操作以提高速度。
◇减少判断转换。
◇尽量静态分配内存。
◇调用系统提供的丰富的内联函数。
此外,为了充分发挥DSP的运算能力,还必须从它的硬件结构出发,最大限度地利用它的8个功能单元,使用软件流水线尽量让程序无冲突地并行执行。也可将最耗时的函数抽取出来,用线性汇编改写,从而最大限度的利用DSP的并行性。
4.2汇编级优化
汇编级优化主要指如下几点操作:
(1)使用寄存器资源
Blackfin561提供了8个32位数据寄存器以及一系列的地址寄存器。使用寄存器代替局部变量时,若局部变量用来保存中间结果,那么用寄存器代替局部变量可省掉很多访问内存的时间。
(2)使用专用指令
Blackfin561提供有求最大值、最小值、绝对值、CUP及大量视频专用指令,应可能用多位的指令来访问少位的数据。通过使用这些指令能大大提高代码的执行速度。如用int型(32位)访问2个short(16位)型数据时,可将其分别放在32位寄存器的高16位和低16位字段。这样,数据读取效率可以提高1倍,从而减少内存访问次数。
(3)使用并行指令和向量指令
ADSP-BF561中每条通用指令都可以和一条或两条存储器访问指令并列执行,这样有利于ADSP-BF561的流水线满负荷运行,更充分发挥ADSP-BF561的数据处理能力。
(4)合理存放反复调用的程序段
把被反复调用的程序段(如DCT变换和IDCT变换)放在片内程序存储区中,把频繁用到的数据段(如编码表)放在片内数据存储器中,而把不常用到的程序和数据段放在片外存储器中,以避免对程序或数据进行不必要的反复搬移。
(5)合理使用内外存储器
BF561片内只有256KB的存储空间,因此当前帧、参考帧和当前帧的重建帧都必须放至片外存储器,压缩码流若被主机读取,也可放至片外。其它数据如程序代码、全局变量、VLC码表、各编码模块产生的中间数据等均可放至片内。
(6)DMA的使用
由于CPU访问片外存储器的速度通常要比访问片内慢几十倍,片外数据的传输通常成为程序运行时的瓶颈,这样,即使代码效率很高,流水线也会因为等待数据而被严重阻塞。解决这一问题的有效方法是用DMA传送数据。程序是逐个宏块进行编码的,在编码当前宏块的同时,先由DMA将下一个宏块的数据、用到的参考帧数据由片外传送至片内,当前宏块做完运动补偿后,DMA又将重建后的宏块由片内传送至片外。这样CPU只对片内数据进行操作,从而使流水线可以顺利进行,而压缩码流按逐个码字有时间间隔地写入,可由CPU直接写至片外。
5 结束语
经过用ADSP-BF561汇编语言改写的对应函数的优化程序经调试运行后,DCT,IDCT部分效率提高了大约15倍,去块滤波部分效率提高了大约6~7倍。对于模块中的其它部分函数,也同样取得了良好的优化结果。说明其优化工作确实达到了良好的效果。