资源描述
Android Overlay优化方案
摘要:总所周知overlay在android操作系统中,主要用于处理Camera preview和视频输出,以及地图图层叠加等。本文主要介绍android图层处理overlay模块从android 1.5(以marvell 310平台为例)至android 2.x改进和个人部分地方优化方案。
1、Marvell 310平台的overlay特性分析
从310cupcake(android1.5)开始,marvell就有使用overlay了,只是这个overlay真是太差强人意。源代码位于AR9960\vendor\marvell\littleton\overlay2display,其实就一个文件display.cpp,编译后位libMrvlOverlay.so,然后其他地方可以直接调用。可以看出,marvell自己做的相当省事,估计310平台的局限性吧,它也没把overlay做成一个通用的hal。
其实overlay说起来很简单的东西,一个display.cpp 300多行代码,主要是对/dev/graphics/fb2进行内存映射,然后写入yuv图像数据,屏幕就出现图像了。
关于overlay,其实它是对应framebuffer中的一层,即fb2这层。在310平台中,处于最底层的是fb0,这个就是所谓的主framebuffer,往上面就是fb2(overlay),fb1也算是overlay,不过是用于ui显示的,最上面的是fb3,用于显示硬件鼠标。
Mavell 310平台的特性,fb0支持全透明不支持半透明,fb2不支持透明,fb1都支持。我们可以发现,不管是在cupcake,还是在donut,一旦使用了overlay,fb2都是盖住下面的主屏幕,导致系统显示无法透出来,在camera preview中就是这样的特性,所以marvell的camera preview就优先使用Surface纯软输出,没使用overlay。
在video playerback中,更能发现,当视频正常播放时,全盘状态,没任何系统菜单时,播放显示都很流畅。但是,当点击屏幕,出现进度条时,就会发现屏幕闪了一下,播放开始迟钝。这就是marvell 310overlay的缺陷问题,他们自己都没去很好解决。当全屏播放时,确实使用了overlay,直接投影到屏幕的fb2上,当点击屏幕出现进度条时,就关闭了overlay,使用系统Surface软件输出,效率就大大降低。可以修改android_surface_output.cpp的代码,让视频播放时一直用overlay,这时候我们就会发现当点击屏幕时,进度条并看不到,这就是fb2挡住了fb0的原因。
原来cupcake和donut的时候,即使使用了软件surface的输出,但是camera preview还算是很流畅,但是到了éclair,camera菜单图层叠加更加复杂,使用软件surface输出时,整个preview很卡,所以,我们不得不使用overlay。
2、underlay方案
通过对marvell 310平台datasheet的研究,发现framebuffer图层这边的层次关系可以做出调整,如下图所示,原先的默认方案是fb0处于最底层,通过对相关寄存器的配置,我们可以配置成underlay的方案,此方案fb0置于fb2的上面,由于fb0可以做到全透明,所以fb2就能透出来。
underlay方案看似可以解决我们上面碰到的问题,但是,由于fb0无法做到半透明,所以camera和video等应用使用到overlay时,就无法看到半透明的菜单或是进度条效果。
underlay的方案还要求定义透明色,在驱动这边通过设置相应寄存器,定义某种特殊的透明色,一旦fb0上有这种透明色,fb2就可以透出来。我们选择的透明色是0xf81f。
这样,我们得定义一个规则,当使用camera或是video时,在显示范围内,当是黑色的,即0x0000时,就在hal中的framebuffer中把0x0000转换成0xf81f,最后再写入fb0。
3、两种方案的比较
除了上面这种underlay的方案,另外一个方案就是原来的video使用的方案。也就是说,在使用overlay时,overlay所在的区域不能有其他系统显示,不然会被挡住,当有菜单弹出来时,或是当overlay不应该显示在最顶层时,关闭overlay马上改用系统软件surface输出。
很明显,这个方案更加不好,在video中,我们也了解到,弹出进度条时,会闪动一下,严重影响用户体验,而且当用到软件surface输出时,整个系统的显示效率很低。
在android2.1中,如果不适用overlay,camera preview是很慢的,所以如果采用这种方案,当用户点击menu键时,camera preview就从刚才的流畅变成一卡一卡的显示。
Underlay方案虽然不支持系统的半透明效果,但是这只针对与camera和video的系统应用而已,我们可以让系统的这两个应用不要半透明效果,同时可以开发新的接口api,让其他外来的camera或是video应用默认情况下就是用软件surface输出而不用overlay。
终上所述,选择underlay方案还是比较理想的。
4、Android2.1中overlay的优化处理
android的设计思路是,在ISurface中创建overlay,接口代码如下:
由于marvell的原因,没提供overlay的hal代码,而是直接调用overlay2display来直接操作overlay,所以,我们可以再ISurface中定义我们的接口,当需要是用overlay时,设置下就行了,最后的接口定义如下所示:
getSurfaceRect用于获取overlay的rect,isOnTop用于检测overlay所在的surface是不是置于顶端,这两个接口都是marvell设计的。
underlay方案主要使用useCameraVideo这个接口,当use==1时,使用overlay,当use==0时关闭overlay。
ISurface的接口设置会通过IBinder一步步通讯最后设置到framebuffer这边,具体的流程路线如下所示:
FramebufferNativeWindow->queueBuffer
framebuffer->fb_post
LayerBase.cpp
LayerBaseClient::Surface->useCameraVideo
ISurface->useCameraVideo
LayerBuffer.cpp
SurfaceLayerBuffer:public LayerBaseClient::Surface
->useCameraVideo
Surfaceflinger->composeSurface
(判断Surface是否显示或是被销毁)
DisplayHardware->setUseCV
IBinder
具体的流程代码请看提交的源程序,这里主要讲下framebuffer这块的处理。
libgralloc中gralloc_priv.h文件中private_handle_t的定义中,我们需要添加一个参数,用于通知framebuffer是否使用overlay,如下图所示:
然后,我们在FramebufferNativeWindow::queueBuffer中,我们需要对handle进行转换,写入useCV的值,而这个值就是从ISurface::useCameraVideo(int use)传进来的。
通过上面的处理,我们就能到达framebuffer了。在framebuffer.cpp中,当使用overlay时,我们需要对0x0000(黑色背景)进行颜色转换,转换成310平台支持的透明色,即0xf81f,这样我们的fb2就能透过fb0。修改的代码如下:
通过上面的这些处理,我们就能实现310平台上的underlay效果了。
最后只要在Camerahardware.cpp和android_surface_output.cpp中调用mSurface->useCameraVideo(1)来打开overlay,调用mSurface->useCameraVideo(0)来关闭overlay就可以了。
camera preview overlay的旋转
由于camera提供的图像原因,如果不对yuv数据进行处理就直接送到overlay,我们会发现,camera preview的图像是倒了九十度的。
所以,我们得先把camera提供的yuv图像数据先旋转九十度,然后再送给overlay。这里介绍两种方法,ipp软件旋转和gcu旋转,ipp速度会比较慢,gcu用到m2d,速度会比较快。
ipp软件旋转
ipp软件旋转,其实就是使用ipp库把camera或是video提供的yuv数据进行处理做九十度选择,然后送给overlay做输出。这边主要介绍下ipp旋转所用到的函数:ippiYCbCr420RszRot_8u_P3R。
我们主要注意下dstStep这个参数,原来用ipp无法旋转,也是在这个参数出了问题,网络上这块函数的介绍基本没有,也只能靠自己研究了。
因为我们是需要做90°旋转,所以这个dstStep就不能跟srcStep一个样,原来srcStep是用width来处理,比如640*480的图像,srcStep就得根据640这个数值来计算。现在,旋转90°,dstStep就得用height来计算,也就是480。
置于srcSize和dstSize这两个参数,很简单,统一方式,比如:
320*240要旋转90°,还得扩大到640*480,那么不管旋转角度,即width和height不用做对调处理。
其他参数的意思,通过看源代码就能会比较清晰的理解了。
gcu旋转
原来marvell在camera这边是使用ipp旋转的,但是一直无法旋转过来,这应该是marvell自己没有做好的问题,而gcu旋转首先是用在video 视频播放这边了,camera这样也可以用到,就修改了过来。
具体就看源程序了,也没啥好介绍的,主要介绍一个地方。
这个函数的第二个参数,1表示使用硬件连续内存,0的话使用虚拟内存。比如下面的内存分配:
如果是使用了pmem内存,那么这个值要设置成1,否则就是设置0了。如果错误设置了这个值,那么出来的图像会很奇怪!
5、冗余方案
默认情况下,我们是优先使用gcu旋转的,这样preview速度会比较快,没有拖泥带水的感觉。但是,gcu内核这边有个bug,有一定小概率会出席gcu内存无法分配的问题,从而导致camera preview出问题,最后整个系统黑屏重启,这是个很严重的问题。
通过分析,发现问题出现在gcu_parse这个调用。
所以,我们做了个冗余方案,当gcu_parse出现问题时,我们切换到ipp软件旋转方式。
6、overlay双缓冲
framebuffer中有双缓冲,主要利用FBIOPUT_VSCREENINFO ioctl在两个缓冲区进行切换。而最开始的overlay,是没有双缓冲的,也就是说,直接写入fb2缓冲区,我们就能从屏幕上看到camera和video的画面。
但是,这会出现一个问题,当画面运动速度快点时,会出现“斜切线”问题,这个问题产生的主要原因是上一帧的overlay还没写完时,下一帧已经到来,导致了图像看起来像是被切掉了,这时候,我们就必须做下双缓冲,来解决这个问题。
在overlay2display这个目录的display.h中,我们修改display_cfg这个结构体,添加我们需要的变量doublebuffer来判断是否使用双缓冲,代码如下:
当doublebuffer==1时,使用overlay;
当doublebuffer==0时,
则不适用。
在display.cpp中,我们还必须定义下两个全局变量:
static int mIndex = 0;
static struct fb_var_screeninfo minfo;
mIndex主要用于两个缓冲区的判断,minfo主要用于mmap时fb_var_screeninfo信息的保存。
在overlay2_mmap对fb2进行内存映射时,我们要判断是否使用双缓冲来决定内存映射的大小,如下所示:
在get_overlay_yuv函数中,我们要获取现在需要写入的缓冲区的地址,这边也要判断是否使用双缓冲:
我们还得在marvell这个overlay模块中添加双缓冲切换输出函数,当使用双缓冲时,最后一步就是调用这个函数:
这里主要用到framebuffer中的FBIOPAN_DISPLAY这个ioctl,对两个缓冲区进行切换,实现overlay的双缓冲输出。
最后,就是在camera或是video的使用中,通过设置doublebuffer这个值,来决定是否使用双缓冲,具体代码请看源程序。
7、总结
使用overlay时,会出现很多莫名奇妙的问题。
刚开始时,我们会发现,启动摄像头时,preview图像已经出现了,但是我们还在系统界面上,这是preview图像被送到overlay速度比较快,而camera应用界面还没出现,系统界面的背景是黑色的,所以,我们就先看到了camera图像。这个问题,我们可以再Surfaceflinger中的compose图像时做处理,可以判断camera界面可见时,再通知framebuffer做透明色转换。
还有个比较重大的问题,这边先做简单介绍,就是别调用mSurface->getSurfaceRect(),这个可能无法正确获取preview的overlay大小,而且频繁调用时会lock住framebuffer,导致打开camera时,系统死机,而且概率很高。
展开阅读全文