在我的 Android 应用程序中,我需要使用以下 Z 顺序呈现三个 View :
- 在底部,覆盖整个屏幕的
MediaCodec
解码器的输出表面。我要求我必须转换MediaCodec
生成的图像(例如缩放它) - 在中间,
GLSurfaceView
(或我定义的其他运行 GL 着色器的表面/ View )覆盖整个屏幕。显然,这一层中的一些像素将是透明的,以便看到下面的MediaCodec
输出。 - 在顶部,任何其他 View - 例如
ImageView
。不确定我是否需要这些最顶层 View 的透明度,也许完全不透明的矩形 View 没问题 - 它们只是不会覆盖整个屏幕并且会四处移动。
看起来这是不可能的,但也许我遗漏了一些东西,或者有一种方法可以在较低级别上付出更多努力(例如 EGL 上下文或类似的东西......我目前不明白) .
我无法让它工作并且担心它不可能的原因是:
- 对于底部
MediaCodec
输出层 (1),我必须能够转换图像。所以,我给 MediaCodec 渲染的表面必须来自TextureView
- 为了能够看穿中间
GLSurfaceView
(2) 的透明像素,我必须调用GLSurfaceView.setZOrderOnTop(true)
。否则 GLSurfaceView 是不透明的。 - 但是调用
GLSurfaceView.setZOrderOnTop(true)
意味着没有其他 View (3) 呈现在GLSurfaceView
之上。例如。ImageView
将始终出现在GLSurfaceView
的不透明像素后面。
看起来调用 GLSurfaceView.setZOrderMediaOverlay(true)
而不是 GLSurfaceView.setZOrderOnTop(true)
是为了解决这个问题并促进这种类型的 Z 排序。如果最底层的 MediaCodec
输出层是 SurfaceView
,它也会执行此操作。但我需要它是一个 TextureView
以便我可以转换它。当下面有一个 TextureView
时,GLSurfaceView.setZOrderMediaOverlay(true)
似乎不起作用:TextureView
完全被中间遮挡了GLSurfaceView
图层而不是通过透明像素显示。
这种 Z 排序不可能是正确的吗?还是可以通过摆弄 EGL 和上下文等来实现?
最佳答案
EGL 上下文在这里并不真正相关。你的战斗是与 SurfaceFlinger 和 View 系统。
如果您运行 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 的表面层在后面,应用程序 UI 层在上面,系统状态 + 导航栏在一切之上。
应用的 View 层次结构中的所有内容都呈现在一个层上。这包括 TextureView
。您无法控制其相对于其他硬件合成层的 Z 顺序。
SurfaceView
的奇特之处在于 View 部分只是一个透明的占位符,而真正的 Action 发生在那个单独的层上,您可以(稍微)控制其 Z 顺序。您可以将其分为三个不同的层次:
- “媒体”(默认)
- “媒体覆盖”
- (此处为应用界面)
- “面板”(ZOrderOnTop)
所以您要做的是将您的 MediaCodec
输出放在默认层,并将您的 GLES 输出放在“媒体覆盖”层。您需要使用 SurfaceView
完成这两项操作。
很难从这里提供更好的建议,因为您描述了您在尝试解决方案时遇到的问题,而不是您试图解决的问题(即您在构建什么?),但我可以提供一些建议的建议。
首先,您可以缩放 SurfaceView。如果您查看上面的 dumpsys 输出,您会注意到“SurfaceView”行的源裁剪矩形为 320x240(视频的大小),目标矩形为 984x738。这是来自 Grafika's “播放视频 (SurfaceView)”,调整 SurfaceView 的大小以保持视频的 4:3 纵横比。 SurfaceFlinger 负责缩放内容以匹配 View 。
其次,如果您不显示受 DRM 保护的视频内容,您可以将其发送到 SurfaceTexture
并在渲染其他内容时使用 GLES 渲染它。 (这正是 TextureView
所做的,这就是它需要硬件加速的原因。)参见例如Grafika 中的“连续捕获”。
更新:可以在 Android System-Level Graphics doc 中找到更长的描述.
关于android - MediaCodec 和 TextureView 的 Z 顺序问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22569271/