Android 使用 MediaCodec 和 Surface 进行编码

Android encoding using MediaCodec and a Surface

我一直在通过 MediaCodec 将视频直接渲染到从我的 UI 中的 SurfaceView 获取的 Surface。这很好用。

我现在正在尝试使用 MediaCodec 作为编码器。作为测试,我想渲染到 Surface(如上所示)并通过配置为编码器的不同 MediaCodec 实例进行环回。

我看到了编码器的 createInputSurface() 方法。我想我希望编码器创建这个表面,然后让解码器 MediaCodec 使用它作为要绘制的表面。首先,这可能吗?

其次,我不确定如何从编码器创建的 Surface 创建 SurfaceView。我只从 SurfaceView 中提取了一个 Surface,但我没有从文档中看到如何反向执行此操作。

表面是生产者-消费者安排的 "producer" 端。一般来说,API以消费者为中心,消费者创建两端,然后将生产者接口(Surface)交还给你。

因此,对于 SurfaceView 或 MediaCodec 编码器,您创建对象并获取其 Surface。然后使用 Canvas、OpenGL ES 或 MediaCodec 解码器向它们发送图形数据缓冲区。

无法将编码器的输入表面用作 SurfaceView 的显示表面——它们是两个不同的管道。 SurfaceView 的消费者位于系统合成器 (SurfaceFlinger) 中,这就是为什么您必须等待 "surface created" 回调触发的原因。 MediaCodec 编码器的消费者在 mediaserver 进程中,虽然异步性被更好地隐藏了。

将 MediaCodec 解码器输出发送到 SurfaceView 非常简单,将输出发送到 MediaCodec 编码器也是如此。如您所料,只需将编码器的输入 Surface 传递给解码器即可。当您想同时做这两件事时,生活就会变得有趣。

Surface 的底层代码(称为 BufferQueue)应该能够(从 Lollipop 开始)多路复用,但我不知道 Lollipop 中的 API 向应用程序公开了该功能。这意味着您在做事时遇到困难。

困难的方法涉及创建 SurfaceTexture (a/k/a GLConsumer),它是管道的消费者端。您可以使用 sole constructor 从中创建一个 Surface。你把它交给 MediaCodec 解码器。现在出来的每一帧都会被 SurfaceTexture 转换为 GLES 纹理。您可以将它们渲染到 SurfaceView 和编码器的输入 Surface。

您可以在 Grafika, and a longer explanation of the mechanics in the graphics architecture doc 中找到各种示例。