MediaCodec和TextureView的Z顺序问题

时间:2014-03-21 20:49:17

标签: android surfaceview mediacodec textureview

在我的Android应用中,我需要使用以下Z顺序渲染三个视图:

  1. 在底部,覆盖整个屏幕的MediaCodec解码器的输出表面。我要求我必须转换MediaCodec生成的图像(例如缩放它)
  2. 在中间,GLSurfaceView(或我运行的GL着色器的其他曲面/视图)覆盖整个屏幕。显然,此图层中的某些像素将是透明的,以便查看下方的MediaCodec输出。
  3. 最重要的是,任何其他观点 - 比如ImageView。我不确定这些最顶层的视图是否需要透明度,也许是完全不透明的矩形视图可以 - 它们只是不会覆盖整个屏幕并且会移动。
  4. 看起来这是不可能的,但也许我错过了一些东西,或者有一种方法可以在较低的水平上做更多的努力(例如EGL上下文或类似的东西......我目前不明白。)

    我无法让这个工作起来并且担心这是不可能的原因是:

    • 对于底部MediaCodec输出图层(1),我必须能够转换图像。因此,我给MediaCodec渲染的表面必须来自TextureView
    • 为了能够透过中间GLSurfaceView(2)的透明像素,我必须调用GLSurfaceView.setZOrderOnTop(true)。否则GLSurfaceView是不透明的。
    • 但是调用GLSurfaceView.setZOrderOnTop(true)意味着没有其他视图(3)呈现在GLSurfaceView之上。例如。 ImageView始终显示在GLSurfaceView的不透明像素后面。

    看起来调用GLSurfaceView.setZOrderMediaOverlay(true)而不是GLSurfaceView.setZOrderOnTop(true)是为了解决这个问题并促进这种类型的Z排序。如果最底部的MediaCodec输出图层是SurfaceView,则会发生这种情况。但我需要它是TextureView所以我可以改变它。如果GLSurfaceView.setZOrderMediaOverlay(true)位于其下方TextureView,则TextureView似乎无效:GLSurfaceView完全被中间{{1}}图层遮挡,而不是通过透明显示像素。

    这种Z排序是不可能的吗?或者可以通过搞乱EGL和上下文等来完成它?

1 个答案:

答案 0 :(得分:2)

EGL上下文在这里并不重要。你的斗争是使用SurfaceFlinger和视图系统。

如果您运行adb shell dumpsys SurfaceFlinger,您可以看到系统合成器知道的所有图层的完整列表。如果您在SurfaceView中播放320x240视频,它看起来像这样(为了简洁起见,删除了几列和其他许多内容):

    type    |          source crop              |           frame           name 
------------+-----------------------------------+--------------------------------
        HWC | [    0.0,    0.0,  320.0,  240.0] | [   48,  411, 1032, 1149] SurfaceView
        HWC | [    0.0,   75.0, 1080.0, 1776.0] | [    0,   75, 1080, 1776] com.android.grafika/com.android.grafika.PlayMovieSurfaceActivity
        HWC | [    0.0,    0.0, 1080.0,   75.0] | [    0,    0, 1080,   75] StatusBar
        HWC | [    0.0,    0.0, 1080.0,  144.0] | [    0, 1776, 1080, 1920] NavigationBar
  FB TARGET | [    0.0,    0.0, 1080.0, 1920.0] | [    0,    0, 1080, 1920] HWC_FRAMEBUFFER_TARGET

这些层从后到前呈Z顺序。 SurfaceView的表面层位于后面,app UI层位于其后面,系统状态+导航栏位于所有位置之上。

应用程序视图层次结构中的所有内容都在单个图层上呈现。这包括TextureView。您无法控制其相对于其他硬件合成图层的Z顺序。

SurfaceView很有意思,因为View部分只是一个透明的占位符,而真正的动作发生在那个单独的层上,你可以控制它的Z顺序(一点点)。你可以将它放在三个不同的层面上:

  • "介质" (默认)
  • "媒体重叠"
  • (app UI在这里)
  • "面板" (ZOrderOnTop)

所以你要做的就是将你的MediaCodec输出放在默认图层,将你的GLES输出放在"媒体覆盖"层。您需要使用SurfaceView

执行这两项操作

很难从这里提供更好的建议,因为您描述了您尝试使用解决方案时遇到的问题,而不是您尝试解决的问题(即您正在构建什么? ),但我可以提出一些建议。

首先,可以缩放SurfaceView。如果你看一下上面的dumpsys输出,你会注意到" SurfaceView" line的源裁剪矩形为320x240(视频大小),目标矩形为984x738。这来自Grafika's"播放视频(SurfaceView)",它调整SurfaceView的大小以保持视频的4:3宽高比。 SurfaceFlinger负责缩放内容以匹配视图。

其次,如果您没有显示受DRM保护的视频内容,您可以将其发送到SurfaceTexture,并在您渲染其他内容时使用GLES进行渲染。 (这正是TextureView所做的,这就是它需要硬件加速的原因。)参见例如"持续捕获"在格拉菲卡。

更新Android System-Level Graphics doc可以找到更长的说明。