android - MediaCodec 同时编解码

标签 android opengl-es android-mediacodec

我正在尝试使用 GPU 将效果应用于视频的帧,然后将这些帧重新编码为新的结果视频。

为了提高性能,我实现了以下流程:

有 3 个不同的线程,每个线程都有自己的 OpenGL 上下文。这些上下文的设置方式是在它们之间共享纹理。

线程 1 从视频中提取帧并将它们作为纹理保存在 GPU 内存中,类似于 this 示例。

线程 2 使用 GPUImage 的修改版本处理纹理,该版本还在 GPU 内存中输出纹理。

最后,线程 3 将从线程 2 获得的纹理写入一个新的视频文件,类似于 here 中描述的方法

帧顺序使用线程 1 和 2 之间以及线程 2 和 3 之间的队列来维护。纹理在用于处理/写入之后从内存中手动删除。

此流程的重点是将每个进程分开,希望最终性能是 3 个线程中最慢的那个。

问题:

最终视频是 90% 的黑帧,只有一部分是正确的。

我检查了提取和处理的各个结果,它们按预期工作。另请注意,3 个线程中描述的 3 个组件在单个线程中可以很好地协同工作。

我已尝试同步线程 1 和线程 3,在为线程 1 添加额外的 100 毫秒休眠时间后,视频效果很好,可能有 1 或 2 个黑帧。在我看来,解码器和编码器的两个实例无法同时工作。

我将使用任何额外要求的详细信息来编辑这篇文章。

最佳答案

在 OpenGL ES 上下文之间共享纹理需要小心。 Grafika's 中的实现方式“show + capture camera” Activity 坏了;见this issue了解详情。基本问题是当纹理更新时,你基本上需要发出内存屏障;实际上,这意味着在生产者端发出 glFinish(),并在消费者端重新绑定(bind)纹理,并在 同步 block 中完成所有这些操作。

如果您可以在单个线程上完成所有 GLES 工作,您的生活将会更简单(也更高效)。根据我的经验,同时激活多个 GLES 上下文是不明智的,您可以通过寻找替代方案来避免一些痛苦。

你可能想要更像这样的东西:

  • 线程 #1 读取文件并将帧送入 MediaCodec 解码器。解码器将输出发送到 SurfaceTexture Surface。
  • 线程 #2 具有 GLES 上下文。它创建了线程 #1 将输出发送到的 SurfaceTexture。它处理图像并在 MediaCodec 编码器的 Surface 上呈现输出。
  • 创建 MediaCodec 编码器的线程 #3 正在等待编码输出。当接收到输出时,它被写入磁盘。请注意,MediaMuxer 的使用可能会停止;见this blog post了解更多。

在所有情况下,线程(以及在后台,进程)之间的唯一通信是通过 Surface 完成的。 SurfaceTexture 和 MediaCodec 实例是从单个线程创建和使用的;只有生产者端点(Surface)被传递。

一个潜在的麻烦点是流量控制——如果你喂得太快,SurfaceTextures 会掉帧。根据情况,组合线程 #1 和 #2 可能有意义。

关于android - MediaCodec 同时编解码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37156318/

相关文章:

android - 如何在屏幕方向期间保持解码?

android - 日历事件 action_insert 上的 startActivityForResult 未返回到应用程序

android - Kotlin 和 Android Studio - Unresolved reference : FirebaseInstanceId

android - 如何实现这种布局

Android OpenGLES 滚动背景

android - MediaRecorder 捕获的音频文件在发送到服务器后损坏

android - native_window_api_connect 返回错误 : Invalid argument (-22)

android - 如何查看短信是否真的发送?

android - 什么opengl es 2没有设置矩阵模式的方法?

ios - OpenGL ES - 降低纹理质量和纹理大小