【发布时间】:2016-03-29 03:05:46
【问题描述】:
我在使用 Android 的 MediaProjection API 时遇到了一些问题(实际上更多,但这些是更关键的问题)。阅读graphics architecture 并没有真正的帮助,所以我只是想了解我是否在我的代码流中跳过了某些内容。
假设:
我有一个专用的 GL 渲染线程,已初始化,并在其上生成了 GL 纹理。我为纹理设置了 WxH 的默认缓冲区大小。
我使用 GL 纹理创建了一个 SurfaceTexture,并为此表面纹理创建了一个 Surface。
通过 MediaProjection 创建一个大小为 WxH 的虚拟显示器,并将其表面设置为上述表面。
问题 1:一切正常(全帧正确输入)或不正常(所有帧都是黑色的;或者例如每帧只有一半可见 - 所有帧都相同的一半;或者屏幕的某些部分是重复到其他部分,有时甚至歪斜)。
问题 2:虽然在某些全屏 GL 游戏中花费了时间,但在经过 FIXED 时间(大约 4 分钟)后,所有传入的帧都被冻结(例如,我收到“新”帧,但它实际上是一个并且相同的图像)。用 glReadPixels 阅读可以确认结果 - 问题是,实际显示超出了该帧。强制它“恢复”的唯一方法是调出状态栏或导航栏,它会立即开始向我发送正确的帧。当然,再过4分钟,又发生了……
问题 3:在 VirtualDisplay 上调用 resize(),在对 GL 纹理也调用 setDefaultBUfferSize() 之后,最终在 90% 的情况下显示问题 #1(黑色/剪切帧,其他屏幕区域的伪影...)
我在同一个线程中使用 updateTextureImage -> GL 纹理绘制的调用序列,所以我的正常理解是永远不会发生我以某种方式从半满的 GL 缓冲区或其他东西中读取的情况......对吧?
我也通过将 VirtualDisplay 直接渲染到 MediaCodec 的表面(不涉及自定义 GL)来测试这个问题 - 相同的行为。 更新 实际上由于 MediaCodec 有一个固定大小的创建表面,所以这个 bug 无法重现,因为我们只能调整虚拟显示器的大小,而不能调整编码器的表面大小,所以这不是一个真正的 bug(但甚至像这样让 VirtualDisplay 以某种方式相应地调整表面大小会很好)。
我觉得关闭虚拟显示器时有什么东西泄漏了,或者在创建 VirtualDisplay 之间没有正确初始化,因为它不一致。很可能会发生全新的 MediaProjection 屏幕捕获许可,具有全新的虚拟显示器,具有刚刚创建的表面纹理,最终只会给我半切帧......让我有一张大扑克脸。 ..
PS:所有这些都发生在装有 Android 6.0.1 的 Nexus 6 上。
【问题讨论】:
-
听起来有点像github.com/google/grafika/issues/43,因为它运行了 N 分钟然后停止更新。提交错误的人发现了一个奇怪的解决方法。您是否使用单个 EGL 上下文?重复看到屏幕部分通常是 GPU 平铺的伪影,并且通常是由于未能
glClear()屏幕造成的。 (我认为一些 bigflake 代码故意跳过glClear(),因为它在全屏上呈现;我建议将屏幕清除为鲜红色,以使任何“洞”变得明显。) -
@fadden - 在直接渲染到 MediaCodec 表面时,我无法控制 glClear。在我的 GL 渲染器中,我确实调用了 glClear(用于信箱化目的,在旋转时清除整个视口),然后渲染纹理(据说是由最新的 updateTextureImage 调用更新的)。问题是纹理本身是不完整的。在它自己的范围内。正如 EncodedNybble 所提到的,当实际显示旋转与虚拟显示尺寸正交(当它自行缩放和居中时)时,它似乎也更频繁地发生。
-
我注意到的另一件事是关闭屏幕并在几秒钟后重新打开可能会解决问题。我怀疑纹理缓冲区大小存在问题,或者生产者在调用 texture.setDefaultBufferSize 后没有使用正确大小的缓冲区?也许它使用旧缓冲区?文档说相机生产者覆盖了尺寸,为什么 VirtualDisplay 也不这样做呢?我认为如果我最初不设置缓冲区大小 IIRC,则不会呈现任何内容。是否有办法强制删除 GL 纹理的旧缓冲区?
标签: android android-mediacodec android-mediaprojection