3

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

  1. 在底部,MediaCodec覆盖整个屏幕的解码器的输出表面。我有要求我必须转换由MediaCodec(例如缩放它)产生的图像
  2. 在中间,一个GLSurfaceView(或我定义的其他运行 GL 着色器的表面/视图)覆盖整个屏幕。显然,这一层中的一些像素将是透明的,以便看到MediaCodec下面的输出。
  3. 最重要的是,任何其他视图 - 比如说ImageView. 不确定我是否需要这些最顶层视图的透明度,也许完全不透明的矩形视图是可以的——它们只是不会覆盖整个屏幕并且会四处移动。

看起来这是不可能的,但也许我错过了一些东西,或者有一种方法可以在较低级别上付出更多的努力(例如 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)有一个时似乎不起作用:它被中间层完全遮挡,而不是通过透明像素显示。TextureViewTextureViewGLSurfaceView

这种 Z 排序是不可能的,这是否正确?还是可以通过搞乱 EGL 和上下文等来完成?

4

1 回答 1

2

EGL 上下文在这里并不真正相关。你的战斗是 SurfaceFlinger 和视图系统。

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

    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奇特的是,视图部分只是一个透明的占位符,而真正的动作发生在那个单独的层上,你可以控制它的 Z 顺序(一点点)。你可以把它放在三个不同的层次上:

  • “媒体”(默认)
  • “媒体覆盖”
  • (应用程序界面在这里)
  • “面板”(ZOrderOnTop)

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

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

首先,您可以缩放 SurfaceView。如果您查看上面的 dumpsys 输出,您会注意到“SurfaceView”行的源裁剪矩形为 320x240(视频大小),目标矩形为 984x738。这来自Grafika 的“播放视频 (SurfaceView)”,它调整了 SurfaceView 的大小以保持视频的 4:3 纵横比。SurfaceFlinger 负责缩放内容以匹配视图。

其次,如果您没有显示受 DRM 保护的视频内容,您可以将其发送到 aSurfaceTexture并在渲染其他所有内容时使用 GLES 进行渲染。(这正是TextureView它的作用,这就是它需要硬件加速的原因。)参见 Grafika 中的“连续捕获”。

更新:可以在Android System-Level Graphics doc中找到更长的描述。

于 2014-03-22T00:37:10.420 回答