android hwcomposer 在视频播放中的应用

来源:互联网 发布:linux和win7双系统 编辑:程序博客网 时间:2024/05/16 11:56

之前写了一篇博客,分析了视频如何显示的

http://blog.csdn.net/wan8180192/article/details/50269405

以及gralloc的内存管理

http://blog.csdn.net/wan8180192/article/details/50513895


这里结合hwcomposer模块,以及视频播放的场景,对其中有一些细节,在这里再做补充一下

android中,多个surface layer要显示到屏幕上,就要合成到一起,合成方式有两种:
离线合成
先将所有图层画到一个最终层(FrameBuffer)上,再将FrameBuffer送到LCD显示。由于合成FrameBuffer与送LCD显示一般是异步的(线下生成FrameBuffer,需要时线上的LCD去取),因此叫离线合成。
在线合成
不使用FrameBuffer,在LCD需要显示某一行的像素时,用显示控制器将所有图层与该行相关的数据取出,合成一行像素送过去。只有一个图层时,又叫Overlay技术。 
由于省去合成FrameBuffer时读图层,写FrameBuffer的步骤,大幅降低了内存传输量,减少了功耗,但这个需要硬件支持。

对于这两种方式,各有优缺点,
离线合成充分利用GPU,更加灵活,不受win layer数量限制。但是功耗大,不利于移动设备。GPU如果性能不强,复杂应用场景下会出现卡顿,实时性不好
在线合成,功耗小,没有性能瓶颈,没有时延。但是不够灵活。UI layer一旦变多,需要重新借助于GPU的离线合成。 

一般来说,优先使用overlay.实在不行就用GPU

在实际代码中,可以看到SurfaceFlinger::doComposeSurfaces中, 有以下处理,
switch (cur->getCompositionType()) {                    case HWC_OVERLAY: {   ////overlay 方式,采用HWC硬件来合成                        const Layer::State& state(layer->getDrawingState());                        if ((cur->getHints() & HWC_HINT_CLEAR_FB)                                && i                                && layer->isOpaque() && (state.alpha == 0xFF)                                && hasGlesComposition) {                            // never clear the very first layer since we're                            // guaranteed the FB is already cleared                            layer->clearWithOpenGL(hw, clip);                        }                        break;                    }                    case HWC_FRAMEBUFFER: {   ///非overlay方式,采用GPU来合成,后续调用的是openGL                        layer->draw(hw, clip);                        break;                    }                    case HWC_FRAMEBUFFER_TARGET: {   /////异常处理。FRAMEBUFFER_TARGET不应该出现在这个流程里。FRAMEBUFFER_TARGET是合成是使用的目标buffer。                        // this should not happen as the iterator shouldn't                        // let us get there.                        ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%d)", i);                        break;                    }                }

其中:
HWC_FRAMEBUFFER_TARGET:该Layer是3D合成的目标Layer
HWC_FRAMEBUFFER:hwcomposer无法处理此Layer,该Layer需要走GPU合成流程,用OpenGL绘制
HWC_OVERLAY:该Layer为硬件合成器所处理,不需要OpenGLES去渲染

结合视频播放的场景,这里打印了部分主要的log, 打出来的基本都是函数名。在log的基础上分析一下。
显示视频帧,exynos上主要有两个路子:
1,利用GPU。
  采用GPU做渲染以实现缩放,色彩空间转换,以及与其他APP UI layer的composer合成。
  这里的合成,实际上就是offline离线合成.
  
2,采用display controller 模块。
  display controller会利用exynos的FIMC模块来完成缩放,色彩空间转换,
  利用exynos 4412的5个win layer, 将视频单独一个win layer,与其他win layer的APP UI合成。
  这实际上是online在线合成。 这种合成方式,也叫做overlay方式
  exynos的display controller实际上相当于高通的MSM平台上的MDP,这两个模块,通俗意义上可以被称作hwcomposer/HWC
 
 
1,先介绍下离线合成场景下的显示过程:  
##########################tiled 和linear 转换 :解码完毕之后,exynos解出来的是很变态的YUV420 tiled格式。后面要显示,就要转成常见的linear格式,目前的实现采用了 NEON软件方法实现
E/libcsc  ( 1660):  csc_convert
E/libcsc  ( 1660):  conv_sw
E/libcsc  ( 1660):  HAL_PIXEL_FORMAT_YCbCr_420_SP
E/libcsc  ( 1660):  HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED


##########################OMX 把YUV数据queuebuffer
E/AwesomePlayer( 1660): status_t err = mNativeWindow->queueBuffer //这里queuebuffer的操作是gralloc利用MALI的UMP/ION内存管理机制分配得到的图形缓冲区,不是framebuffer
E/Surface ( 1660): Surface::hook_queueBuffer
E/SurfaceFlinger( 1652): Layer::onFrameAvailable
E/SurfaceFlinger( 1652): handlePageFlip
E/SurfaceFlinger( 1652): latchBuffer //把YUV数据绑定到openGL纹理,


##########################VSYNC到来,开始用GPU合成,主要是在doComposeSurfaces里面,实际上就是GPU读取YUV数据作为纹理,进行渲染。渲染完了放在了framebuffer里面
E/SurfaceFlinger( 1652): handleMessageRefresh
E/SurfaceFlinger( 1652): doComposition
E/SurfaceFlinger( 1652): doDisplayComposition
E/SurfaceFlinger( 1652): doComposeSurfaces
E/SurfaceFlinger( 1652): hasGlesComposition
E/Surface ( 1652): Surface::hook_dequeueBuffer   
E/Surface ( 1652): Surface::dequeueBuffer   //这里dequeuebuffer的操作是gralloc申请到的framebuffer
E/SurfaceFlinger( 1652): count 2
E/SurfaceFlinger( 1652): else clip.isEmpty
E/SurfaceFlinger( 1652): onDraw
E/SurfaceFlinger( 1652): drawWithOpenGL 0,0,1280,720,1280,720,  //进行纹理渲染。
E/SurfaceFlinger( 1652): count 2
E/SurfaceFlinger( 1652): else clip.isEmpty
E/SurfaceFlinger( 1652): onDraw
E/SurfaceFlinger( 1652): drawWithOpenGL 0,0,800,480,800,480, //这是干啥?哪位大侠知道?暂且认为是在做composer


##########################渲染,合成之后调用swapBuffers,把渲染完的数据hook_queueBuffer,这里的buffer都是framebuffer了。然后调用mFbDev->post,也就是framebuffer HAL的fb_post,画到屏幕上。
E/SurfaceFlinger( 1652): hw->swapBuffers
E/SurfaceFlinger( 1652): DisplayDevice::swapBuffers
E/SurfaceFlinger( 1652): eglSwapBuffers  //eglSwapBuffers最终调用了openGL,maliDDK中的代码,也就是调用了调用了libmali.so,其内部调用了NativeWindow->queueBuffer,这部分代码没有开源,不做分析
E/Surface ( 1652): Surface::hook_queueBuffer
E/SurfaceFlinger( 1652): FramebufferSurface::onFrameAvailable
E/SurfaceFlinger( 1652): HWComposer::fbPost
E/SurfaceFlinger( 1652): mFbDev->post


##########################下面好像都没有实质工作。都是在处理fence之类的
E/SurfaceFlinger( 1652): mDisplaySurface->advanceFrame
E/SurfaceFlinger( 1652): DisplayDevice::flip
E/SurfaceFlinger( 1652): SurfaceFlinger::postFramebuffer
E/SurfaceFlinger( 1652): hw->onSwapBuffersCompleted
E/SurfaceFlinger( 1652): else currentLayers[i]->onLayerDisplayed
E/SurfaceFlinger( 1652): Layer::onLayerDisplayed
E/SurfaceFlinger( 1652): else currentLayers[i]->onLayerDisplayed
E/SurfaceFlinger( 1652): Layer::onLayerDisplayed


综上:离线合成的方法实际上就是: 
decoder---->UMP/ION graphicbuffer 1
                                                                    -------->GPU--------->framebuffer----->screen
APP UI ---->UMP/ION graphicbuffer 2

2,在线合成的尚未调试完毕,具体细节后面会补充:
其主要思路就是
decoder-->FIMC---------------------------------->framebuffer for win1
                                                                                                                -------->display controller----->screen
APP UI -->ION graphicbuffer ---->GPU----->framebuffer for win2
可以看到HWC在合成时,overlay方式的视频不需要直接和非Overlay方式的APP UI layer同步,它只要和这些非Overlay层合成的最终结果同步就可以了。
非Overlay层合成的最终结果放在了FramebufferTarget中。
GPU渲染完非Overlay的层后,通过queueBuffer()将GraphicBuffer放入FramebufferSurface对应的BufferQueue,然后FramebufferSurface::onFrameAvailable()被调用。它先会通过nextBuffer()->acquireBufferLocked()从BufferQueue中拿一个GraphicBuffer,接着调用HWComposer::fbPost()->setFramebufferTarget(),其中会把刚才acquire的GraphicBuffer设到HWC的Layer list中的FramebufferTarget slot中,然后进行HWC合成

1 0