其他分享
首页 > 其他分享> > Android MediaCodec在异步模式下编码和解码

Android MediaCodec在异步模式下编码和解码

作者:互联网

我正在尝试解码文件中的视频,并使用Media Level 21C及更高版本支持的新异步模式中的MediaCodec将其编码为不同的格式(Android OS 5.0 Lollipop).

在诸如Big Flake,Google的Grafika等网站上的同步模式中有很多例子,以及StackOverflow上的几十个答案,但它们都不支持异步模式.

我不需要在此过程中显示视频.

我相信一般的过程是使用MediaExtractor作为MediaCodec(解码器)的输入来读取文件,允许Decoder的输出呈现为Surface,也是MediaCodec(编码器)的共享输入,然后最后通过MediaMuxer编写编码器输出文件. Surface是在编码器设置期间创建的,并与解码器共享.

我可以将视频解码为TextureView,但是使用编码器而不是屏幕共享Surface并不成功.

我为我的两个编解码器设置了MediaCodec.Callback().我认为问题在于我不知道在Encoder的回调的onInputBufferAvailable()函数中该怎么做.我不知道(或知道如何)将数据从Surface复制到编码器中 – 这应该自动发生(就像使用codec.releaseOutputBuffer(outputBufferId,true)在Decoder输出上完成的那样;).但是,我相信onInputBufferAvailable需要调用codec.queueInputBuffer才能运行.我只是不知道如何设置参数而不从像Decode端使用的MediaExtractor那样获取数据.

如果你有一个打开视频文件的示例,解码它,使用异步MediaCodec回调将其编码为不同的分辨率或格式,然后将其保存为文件,请分享您的示例代码.

===编辑===

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

Android MediaCodec

解决方法:

我相信你不需要在编码器的onInputBufferAvailable()回调中做任何事情 – 你不应该调用encoder.queueInputBuffer().就像在同步模式下进行Surface输入编码时从不手动调用encoder.dequeueInputBuffer()和encoder.queueInputBuffer()一样,也不应该在异步模式下执行.

当你调用decoder.releaseOutputBuffer(outputBufferId,true); (在同步和异步模式下),内部(使用您提供的Surface)将输入缓冲区从曲面中取出,将输出渲染到其中,然后将其排列回曲面(到编码器).同步模式和异步模式之间的唯一区别在于缓冲区事件在公共API中的暴露方式,但在使用Surface输入时,它使用不同的(内部)API来访问它,因此同步模式和异步模式应该不重要这一点.

所以据我所知(尽管我自己没有尝试过),你应该只为编码器留下onInputBufferAvailable()回调.

编辑:
所以,我自己尝试这样做,它(几乎)就像上面描述的一样简单.

如果编码器输入表面直接配置为解码器的输出(中间没有SurfaceTexture),那么事情就会起作用,同步解码编码循环转换为异步解码.

但是,如果使用SurfaceTexture,则可能会遇到小问题.如何等待帧到达SurfaceTexture与调用线程有关的问题,请参阅https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java#106https://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以获取对此的引用.

就我所见,问题是在https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/OutputSurface.java#240中的awaitNewImage中.如果应该在主线程上调用onFrameAvailable回调,如果awaitNewImage调用也在主线程上运行,则会出现问题.如果onOutputBufferAvailable回调也在主线程上被调用并且你从那里调用awaitNewImage,那么我们就会遇到一个问题,因为你最终会等待一个回调(使用wait()来阻止整个线程)运行直到当前方法返回.

因此,我们需要确保onFrameAvailable回调与调用awaitNewImage的线程不同.一个非常简单的方法是创建一个新的单独线程,除了服务onFrameAvailable回调之外什么都不做.为此,您可以执行以下操作:这个:

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

我希望这足以让您能够解决您的问题,如果您需要我编辑其中一个公共示例以在那里实现异步回调,请告诉我.

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

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

并在渲染之前将其重新附加到另一个线程中:

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

EDIT3:
另外,如果在同一线程上接收到编码器和解码器回调,则执行渲染的解码器onOutputBufferAvailable可以阻止编码器回调被传递.如果它们未被传递,则可以无限制地阻止渲染,因为编码器没有获得返回的输出缓冲区.这可以通过确保在不同的线程上接收视频解码器回调来解决,这避免了onFrameAvailable回调的问题.

我尝试在ExtractDecodeEditEncodeMuxTest之上实现所有这些,并使其工作看起来很好,看看https://github.com/mstorsjo/android-decodeencodetest.我最初导入了未更改的测试,并进行了异步模式的转换并分别修复了棘手的细节,使其变得容易查看提交日志中的各个修复程序.

标签:video-encoding,mediacodec,android,android-5-1-1-lollipop,android-5-0-lollipop
来源: https://codeday.me/bug/20191003/1851158.html