使用 MediaCodec 和 Surface 进行 Android 编码

Posted

技术标签:

【中文标题】使用 MediaCodec 和 Surface 进行 Android 编码【英文标题】:Android encoding using MediaCodec and a Surface 【发布时间】:2015-12-03 02:02:33 【问题描述】:

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

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

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

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

【问题讨论】:

【参考方案1】:

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

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

没有办法获取编码器的输入 Surface 并将其用作 SurfaceView 的显示 Surface——它们是两个不同的管道。 SurfaceView 的消费者位于系统合成器 (SurfaceFlinger) 中,这就是为什么您必须等待“表面创建”回调触发的原因。 MediaCodec编码器的消费者在mediaserver进程中,虽然异步性比较隐蔽。

将 MediaCodec 解码器输出发送到 SurfaceView 很简单,就像将输出发送到 MediaCodec 编码器一样。正如您所猜测的,只需将编码器的输入 Surface 传递给解码器。生活变得有趣的地方是你想同时做这两件事。

Surface 的底层代码(称为 BufferQueue)应该能够(如 Lollipop)进行多路复用,但我不知道 Lollipop 中的 API 会将这种能力暴露给应用程序。这意味着您在做事时遇到困难。

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

您可以在Grafika 中找到各种示例,在graphics architecture doc 中可以找到更详细的机制说明。

【讨论】:

我现在正在使用 MediaCodec 将流解码到 SurfaceView。这很好用。使用 MediaCodec 作为编码器让我感到困惑的部分是我需要向编解码器询问表面(createSurface)。我是否使用编码器创建表面并将该表面引用提供给要使用的解码器?还是我必须在解码 Surface 像素和编码器创建的像素之间执行复制? 是... 将解码器的输出直接发送到编码器,首先使用createInputSurface()创建编码器的Surface,然后在配置时将其交给解码器。不需要手动复制。 如果我想在表面上查看解码器实例的输出,这是否意味着我必须使用另一个 SurfaceView/SurfaceTexture 实例,因为编码器会为其功能创建不同的表面? 您可以将解码器的输出发送到单个 Surface。这意味着您可以查看它或对其进行编码,但不能同时查看它,除非您做额外的工作(例如,使用答案中提到的 SurfaceTexture)。 嗯。如果我使用编码器创建的 Surface,我的解码器实例会在 dequeueOutputBuffer 处引发 IllegalStateException。如果我在从 SurfaceView 派生的 Surface 中换回,我不会收到此异常并且它按预期工作。所有的事情都和我上一个一样。在解码端工作的代码版本。

以上是关于使用 MediaCodec 和 Surface 进行 Android 编码的主要内容,如果未能解决你的问题,请参考以下文章

如何将相机预览传递给 MediaCodec.createInputSurface() 创建的 Surface?

Android MediaCodec硬件解码视频播放

Android屏幕镜像一:屏幕采集 + MediaCodec编码

使用MediaCodec进行转码会导致绿色和混乱的视频

Android MediaCodec

给Android工程师的音视频教程之一文弄懂MediaCodec