简体   繁体   English

解码H264视频时Android MediaCodec releaseOutputBuffer抛出MediaCodec.CodecException

[英]Android MediaCodec releaseOutputBuffer throws MediaCodec.CodecException when decoding H264 video

I am using MediaCodec API to decode a H264 video stream using a SurfaceView as the output surface.我正在使用MediaCodec API 使用SurfaceView作为输出表面解码 H264 视频流。 The decoder is configured successfully without any errors.解码器配置成功,没有任何错误。 When I try to finally render the decoded video frame onto the SurfaceView using releaseOutputBuffer(bufferIndex, true) , it throws MediaCodec.CodecException , however the video is rendered correctly.当我尝试最终使用releaseOutputBuffer(bufferIndex, true)将解码的视频帧渲染到SurfaceView ,它会抛出MediaCodec.CodecException ,但是视频渲染正确。

Calling getDiagnosticInfo() and getErrorCode() on the exception object return an error code of -34, but I can't find in the docs what this error code means.在异常对象上调用getDiagnosticInfo()getErrorCode()返回 -34 的错误代码,但我在文档中找不到此错误代码的含义。 The documentation is also very unclear about when this exception is thrown.文档也非常不清楚何时抛出此异常。

Has anyone faced this exception/error code before?有没有人遇到过这个异常/错误代码? How can I fix this?我怎样才能解决这个问题?

PS: Although the video works fine but this exeception is thrown at every releaseOutputBuffer(bufferIndex, true), call. PS:虽然视频工作正常,但在每个releaseOutputBuffer(bufferIndex, true),调用时都会抛出这个异常。

Android media-codec is very dependant on the device vendor. Android 媒体编解码器非常依赖于设备供应商。 Samsung is incredibly problematic other devices running the same code will run fine.三星的问题令人难以置信,其他运行相同代码的设备也能正常运行。 This has been my life for the last 6 months.这就是我过去 6 个月的生活。

The best approach to do although it can feel wrong is to try + catch + retry.尽管可能会感觉不对,但最好的方法是尝试 + 捕获 + 重试。 There are 4 distinct places where the MediaCodec will throw exceptions: MediaCodec 将在 4 个不同的地方抛出异常:

  1. Configuration - NativeDecoder.Configure(...);配置 - NativeDecoder.Configure(...);
  2. Start - NativeDecoder.Start();开始 - NativeDecoder.Start();
  3. Render output - NativeDecoder.ReleaseOutputBuffer(...);渲染输出 - NativeDecoder.ReleaseOutputBuffer(...);
  4. Input - codec.QueueInputBuffer(...);输入 - codec.QueueInputBuffer(...);

NOTE: my code is in Xamarin but the calls map very closely to raw java.注意:我的代码在 Xamarin 中,但调用非常接近原始 Java。

The way you configure your format description also matters.您配置格式描述的方式也很重要。 The media-codec can crash on NEXUS devices if you don't specify:如果您不指定,媒体编解码器可能会在 NEXUS 设备上崩溃:

formatDescription.SetInteger(MediaFormat.KeyMaxInputSize, currentPalette.Width * currentPalette.Height);

When you catch any exception you will need to ensure the mediacodec is reset.当您捕获任何异常时,您需要确保媒体编解码器已重置。 Unfortunatly reset isnt available to older api-levels but you can simulate the same effect with:不幸的是,较旧的 api 级别无法使用重置,但您可以使用以下方法模拟相同的效果:

    #region Close + Release Native Decoder

    void StopAndReleaseNativeDecoder() {
        FlushNativeDecoder();
        StopNativeDecoder();
        ReleaseNativeDecoder();
    }

    void FlushNativeDecoder() {
        if (NativeDecoder != null) {
            try {
                NativeDecoder.Flush();
            } catch {
                // ignore
            }
        }
    }

    void StopNativeDecoder() {
        if (NativeDecoder != null) {
            try {
                NativeDecoder.Stop();
            } catch {
                // ignore
            }
        }
    }

    void ReleaseNativeDecoder() {
        while (NativeDecoder != null) {
            try {
                NativeDecoder.Release();
            } catch {
                // ignore
            } finally {
                NativeDecoder = null;
            }
        }
    }

    #endregion

Once you catch the error when you pass new input you can check:在传递新输入时捕获错误后,您可以检查:

if (!DroidDecoder.IsRunning && streamView != null && streamView.VideoLayer.IsAvailable) {
        DroidDecoder.StartDecoder(streamView.VideoLayer.SurfaceTexture);
}

DroidDecoder.DecodeH264FrameBuffer(payload, payloadSize, frameDuration, presentationTime, isKeyFrame);

Rendering to a texture-view seems to be the most stable option currently.渲染到纹理视图似乎是目前最稳定的选项。 But the device fragmentation has really hurt android in this area.但是设备碎片化确实在这方面伤害了android。 We have found cheaper devices such as a the Tesco Hudl to be of the most stable for video.我们发现更便宜的设备(例如 Tesco Hudl)对于视频来说是最稳定的。 Even had up to 21 concurrent videos on screen at 1 time.甚至 1 次屏幕上最多可同时显示 21 个视频。 Samsung S4 can get around 4-6 depending on the resolution/fps but something like the HTC can work as well as the Hudl.三星 S4 可以达到 4-6 左右,具体取决于分辨率/fps,但像 HTC 这样的东西可以像 Hudl 一样工作。 Its been a wake up call and made me realise samsung devices are literally copying apple design and twiddling with the android-sdk and actually breaking a lot of functionality along the way.它敲响了警钟,让我意识到三星设备实际上是在复制苹果设计并使用 android-sdk,实际上在此过程中破坏了许多功能。

It is most probably an issue with the codec you are using.这很可能是您使用的编解码器的问题。 Try using something like this to尝试使用这样的东西

private static MediaCodecInfo selectCodec(String mime){
    int numCodecs = MediaCodecList.getCodecCount();

    for(int i = 0; i < numCodecs;  i++){
        MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
        if(!codecInfo.isEncoder()){
            continue;
        }

        String[] types = codecInfo.getSupportedTypes();
        for(int j = 0; j < types.length; j++){
            if(types[j].equalsIgnoreCase(mime)){
                return codecInfo;
            }
        }
    }
    return null;
}

And then setting your encoder with:然后设置您的编码器:

MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
mEncoder = MediaCodec.createCodecByName(codecInfo.getName());

That may resolve your error by ensuring that the Codec you've chosen is fully supported.这可以通过确保您选择的编解码器得到完全支持来解决您的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM