Android 调整虚拟显示器大小

5
我对Android的MediaProjection API有一些问题(实际上更多,但这些是比较关键的问题)。阅读图形架构并不能真正帮助我,所以我只是想了解是否在我的代码流程中跳过了某些步骤。
假设:
1. 我有一个专用的GL渲染线程,已初始化,并生成了一个GL纹理。我为纹理设置了默认的缓冲区大小为WxH。 2. 我使用GL纹理创建SurfaceTexture和此表面纹理的Surface。 3. 通过MediaProjection创建大小为WxH的虚拟显示,并将其表面设置为上述表面。
问题1:一切都工作得很好(完整的帧正确地出现),或者不工作(所有帧都是黑色的;或例如每个帧的一半可见 - 对于所有帧都是相同的一半;或屏幕的某些部分重复到其他部分,有时甚至会扭曲)。
问题2:在全屏GL游戏中花费时间后,经过固定时间(约4分钟),所有传入的帧都会冻结(例如,我接收到“新”帧,但它实际上是同一幅图像)。使用glReadPixels阅读可以确认结果 - 问题是,实际显示已经超过了该帧。唯一强制“恢复”的方法是打开状态栏或导航栏,这会立即开始发送正确的帧。当然,在另外4分钟后,它会再次发生...
问题3:在调用VirtualDisplay上的resize()之后,在将setDefaultBUfferSize()也称为GL纹理之后,90%的情况下会出现问题#1(黑色/切割帧,其他屏幕区域的伪影...)
我正在使用updateTextureImage - > GL纹理绘制相同线程中的调用序列,因此我的正常理解是,我永远不应该以某种方式从半满的GL缓冲区中读取,或者什么的...对吗?
我还通过直接将VirtualDisplay渲染到MediaCodec的表面(没有自定义GL)来测试此问题 - 相同的行为。
更新:实际上,由于MediaCodec具有固定大小的创建表面,因此无法重现错误,因为我们只能调整虚拟显示的大小,但不能调整编码器的表面大小,因此这并不是真正的错误(但即使像这样,VirtualDisplay也可以以某种方式调整表面大小)。
我觉得在关闭虚拟显示或在创建VirtualDisplay之间未正确初始化时会泄漏某些东西,因为它不一致。很可能发生全新的MediaProjection屏幕捕获权限,具有全新的虚拟显示和刚创建的表面纹理,最终只会给我提供一半切割的帧...让我大吃一惊...
PS:所有这些都发生在Android 6.0.1上的Nexus 6上。

1
听起来有点像 https://github.com/google/grafika/issues/43,因为它运行了 N 分钟然后停止更新。提交错误报告的人发现了一个奇怪的解决方法。您是否使用单个 EGL 上下文?看到屏幕部分重复通常是 GPU - fadden
我和你的设置类似,我的问题似乎只在屏幕旋转和尝试调整虚拟显示器/表面纹理大小时发生(如此处所述)。这里是我看到的,还有一些屏幕“复制”。当我首次创建VirtualDisplay和Surface/SurfaceTexture时,对我来说没有问题。它不会在每次旋转时都发生,这让我相信这是一个竞争条件。我正在使用6.0.1上的Nexus 5x。 - EncodedNybble
@fadden - 当直接渲染到MediaCodec表面时,我无法控制glClear。在我的GL渲染器中,我调用glClear(用于信封带目的,在旋转时清除整个视口),然后呈现纹理(这应该是由最新的updateTextureImage调用更新的)。问题是纹理本身在其自身范围内是不完整的。正如EncodedNybble所提到的,当实际显示旋转正交于虚拟显示大小(当它自己缩放和居中)时,这种情况似乎更加频繁发生。 - Adrian Crețu
我注意到的另一件事是,关闭屏幕并在几秒钟后重新打开可能会解决问题。我怀疑纹理缓冲区大小存在问题,或者生产者没有使用正确大小的缓冲区,在调用texture.setDefaultBufferSize之后?也许它正在使用旧缓冲区?文档说相机生产者覆盖了大小,为什么VirtualDisplay也不这样做呢?如果我最初没有设置缓冲区大小,我认为什么都不会被渲染,如果我没记错的话。有没有办法强制删除GL纹理的旧缓冲区? - Adrian Crețu
1个回答

1
我放弃了,重新创建了虚拟显示器,使用了一个新的SurfaceTexture,但保留了GL纹理。这样就消除了闪烁和半尺寸帧。我还遇到了一些logcat警告,先前使用的SurfaceTexture的BufferQueue被放弃了,在调用VirtualDisplay的release()之后,还会得到1或2个onFrameAvailable回调(可能是异步的,并且应该等待stop()回调,但这时太复杂了)。
即使做了所有这些事情,仍然会发生非常奇怪的事情:有时候捕获只会捕获我的应用程序。如果我向下滑动状态栏或返回桌面,所有东西都会变黑(即使我的应用程序在状态栏窗口后面)。唯一的解决办法是关闭/打开屏幕,或将Camera / Camera2预览呈现到我的SurfaceTexture中,然后重新创建VirtualDisplay。关于纹理,底层肯定有问题。
看起来VirtualDisplay.resize()已经损坏,同样适用于使用现有SurfaceTexture创建新的Virtual Display。

很抱歉,我在这里没有得到与您相同的行为。即使使用新的VirtualDisplay、新的Surface和新的SurfaceTexture,甚至是新的OpenGL纹理(我确实收到了废弃警告),当我尝试在方向更改后创建一个新的VirtualDisplay时,仍然会出现我之前提到的“灰色条”和奇怪的撕裂/重复/较小的问题。我同意,在这些设备上,6.0.1的VirtualDisplay似乎在处理最近旋转的调整或显示时存在问题。 - EncodedNybble
我不会冒险在设备旋转时重新创建VirtualDisplay :) 要小心,不要在任何已释放的纹理上调用updateTexImage,这种情况发生在我身上(由于onFrameAvailable仍在被调用),所有随后的帧都因某种原因闪烁。我保留GL纹理以防止任何“外部oes”重新绑定(并且也不会搞乱线程,我只是在主/任何线程上重新创建surfacetexture),也许这与此有关? - Adrian Crețu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接