【问题标题】:Android MediaCodec releaseOutputBuffer throws MediaCodec.CodecException when decoding H264 videoAndroid MediaCodec releaseOutputBuffer在解码H264视频时抛出MediaCodec.CodecException
【发布时间】:2016-02-04 17:21:53
【问题描述】:

我正在使用MediaCodec API 以SurfaceView 作为输出表面来解码 H264 视频流。解码器配置成功,没有任何错误。当我尝试使用releaseOutputBuffer(bufferIndex, true) 最终将解码的视频帧渲染到SurfaceView 上时,它会抛出MediaCodec.CodecException,但是视频渲染正确。

在异常对象上调用 getDiagnosticInfo() 和 getErrorCode() 返回错误代码 -34,但我在文档中找不到此错误代码的含义。文档也非常不清楚何时抛出此异常。

以前有没有人遇到过这个异常/错误代码?我该如何解决这个问题?

PS:虽然视频运行良好,但每次releaseOutputBuffer(bufferIndex, true), 调用都会引发此异常。

【问题讨论】:

    标签: android video decode android-mediacodec


    【解决方案1】:

    Android 媒体编解码器非常依赖设备供应商。三星存在令人难以置信的问题,其他运行相同代码的设备也能正常运行。这就是我过去 6 个月的生活。

    尽管感觉不对,但最好的方法是尝试 + 捕获 + 重试。 MediaCodec 将在 4 个不同的地方引发异常:

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

    注意:我的代码在 Xamarin 中,但调用映射非常接近原始 java。

    配置格式描述的方式也很重要。如果您不指定,媒体编解码器可能会在 NEXUS 设备上崩溃:

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

    当您发现任何异常时,您需要确保媒体编解码器已重置。不幸的是,旧 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
    

    一旦您在传递新输入时发现错误,您就可以检查:

    if (!DroidDecoder.IsRunning && streamView != null && streamView.VideoLayer.IsAvailable) {
            DroidDecoder.StartDecoder(streamView.VideoLayer.SurfaceTexture);
    }
    
    DroidDecoder.DecodeH264FrameBuffer(payload, payloadSize, frameDuration, presentationTime, isKeyFrame);
    

    渲染到纹理视图似乎是目前最稳定的选项。但是设备碎片化确实在这方面伤害了android。我们发现 Tesco Hudl 等更便宜的设备是最稳定的视频设备。甚至 1 次屏幕上最多有 21 个并发视频。三星 S4 可以达到 4-6 左右,具体取决于分辨率/fps,但 HTC 之类的东西可以和 Hudl 一样好用。这给我敲响了警钟,让我意识到三星设备实际上是在抄袭苹果的设计,并在玩弄 android-sdk 并且实际上在此过程中破坏了很多功能。

    【讨论】:

    • @redbain 我同意你的意见。三星通常是最不稳定的。他们完全破坏了他们在 Android 5.0 中的 OpenMAX 实现。
    • 在我的情况下,每次 ReleaseOutputBuffer 调用都会引发 MediaCodec 异常。所以我不认为在每一帧之后重新启动解码器是一种选择。
    • @nangal.vivek 你在解析 NALU 段吗?您必须在流中解析我们的 nalu 片段,并确保它们是附件 b 格式。即 0x00,0x00,0x00,0x01,。可能是问题。我不知道您是否可以为此使用媒体提取器,在我的情况下,我们正在解析实时网络提要​​。
    【解决方案2】:

    这很可能是您使用的编解码器的问题。尝试使用类似的东西来

    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;
    }
    

    然后设置您的编码器:

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

    这可以通过确保您选择的编解码器得到完全支持来解决您的错误。

    【讨论】:

    • 这不能解决问题。
    • 从错误码-34有什么可以推断出来的吗?
    • 不幸的是我找不到。您可能需要查看 MediaCodec 的源代码,以找到该代码中抛出 -34 的位置。
    猜你喜欢
    • 2013-04-25
    • 2019-06-21
    • 2021-08-30
    • 2014-02-06
    • 2017-12-16
    • 2021-07-29
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多