android - MediaCodec 和 TextureView 的 Z 顺序问题

标签 android surfaceview android-mediacodec textureview

在我的 Android 应用程序中,我需要使用以下 Z 顺序呈现三个 View :

  1. 在底部,覆盖整个屏幕的 MediaCodec 解码器的输出表面。我要求我必须转换 MediaCodec 生成的图像(例如缩放它)
  2. 在中间,GLSurfaceView(或我定义的其他运行 GL 着色器的表面/ View )覆盖整个屏幕。显然,这一层中的一些像素将是透明的,以便看到下面的 MediaCodec 输出。
  3. 在顶部,任何其他 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/

相关文章:

android - 推特登录失败

带有列表导航的 Android ActionBar : save selected item position during configuration change?

安卓 MediaCodec 解码器 : Slow Down Video Playback

android - MediaMuxer 错误 "Failed to stop the muxer"

android - 如何使用 MediaCodec 修剪视频

java - Phonegap 从 Java 代码中获取本地存储值?

java - Android 首选项屏幕中的自定义标题栏

android - surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) 已弃用

Android camera surfaceChanged不断被调用

android - 在我允许后表面 View 不显示相机