文献标识码:A
文章编号: 0258-7998(2011)12-0140-04
自由立体显示技术是指不需佩戴诸如立体眼镜等附属设备的三维立体显示技术[1],又称“裸眼式3D技术”,观察者无需借助立体眼镜即可裸眼体验立体感觉。自由立体视频技术的诸多优点决定其必然成为今后立体显示的发展趋势。目前国内外尚无商用、成熟、专用于自由立体显示技术的播放器软件,所以开发专用于自由立体视频播放显示的技术方案显得尤为重要。根据多视点自由立体显示技术的特点,本文提出了一种基于光栅显示技术的自由立体视频播放方案。
该方案基于MFC框架,播放器功能完全采用底层的WIN32API实现,具有结构清晰、设计简洁、可扩展性强的特点。与普通视频相比,立体视频具有多个视频通道,数据量较大。本文所涉及的自由立体视频源,其每一帧的立体图像都由8幅存在一定视差的平面图像合成而来。需要播放立体视频时,通过与狭缝光栅相匹配的立体合成算法[2],将8幅分辨率为720×360的图像合成分辨率为1 920×1 080的立体图像进行显示。所以,播放器除了具有普通视频播放器的基本功能之外,还具备视频格式自动匹配、帧率控制及平面/立体模式自由切换等功能。同时,为了达到明显的立体效果,帧率至少要达到20 f/s以上。
1 自由立体视频播放器实现原理
播放器基于MFC框架,按钮控件实现对播放器各项功能的控制,进度条控件显示视频解码线程的进度,滚动条控件显示视频播放的进度,并具备拖动滚动条以控制视频进度的功能。静态文本控件内显示视频帧数,累计时长、平均帧率等实时信息。播放器原理如图1所示。
立体播放器处理视频文件的流程是,从数据文件读取YUV420数据,根据图像的大小,以帧为单位转换为内存无关位图DIB(即RGB格式)[3],并利用与狭缝光栅相匹配的合成算法将RGB像素数据重新组合排列,最终将内存中已经排列好的RGB数据显示在播放器的视频区域。
视频播放器的显示原理与动画片的原理类似,利用人眼的视觉暂留特性,快速地把一张张图像贴到播放器的视频区域。人眼无法分辨这些静止图像,感觉如同播放动画一样。
通过贴有特定光栅的显示器来显示、播放立体图像,由于观察者双眼相对于光栅屏幕的空间位置不同,会产生双眼视差[4],从而令观察者无需借助立体眼镜即可裸眼体验立体感觉。
2 播放器性能
为了精确测量播放程序中各个功能模块的耗时情况,程序中加入了测量时间的功能。
实现方式:在程序运行时,首先调用QueryPerformanceFrequency()函数,得到CPU的时钟频率m_freq。在需要测量时间的操作 之前和之后调用QueryPerformanceCounter()函数,分别得到2个时钟计数值 m_count
_start和m_count_stop,则利用公式:
dT=1 000(m_count_stop-m_count_star)/m_freq
即可得到执行操作所消耗的时间。
利用此种方法,测得播放器播放视频过程中各个环节的耗时情况,如图2所示。
初步实现播放功能时,播放视频的帧率很低,只有9 f/s~11 f/s,所以需要针对视频播放过程中各个环节的原理和特点采取不同的方式提高播放器的运行效率。
3 关键技术研究与性能改进方法
3.1 内存中图像数据的显示
将内存中的RGB数据显示到视频区域,需要在具备图像显示功能的函数体中,提供表示图像颜色位数、尺寸等格式信息的结构体BITMAPINFO,以及图像数据起始地址的指针BMPbuffer[5]。对具备显示图像功能的函数进行测试,结果如下:
(1) StretchDIBits。可以用拉伸模式完成全屏显示功能,但是在拉伸模式下,图像会出现明显的色彩错误。
(2) SetDIBitsToDevice。只能原比例显示图像,如果显示器的分辨率设置小于1 920×1 080,则图像无法完整显示。
(3) DrawDib函数组。DrawDib是不依赖于图形设备接口(GDI)而直接操作显存的函数组。能够实现图像的显示功能,并且能以任意宽高比进行拉伸显示,拉伸后的图像无色彩错误。所以,此处采用DrawDib函数组来实现内存位图的显示功能。
显示功能的核心伪代码为:
DrawDibOpen();
DrawDibBegin();
DrawDibDraw(m_hDIB,hdc,rect.left,rect.top,rect.right,rect.bottom,&m_lpBmpInfo_->bmiHeader,BMPbuffer_,0,0,Width_,Height_,DDF_SAME_DRAW);
DrawDibEnd();
DrawDibClose()。
核心函数是DrawDibDraw(),参数中需要提供视频显示控件上表示绘图区域的参数rect、LPBITMAPINFO类型的结构体指针m_lpBmpInfo及DIB数据的起始地址BMPbuffer。
经测试,DrawDib函数组能够很好地完成DIB图像的显示,贴图速度快,没有明显的延迟现象,并且在全屏模式播放时没有出现类似使用StretchDIBits()函数时出现的色彩失真,达到了良好的显示效果。
3.2 内存映射文件处理大文件操作
分辨率为1 920×1 080的600帧的视频,采用YUV420存储格式时,体积高达1.78 GB。目前,对于这种大文件的操作通常是以内存映射文件的方式来加以处理的[6]。
根据播放器功能模块的耗时统计结果,未使用内存映射文件方式,而直接从硬盘读取分辨率为720×360×8的YUV420数据,平均每帧耗时35 ms,是整个播放流程中耗时最大的环节。所以,采用内存映射文件的方式改进文件读取模块可以提高播放器的效率。内存文件映射是Windows的一种内存管理方法,通过文件映射使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联,可以直接对被映射的文件进行访问,不必执行文件I/O操作也无需对文件内容进行缓冲处理,这种特性在进行大文件的磁盘事务操作时将获得很高的效益。内存映射文件的一般编程流程如图3所示。
经过实际对比测试,直接从硬盘读取分辨率为720×360×8的YUV420数据平均毎帧耗时35 ms。而通过内存映射文件的方式读取每帧数据的耗时可减少到15 ms左右。可见内存映射文件在处理大数据量文件时表现出了良好的性能,比通常使用的CFile类和 ReadFile()等函数的文件处理方式更加快速有效。
3.3 缓冲解码
播放器已经实现了YUV视频格式的播放功能,在此基础上加入H.264解码模块以实现播放器对H.264格式的支持。
采用开放源码的JMVC来处理H.264视频视频的解码。经测试,对于1 920×1 080的H.264视频,JMVC解码速度在12 f/s左右,如果采用实时解码播放的方式,播放的帧率较低,达不到流畅播放的要求,因此采用缓冲解码的方式来实现。环形缓冲区的工作原理如图4所示。
为了接收JMVC解码H.264产生的YUV数据,首先在内存中开辟出能够存储100帧YUV数据的环形缓冲区YUV Buffer。所谓“环形”,是指当需要接收的YUV图像帧数多于缓冲区的承载量时,数据指针从缓冲区结尾处重新跳转至缓冲区起始处,即重新返回到缓冲区起始处开始存储,将先前的YUV数据覆盖掉。播放线程将缓冲区内的YUV数据处理并显示出来后,数据指针同样从缓冲区结尾处重新跳转至缓冲区起始处,即重新返回缓冲区起始处,继续处理播放YUV数据。
3.4 多线程的应用
根据播放器功能模块的耗时统计结果,使用单线程方式处理YUV数据的转换和RGB数据的合成平均耗时为18 ms和20 ms。
为了提高多核CPU的利用效率,采用多线程方式改进程序的功能模块。由于图像以帧为单位,每帧YUV图像数据排列结构相同且无相关性,所以在处理时将每帧图像由上至下分成4快,每块图像数据开启一个线程,同时采用四线程来处理转换过程。转换得到RGB图像,其像素数据的排列顺序也是从左至右、从下至上,所以RGB的合成工作也可采用四线程实现。
经过实际对比测试,采用4线程处理YUV数据的转换和RGB数据的合成,平均耗时降至5 ms和7 ms,大大提高了图像数据的转换与合成效率。
3.5 多线程同步、线程间通信
在播放H.264视频格式的模式下,播放器首先启动解码线程,向缓冲区内写入YUV数据。当缓冲区内开辟的100帧控件被装满后,需要利用线程通信机制向播放线程发送消息。播放线程开始处于等待状态,从消息队列中取得相应消息并验证,当识别出解码线程发送的特定消息后开启播放功能。采用此种方式,可以在保证视频不间断播放的基础上提高视频播放的帧率。但是由于采用多线程实现,线程间的同步成为不可忽视的问题。
解码和播放两个线程是同时执行的,解码线程提供YUV图像数据,播放线程将缓冲区内的YUV数据处理成RGB位图数据并显示出来。不允许在某一时刻,在任何一段内存区域上同时发生读取和写入操作,所以,必须采取有效的线程同步措施。常用的线程同步方式有:互斥对象、事件对象、临界区等。播放器中需要同步的只有解码和播放两个线程,所以此处采用互斥对象来实现线程的同步。线程之间的同步规则是:
(1) 解码线程可以超前播放线程,但是超前的帧数不能大于缓冲区的长度。
(2) 播放线程不可以超前解码线程。
根据缓冲区的长度构造一个互斥对象数组,数组的每个元素与缓冲区中一帧的YUV数据相对应。解码线程向缓冲区的某个区域写入数据之前,首先要请求与该位置相对应的互斥对象的拥有权,完成写入操作后,应立即放弃该互斥对象的所有权;播放线程从缓冲区内读取数据之前,也要首先请求与该位置相对应的互斥对象,完成读取操作后立即释放该互斥对象。
由于互斥对象只能被一个线程所拥有,在执行相关操作前,如果得不到相应的互斥对象,线程函数就会暂停执行,等待另一个线程释放互斥对象,有效地避免了同一段内存同时被两个线程读取和写入情况的。
经过测试,这种方案成功地实现了解码线程和播放线程的同步协调,有效地避免了播放视频过程中出现进度差错的问题。
3.6 视频播放速率的控制
播放线程中包含的操作有:(1) YUV到RGB转换;(2) RGB图像数据的重新合成;(3) RGB图像的显示。这三项操作整合到一个线程中,因为其时间损耗之和明显小于H.264解码,所以通过控制播放线程的速率来实现控制整体播放帧率的功能。
播放线程中,设置一个定时器,每隔一定时间,执行一帧的播放操作。播放线程每处理一帧图像耗时约15 ms,每秒能显示图像的帧数约为66帧(1 000/15=66.6),所以用定时器控制播放线程的执行时,只要将定时器的时隙设置成大于15 ms的任意值,就能实现以指定的帧率来播放视频。例如,想要以25 f/s的帧率播放,只需将控制播放线程的定时器时隙设置成40 ms即可。
经过优化后的各个环节耗时情况如图5所示。
3.7 平面2D/立体3D模式任意切换功能
平面与立体图像的区别只是RGB像素排列的不同,所以要播放普通的平面2D图像,只需要在合成立体图像的环节中,将8个不同视点的数据来源设置成相同的8个视点图像中的任意一个,合成后的图像则是普通的2D平面图像,通过光栅屏幕的观看效果和在普通屏幕上观看平面图像的效果是相同的。
4 播放器外观及显示效果
最终实现的播放器具备打开、列表、缓冲、播放、暂停、停止、进度控制、全屏播放及2D/3D模式切换等功能。如图6所示,上图为普通模式播放效果,下图为立体模式播放效果(立体模式需要在贴有光栅的专门显示设备上观看,在普通显示器上观察到的是模糊的图像)。
本文提出的立体视频播放器的实现方案运用WIN32API实现播放功能,播放器不依赖于任何其他外部组件,软件结构清晰、设计原理简洁、具有很强的可扩展性。目前播放器已经实现了对YUV和H.264两种视频格式的支持,以后还可以根据需要扩展其功能。立体播放器以贴有光栅的42英寸大屏幕显示器作为显示输出设备,播放视频清晰流畅,立体视觉效果明显,帧率符合设计要求,达到了预期的设计目标和性能要求。
参考文献
[1] 秦开怀,罗建利.自由立体显示技术及其发展[J].中国图像图形学报,2009,14(10):1934-1940.
[2] 宋晓炜,杨蕾.一种光栅普适的LCD多视点立体图像合成方法[J].计算机应用,2008,1(28):195-198.
[3] 邵丹,韩家伟.YUV与RGB之间的转换[J]. 长春大学学报,2004(4):51-53.
[4] 杨嘉琛,侯春萍,雷建军.基于人眼视觉特征的立体图像质量客观评价方法[J].天津大学学报,2009,42(7):622-627.
[5] 王伯尊,张凤茹.在Windows下实现多幅大尺寸位图的显示与浏览[J].应用科技,2002,29(4):42-44.
[6] 贾琴勇,郭庆平.内存映射文件在大型数据文件中的实现及其优越性[J].电脑知识与技术,2007(17):1352-1353.