简体   繁体   中英

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. 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.

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. 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.

Android media-codec is very dependant on the device vendor. Samsung is incredibly problematic other devices running the same code will run fine. This has been my life for the last 6 months.

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:

  1. Configuration - NativeDecoder.Configure(...);
  2. Start - NativeDecoder.Start();
  3. Render output - NativeDecoder.ReleaseOutputBuffer(...);
  4. Input - codec.QueueInputBuffer(...);

NOTE: my code is in Xamarin but the calls map very closely to raw java.

The way you configure your format description also matters. The media-codec can crash on NEXUS devices if you don't specify:

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:

    #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. We have found cheaper devices such as a the Tesco Hudl to be of the most stable for video. Even had up to 21 concurrent videos on screen at 1 time. Samsung S4 can get around 4-6 depending on the resolution/fps but something like the HTC can work as well as the 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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