摘要: 本论文详细讨论和分析了在ARM11上实现分辨率为D1(720×480)解码播放的优化和部分经验。本文实现的是在linux平台下采用开源mplayer代码针对ARM11内核进行优化。本论文先后提出了基于ARM11的4个优化方法,首先介绍了采用ARM11所具有的SIMD指令优化方法;然后讨论了cache的特性,采用重排序汇编代码的方法来提高cache的性能;以及在IDCT算法中加多4×4块变化的方法;最后在输出显示进行了优化.本文结论是:在优化后的代码成功的在532MHz ARM11内核的MX31嵌入式平台上实现分辨率为720×480 28fps和720×576 23fps的解码播放。 关键词:ARM11,MPEG-2,视频解码,优化
- 前言:
目前市场上不断更新的手持设备如手机,PMP等都可以播放各种视频,但是其分辩率大都是CIF (352×288)和QVGA(320×240)或者更低,这是因为视频解码的复杂度和数据量大而手持设备必须考虑其功耗等因素,因此目前采用的处理器的处理能力对于高分辨率的视频解码播放来说还不够快。视频解码器的设计方案可分为两种:硬解码和软解码。软解码和硬解码相比有自己独特的优势:成本和功耗低,移植和更新灵活、方便。但是对于处理器的处理速度和软件优化的程度要求都比较高,否则难以达到高分辨率的实时解码播放。再由于目前比较流行的一款嵌入式处理器内核是ARM公司推出的ARM系列,本文将在此基础上讨论和分析在ARM11平台上实现分辨率为D1(720×480)的软解码实时播放优化。 过去,前人总结了不少高分辨率的视频软解码的优化方法,但大都是针对PC的处理器,对于嵌入式处理器平台的优化比较少。本文作者在前人的PC处理器平台的优化经验基础上总结出了对于嵌入式处理器(ARM11平台)上实现D1的实时解码播放的优化方法和经验。 本论文下面的结构安排是:2)MPEG-2概述;3)优化平台介绍;4)优化难点分析;5)具体优化方法和测试结果分析;6)总结。 2.MPEG-2概述: MPEG视频标准是最常用的一种视频压缩标准,例如用在了数字电视,DVD。它主要是根据视频图像之间的空间冗余,时间冗余和数据冗余来进行压缩的。相应地出现了运动估计(ME),离散余弦变换(DCT)和可变长编码(VLC)。 下图1是一个典型帧序列,箭头说明了各帧之间的参考关系。 I帧与前面的帧之间相关性最小,例如如果相邻之间的图像发生了较大改动,即之间的相关性较小那么后一帧一般用I帧来编码。其次是P帧和B帧,即帧间距离越近其相关性越大,画面发生的变动也最小,那么冗余信息越多,压缩量就越大。 视频的第一帧是I帧,它包含了此帧图像的完整信息(在MPEG-2里面压缩方法是把视频数据分为块(8×8个象素点)进行DCT变换后压缩的),随后是B帧,它是由前面的I帧进行前向预测或者由后面的P帧进行后向预测来压缩的,压缩后只剩下它与参考帧之间的运动矢量信息和与参考帧之间的残差信息,其中残差数据也是需DCT变换。其后是P帧,前面的I帧或者P帧是它的参考帧,和B帧一样包含了它与参考帧之间的运动矢量信息和残差信息,残差数据也需进行DCT变换,所有这些数据最后再进行可变长编码后得到压缩后的数据。 我们还可以看出只有I,P帧可作为参考帧,而B帧是不作任何参考来用的。图1中为显示顺序而编码后的顺序则是[1 4 2 3 8 5 6 7] 。 图1 3.优化平台 实验平台是由深圳爱国者提供的i.MX31 SDK实验平台,测试码流存放在外接硬盘中,内存Mobile DDRAM ,CPU的内核为ARM11,具有IPU(Image Process Unit)它可以把软件解码后的YUV数据进行RGB转换后在LCD上面显示出来,不占用CPU时间,以往用软件做这些消耗了大量的CPU时间。 优化采用的代码是mplayer,它是目前网络上公认的比较好的一款播放器。而在mplayer源代码里面有两个可以播放MPEG-2视频的代码库,分别是ffmpeg2和libmpeg2,这里选择的是libmpeg2里面的c代码作为我们的优化基础。选用的嵌入式操作系统是linux-2.6.10。 测试码流选择了2个720×480和2个720×576分辨率的码流,level和profile都是main,4:2:0,如表1所示。 表1 测试码流
码流 |
帧数 |
大小 |
分辨率 |
koreaMTV.DVD |
?6192? |
131MB |
DVD(720×480) |
vts_02_1.vob |
?35705 |
637MB |
DVD(720×480) |
grounpe.ts |
?1235? |
30.4MB |
TS(720×576) |
jump.ts |
?1224? |
31.4MB |
TS(720×576) |
4.优化难点分析 VLD(variable length decoding):视频流从硬盘中读入到内存后首先进行可变长解码,因为读进来的数据都是经过压缩后的,因此数据量小了很多,cache有足够的空间将其存入(例如码率为6Mbit/s,可计算其每帧数据为25KB,而L2 cache有128KB)。 IDCT(inverse discrete cosine transform):对于一个8×8子块进行IDCT变换,在整个解码过程中计算量最大,频繁地被调用,但优化空间往往也比较大,可以考虑用ARM11的DSP功能指令对其优化。 MC(motion compensation):根据运动矢量MV从参考帧中拷贝对应宏块数据到当前解码帧中,并且对于不同模式分别对宏块中的相邻两个或者4个象素点进行平均处理。由于参考宏块之间在参考帧中的位置随机,而一个参考帧的数据很大不可能都存在cache中,因此所要的参考宏块一般都不在cache中,CPU就必须从外面的主DDRAM中读入,所以MC在整个解码过程中耗时比较久。 DISP(display):解码后的YUV数据需转换为RGB数据后再输出给LCD显示,但幸运的是很多CPU都提供硬件支持,解码后的YUV数据直接拷贝给LCD映像在DDRAM里面的4个buffer里面,它会自动完成剩下的RGB转换和显示任务,这样就不需要占用宝贵的CPU时间,因此DISP耗时主要存在于解码后对数据的拷贝时间。 为了更清楚认识各部分的耗时比例,这里讨论了一个例子是用优化前的mplayer代码对一个测试码流进行解码播放,如下图2所示。因为每个码流的编码对象不一样,数据也不一样因此每部分占用的时间也不尽相同,但一般情况下相差也不会太大。 图2 优化前的各部分耗时比例 5.具体优化方法和测试结果分析 虽然ARM11的主频已高达532MHz(在MX31上),但由于像DVD(720×480)这样的视频解码其处理数据量较大,若采用mplayer播放器开源的C代码直接在其上面播放也只能达到19fps(不包括音频播放)。优化中其关键是如何减少对内存的访存和如何有效地进行并行处理。 5.1 使用ARM11的SIMD指令。 方法之一是采用ARM11处理器中的DSP指令,包括:并行加减,访存,饱和处理等(参考ARMv6指令集)。ARM11是32位宽处理器,所以可以把4个8位或者2个16位打包进行并行的运算、读写,这样做可以提高处理效率。其中一个方法是人工地用汇编语言来编写,这里根据ARMv6j的DSP指令把C代码转化为汇编代码后,从指令的条数上我们可以明显的看出比用专门针对ARMv6j的ARM gcc 编译出来的汇编少了很多。(这里采用的ARM GCC compiler 3.4.3 for ARM linux 是针对最新的ARMv6j,优化级别是o2?,尽管采用的ARM GCC 编译器编译时对代码作了很多优化,但在某些情况下始终做不到人为的优化好)。 下面介绍一个饱和指令的例子。解码过程中一般都要对最后的图像数据限制在0~255之间,在C代码里往往有2种方式做这一步,分别是图(a)用判断的方法;图(b)用读表方法。 表2饱和数据的方式
(a)判断 |
#define CLIP(i)? (i < 0) ? 0 : ((i > 255) ? 255 : i) a = CLIP(a) ; // 把a限制在0~255之间 |
(b)读表 |
#define CLIP(i)? ((mpeg2_clip + 3840)[i]) //指定CLIP(i)为一数组 for (i = -3840; i < 3840 + 256; i++) CLIP(i) = (i < 0) ? 0 : ((i > 255) ? 255 : i); //初始化数组 a = CLIP(a) ; // 把a限制在0~255之间 |
(c)用指令 |
asm("? usat? %0 ,#8 , %0? ":"=r"(a):"r"(a):"cc"); //把a限制在0~255之间 |
表2(a)中用判断的方法不利用流水线的流水工作,因为判断发生跳转到未预知的地址,需要刷新流水线。表2(b)用读表的方法不影响流水线,只需读一次表即可,但其耗时也不能忽视,因为这样做对cache的效率不高。这时候就可以发挥ARM的饱和指令的优势,只需要用(usat? r0,#8,r0) 就可把r0饱和限制在0~255之间如表2 (c)。 5.2 提高内存访存性能 较以前的处理器现在处理器的频率得到了很大的提高,但DDRAM的读写速度却没有得到这么高比例的提高,ARM11的主频是532MHz,而DDRAM的存取时间为×纳秒,占用了约×个时钟周期,所以对于要求处理速度非常快的像视频解码的瓶颈之一在于对内存的访存的等待时间上。为此我们要想尽各种方法去减少对内存访存。 尽量使用寄存器和SIMD。为了减小对内存的读和写次数,可以在一次性变换中,例如在一行变换中的DCT系数占用了16位宽数据再由于数组中的数据是连续分配的故可以一次读取32位宽来同时读取2个系数,通过移位(因为采用的是桶行移位器所以不占用CPU资源)分别提取出来放在8个寄存器中。对于ARM11在一般应用程序中用户可以任意使用r0~r12和LR中的14个寄存器,所以我们对于c代码进行汇编代码优化的时候,可以做到只对内存进行最少访存次数,即在一行变换的时候只需对内存一次读和一次写,同时在读和写的时候通过把两个16位打包后处理提高了并行处理能力,这称为单发射多数据处理(single-instruction, multiple data, SIMD). 避免数据互锁。从ARM9开始,ARM使用了装载和乘法的多级执行流水线,这项技术带来了处理器的互锁,如果装载一个数据,接着在后面指令中立即使用这个数据,那么处理器可能停下来几个周期,以等待被装载的数据,其等待的周期依赖于对内存的访问速度,假设对内存的访问是执行指令周期的一倍的话那么实际上使得cpu的速度降低了一倍即cpu的主频是532MHz那么实际cpu只工作在266MHz,因为另一半的时间都耗在等待上了。而编译后的装载指令在代码中出现的频率很高,大约占所有指令的1/3左右,可以通过适当地安排好load和store指令的顺序来解决互锁,但如果数据不在cache里面,这样做还不够还需数据预取,数据预取在后面有介绍。因此优化好DDRAM的读写,对于提高整个解码速度有关键作用,这正是为什么要尽量减小对内存的访存次数的原因,也正因为如此高级一点cpu在设计的时候都在cpu和内存之间增加了cache,为的是提高访存效率。 Table 3 arrangement of the assembly code
(a) By compiler |
(b) Rearranged |
… LDR r1, [r0] SUB r3, r3, #1 ADD r2, r2, r1 SUB r4, r4, r3 STR r4, [sp] … |
… LDR r1, [r0] SUB r3, r3, #1 SUB r4, r4, r3 STR r4, [sp] ADD r2, r2, r1 … |
Cache是连接CPU和外部主存的高速缓存器,一般CPU的cache L1大小有4K,8K,16K,32KB。cache的访存速度介于寄存器和外部内存之间,同时分为I-cache(指令cache)和D-cache(数据cache)。这里i.MX31是32KB,分为16KB 的I-cache和D-cache,L2稍大一点128KB,但其访问数度不如L1的快 。如果CPU访问的数据在cache中则命中,否则要去访问外部内存数据,这时会把所访问内存连续的数据一起放入cache中直到填满cache中的一行为止,因此CPU 对于频繁调用的数据会自动存在于cache中,这样减小了对外部内存的访存时间从而提高了访存的速度。 根据cache的这样些结构特点,我们可以总结出优化的原则:保持连续访问数据的联系性,数据量尽量不要超过cache的大小,成组的进行读写操作,尽量多使用cache里面的数据,尽量使用寄存器代替对外部DDRAM的访存,预取数据到cache中去。 在文献[3]中提到了在MPEG-2里面先用预取指令预取参考块的数据到cache中,再进行IDCT变换后参考块数据已经在cache中了,而此时做MC自然就很快。但Pentium和ARM的结果不一样,我们尝试这样在ARM里面做是没有意义的。 在汇编指令集中只有一个预取指令PLD,我们用[3]中同样的方法先预取参考块再做IDCT后再做MC,测试结果表明,在MC中读取参考块数据的却快了很多,说明数据已经在cache中了,然而预取过程所占用的时间却很长,使得整个解码过程在时间上没有一点减小,反而多出了点时间。 5.3 当所有非零系数都集中在左上角的4×4块区域时,可以分裂出来作为4×4的块运算。 因为DCT变换后大部分的非零系数都主要集中在左上角,所以在IDCT运算中除了作对每一行的8个系数进行后7个非零的判断以及对于整个block块交流系数(AC)都为零的判断以外(在大部分公开的源代码里面都做了这一步),这里也判断了当所有非零系数都集中在左上角的4×4块区域中时我们可以把整个8×8变换缩小为4×4的变换如图1所示,从而减小了不必要的对零系数的访存和运算时间。为了验证其效果和其必要性同时对几个DVD的码流做了如下测试。实验结果表明这样做可以提高1fps左右。时间上减少了×××
steams |
8×8 |
4×4 |
koreaMTV.DVD |
25.14fps |
26.49 fps |
vts_02.vob |
25.94 fps |
26.55 fps |
grounps.ts |
20.61 fps |
20.90 fps |
jump.ts |
19.87 fps |
20.37 fps |
5.4输出显示优化 如下图4中,数据流经过VLD,IQ,IDCT和MC后解码出来的YUV数据存放在current_buffer(前帧缓存)中。若当前帧是I,P帧,则把refference_buffer[1](参考帧1)中的参考帧拷贝给LCD_buffer(LCD显示缓存)去显示,而后把refference_buffer[2](参考帧2)指向refference_buffer[1],refference_buffer[1]指向current_buffer;若当前是B帧则直接把current_buffer拷贝到LCD_buffer去显示。 图4 MPEG-2原始解码和显示结构图 Figure 4 Original MPEG-2 Video-decoding Algorithm 由上图4:整个解码和显示结构框图可以看出,解码后的YUV数据分别存放在refference_buffer和current_buffer里面,若要让LCD显示出图像来还需拷贝到4个LCD_buffer中的任何一个,这4个LCD_buffer是IPU映射到DDRAM中的,如前面所述,它完成YUV到RGB的转换和显示任务而无需占用CPU的时间。 既然LCD_buffer和refference_buffer以及current_buffer都是在DDRAM中分配的而且可以灵活地控制4个LCD_buffe中的任一个作为当前的显示buffer.,那么我们可以把LCD_buffer即作为解码buffer同时又作为显示的buffer而无需refference_buffer和current_buffer,这样做不仅减少了占用的内存而且关键是减少了大批数据的拷贝过程,如图5所示改进后的解码和显示结构框图。当然前提是必须确保CPU对LCD_buffer和refference_buffer的访存同样快,若LCD_buffer的访存较慢,那么这样做的结果是反而影响了整个解码过程使得整个解码播放的速度大幅度下降。 图5 MPEG-2改进后的解码和显示结构图 Figure 5 New MPEG-2 Video-decoding Algorithm 表5 各部分优化情况
Stream |
V0(Original) |
V1(DISP) |
V2(IDCT) |
V3(MC) |
V4(Drop 5 frames) |
koreaMTV.DVD |
19.44fps |
22.42 fps |
26.49 fps |
29.04 fps |
34.12 fps |
vts_02.vob |
20.55 fps |
24.32 fps |
26.55 fps |
29.43 fps |
34.67 fps |
grounps.ts |
15.56 fps |
18.94 fps |
20.90 fps |
23.46 fps |
26.78 fps |
jump.ts |
15.13 fps |
18.88 fps |
20.37 fps |
22.93 fps |
26.21 fps |
为了验证以上所提出的方法的效果,这里用优化后的解码器对几个测试码流进行了解码测试。V1 对应输出显示后的结果,而V2 和V3 体现了其余方法优化后所带来的结果。 V1 改进后的显示结构因为少了对每帧数据的拷贝过程使得解码速度上有了明显的提高,可使解码速度从19fps 提高到22.4fps,综合对以上码流的测试表明有了近3帧每秒的提高。即在每解码一帧的时间上减少了10ms。 V2 是针对IDCT函数优化的结果,采用到的方法主要包括4×4变化,SIMD指令。 在5.3节中我们已经看到了4×4变换块带来了0.5帧每秒左右的提高,再加上5节中所提到的SIMD技术可使得在V1的基础上有2帧每秒左右的提高。 V3 是针对MC函数的优化结果,采用到的方法主要是SIMD指令和重排序汇编指令。 由表5中数据可知在V2的基础上有了近3帧每秒的提高。 综合上面的优化方法,到目前为止最终可使DVD码流的解码播放达到29fps接近30fps的实时解码播放。对MPEG-2声音部分也做了同样方法的优化,加上优化了的声音解码播放后速度上只降了不到2帧每秒,对AC3音频播放还不支持。因此含声音一起播放时候只有25fps,需丢掉5帧后可以正常同步播放,而不会有太大影响。 对于720×480分辨率的要求正常播放速度是30fps,而720×576分辨率的要求是25fps,表4中V4显示结果是在没有帧速限制的情况下统计的所以都大于正常速度,正常播放的时候播放器会根据码流中的信息自动调节帧速。 总结: 本论文总结了分辨率为D1(720×480)的MPEG-2视频解码在嵌入式系统中的优化方法和经验。整个优化过程包括SIMD优化,重排序汇编代码从而有效利用cache,在IDCT中采用新的4×4判断和输出显示buffer的优化。测试结果表明在公开源C代码基础上优化后可使D1的视频在内核为ARM11的i.MX31处理器平台上面从19fps提高到28fps接近30fps的实时解码。我们相信在此基础上还有优化空间。 感谢: 参考文献: [1]? ISO/IEC 13818: 'Generic coding of moving pictures and associated audio (MPEG-2)' [2]? mplayer播放器源代码下载网址http://www.mplayerhq.hu/? [3]? Han Chen. Kai Li. Bin Wei .MEMORY PERFORMANCE OPTIMIZATIONSFOR REAL-TIME FTWARE HDTV DECODING. IEEE International Conference on Multimedia and Expo (ICME2002), August 2002 [4]? T.F. Chen and J.L. Baer. A Performance Study of Software and Hardware Data Prefetching Schemes. Proc. of the 21st Annual Intl. Symposium on Computer Architecture, pp223-232, 1994. [5]? ARM嵌入式系统开发:软件设计与优化/(美)Andrew N. Sloss, (英)Dominic Symes, (美)Chris Wright着;沈建华译. 北京:北京航空航天大学出版社,2005 // 应改为英文版本 [6]? MCIMX31 and MCIMX31L Multimedia Applications Processors Reference Manual . 2006 [7]? John L Hennessy and David A Patterson, Computer Architecture: A Quantitative Approach, Morgan Kaufman, CA, 1996. (秩名) |