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 中找到各种示例。
我一直在通过 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 中找到各种示例。