延迟Android虚拟显示的帧

时间:2016-03-22 22:12:27

标签: android android-ndk mediacodec surface

我想解决的基本问题是将发送到虚拟显示器的内容延迟一秒左右。所以基本上,我试图在初始录制后将所有帧移动1秒。注意,表面用作输入,而另一表面用作通过该虚拟显示器的输出。我最初的预感是探索一些想法,因为修改Android框架或使用非公共API是好的。 Java或本机C / C ++很好。

a)我尝试在 SurfaceFlinger 中将发布到虚拟显示器或输出表面的帧延迟一两秒。这不起作用,因为它会导致所有曲面延迟相同的时间(帧的同步处理)。

b) MediaCodec 使用曲面作为输入进行编码,然后生成解码数据。无论如何使用MediaCodec,它实际上不编码,只生成未编码的原始帧?似乎不太可能。此外,MediaCodec如何做到这一点?逐帧处理事物。如果我可以推断该方法,我可以从输入表面逐帧提取并创建一个延迟了我需要的时间量的环形缓冲区。

c)软件解码器(例如 FFmpeg )如何在Android中实际执行此操作?我假设它们采用表面但是它们将如何逐帧推断和处理

请注意,我当然可以编码和解码来检索帧并发布它们,但我想避免实际解码。请注意,修改Android框架或使用非公共API很好。

我也发现了这个:Getting a frame from SurfaceView

似乎选项d)可能正在使用SurfaceTexture,但我想避免编码/解码过程。

1 个答案:

答案 0 :(得分:2)

据我所知,你有一个虚拟显示器将其输出发送到Surface。如果您只使用SurfaceView输出,则虚拟显示器输出的帧会立即显示在物理显示屏上。目标是在虚拟显示器生成帧和Surface消费者接收帧之间引入一秒延迟,以便(再次使用SurfaceView作为示例)物理显示器在一秒钟之后显示所有内容。

基本概念很简单:将虚拟显示输出发送到SurfaceTexture,并将帧保存到循环缓冲区中;同时另一个线程是从循环缓冲区的尾端读取帧并显示它们。这个问题就是@AdrianCreţu在评论中指出的:60fps的全分辨率屏幕数据的一秒将占用设备内存的很大一部分。更不用说复制那么多数据会相当昂贵,而且有些设备可能无法跟上。

(无论您是在应用程序中还是在SurfaceFlinger中执行此操作...最多60个屏幕大小的帧的数据必须某处一整秒。)

您可以通过各种方式减少数据量:

  • 降低分辨率。将2560x1600缩放到1280x800可移除3/4像素。在大多数显示器上应该很难注意到质量损失,但这取决于您正在查看的内容。
  • 减少颜色深度。从ARGB8888切换到RGB565将把尺寸减小一半。但这会很明显。
  • 降低帧速率。您正在为虚拟显示生成帧,因此您可以选择更慢地更新它。动画在30fps时仍然相当平滑,内存需求减半。
  • 应用图像压缩,例如PNG或JPEG。没有硬件支持,相当有效,但速度太慢。
  • 编码帧间差异。如果帧与帧之间的变化不大,则增量变化可能非常小。像VNC这样的桌面镜像技术就是这样做的。在软件方面做得有点慢。

像AVC这样的视频编解码器将压缩帧并编码帧间差异。这就是你如何将1GByte / sec降低到10Mbit / sec并且看起来还不错。

例如,考虑Grafika中的“连续捕获”示例。它将Camera输出馈送到MediaCodec编码器,并将H.264编码的输出存储在环形缓冲区中。当你点击“捕获”时,它会保存最后7秒。这可以很容易地以7秒的延迟播放相机输入,并且它只需要几兆字节的内存即可。

“screenrecord”命令可以在ADB连接上转储H.264输出或原始帧,但实际上ADB的速度不足以跟上原始帧(即使在微小的显示器上)。它没有做任何你不能从应用程序做的事情(现在我们有mediaprojection API),所以我不建议将它用作示例代码。

如果您还没有,请仔细阅读graphics architecture doc

相关问题