Android MediaCodec在asynchronous模式下进行编码和解码

我试图解码一个文件中的video,并将其编码为与MediaCodec不同的格式,支持API Level 21及更高版本(Android OS 5.0 Lollipop)的新asynchronous模式

在诸如Big Flake ,Google的Grafika以及StackOverflow上的几十个答案等站点上,有许多同步模式的例子,但是它们都不支持asynchronous模式。

在这个过程中,我不需要显示video。

我相信一般的过程是用MediaExtractor读取文件作为MediaCodec (解码器)的input,允许解码器的输出渲染到也是MediaCodec (编码器)的共享input的Surface中,然后最后通过MediaMuxer编写Encoder输出文件。 Surface是在编码器设置过程中创build的,并与解码器共享。

我可以把video解码成一个TextureView ,但是用编码器而不是屏幕共享Surface还没有成功。

我为我的两个编解码器设置了MediaCodec.Callback() 。 我相信一个问题是我不知道在编码器的callback的onInputBufferAvailable()函数中onInputBufferAvailable() 。 我不知道如何(或知道如何)将数据从Surface复制到编码器中 – 这应该是自动发生的(就像在解码器输出上用codec.releaseOutputBuffer(outputBufferId, true); )所做的那样)。 然而,我相信onInputBufferAvailable需要调用codec.queueInputBuffer才能正常工作。 我只是不知道如何设置参数,而不像解码端使用的MediaExtractor那样获取数据。

如果您有一个打开video文件示例,对其进行解码,使用asynchronousMediaCodeccallback将其编码为不同的分辨率或格式,然后将其另存为文件 ,请分享您的示例代码。

=== 编辑 ===

这是一个在asynchronous模式下我正在尝试做同步模式的工作示例:ExtractDecodeEditEncodeMuxTest.java: https ://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media /src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java这个例子是在我的应用程序

Android MediaCodec

Solutions Collecting From Web of "Android MediaCodec在asynchronous模式下进行编码和解码"

我相信你不需要在编码器的onInputBufferAvailable()callback中做任何事情 – 你不应该调用encoder.queueInputBuffer() 。 就像您在同步模式下执行Surfaceinput编码时从不手动调用encoder.dequeueInputBuffer()encoder.queueInputBuffer() ,也不应该在asynchronous模式下进行。

当你调用decoder.releaseOutputBuffer(outputBufferId, true); (在同步和asynchronous模式下),内部(使用你提供的Surface )从表面出列input缓冲区,将输出渲染到其中,并将其排回到表面(到编码器)。 同步和asynchronous模式之间的唯一区别在于缓冲区事件如何在公共API中公开,但是在使用Surfaceinput时,它使用不同的(内部)API来访问相同的,所以同步模式和asynchronous模式应该无关紧要这一切。

所以据我所知(尽pipe我自己还没有尝试过),你应该把编码器的onInputBufferAvailable()callback留空。

编辑:所以,我试图自己做,这(几乎)如上所述简单。

如果编码器input表面被直接configuration为解码器的输出(没有SurfaceTexture),事情就会起作用,一个同步的解码 – 编码循环被转换成一个asynchronous的。

但是,如果您使用SurfaceTexture,则可能会碰到一个小问题。 有一个关于如何等待框架到调用线程的SurfaceTexture的问题,请参阅https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media /src/android/media/cts/DecodeEditEncodeTest.java#106和https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts /EncodeDecodeTest.java#104和https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/OutputSurface.java#113对此。

这个问题,据我awaitNewImage ,是在awaitNewImagehttps://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts /OutputSurface.java#240 。 如果应该在主线程上调用onFrameAvailablecallback函数,那么在主线程上运行awaitNewImage调用也会遇到问题。 如果onOutputBufferAvailablecallbackonOutputBufferAvailable也在主线程中调用, awaitNewImage从那里调用awaitNewImage ,那么我们有一个问题,因为你最终会等待一个callback(用一个阻塞整个线程的wait() ),运行直到当前的方法返回。

所以我们需要确保onFrameAvailablecallbackawaitNewImage与调用awaitNewImage 。 这样做的一个非常简单的方法是创build一个新的单独的线程,除了服务onFrameAvailablecallback。 要做到这一点,你可以做这样的事情:

  private HandlerThread mHandlerThread = new HandlerThread("CallbackThread"); private Handler mHandler; ... mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); ... mSurfaceTexture.setOnFrameAvailableListener(this, mHandler); 

我希望这足以让你解决你的问题,让我知道你是否需要我编辑其中一个公共示例来实现asynchronouscallback。

EDIT2:另外,由于GL渲染可能是在onOutputBufferAvailablecallback中完成的,所以这可能与设置EGL上下文的线程不同。 所以在这种情况下,需要在设置它的线程中释放EGL上下文,如下所示:

 mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 

在渲染之前将其重新附加在其他线程中:

 mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext); 

EDIT3:另外,如果编码器和解码器callback在同一个线程上被接收,那么解码器onOutputBufferAvailable进行渲染,可以阻止传递编码器callback。 如果它们没有被传送,则渲染可以无限地被阻塞,因为编码器不会得到返回的输出缓冲区。 这可以通过确保在不同的线程上接收video解码器callback来解决,而这可以避免使用onFrameAvailablecallback的问题。

我试着在ExtractDecodeEditEncodeMuxTest上实现所有这些,看起来很好,看看https://github.com/mstorsjo/android-decodeencodetest 。 我最初导入了未经修改的testing,并将其转换为asynchronous模式,并单独修复了棘手的细节,以便于查看提交日志中的单个修复程序。