简体   繁体   English

MediaCodec 原始 H264 解码问题 - 超时并且decoder.dequeueOutputBuffer 总是返回 -1

[英]MediaCodec raw H264 decoding issue - times out and decoder.dequeueOutputBuffer always return -1

Firstly, thanks for taking time to read this.首先,感谢您花时间阅读本文。 I need some help or insights as I'm facing decoding issue with h264 frames.我需要一些帮助或见解,因为我正面临 h264 帧的解码问题。 I'm posting this question as most of other related posts to this do not provide clear steps.我发布这个问题是因为与此相关的大多数其他相关帖子都没有提供明确的步骤。 I've shared the code for decoder thread which has got the main logic.我已经共享了具有主要逻辑的解码器线程的代码。 I get the sps, pps and data from network and thus I make this mConfigured flag as true when sps and pps are received and respective arrays are fed.我从网络获取 sps、pps 和数据,因此当接收到 sps 和 pps 并馈送相应的数组时,我将此mConfigured标志设为 true。 I can provide more info if needed.如果需要,我可以提供更多信息。 PS - Please excuse for the coding standards as it is still a POC in development. PS - 请原谅编码标准,因为它仍然是一个正在开发的 POC。

decoder.dequeueOutputBuffer >> always return -1 decoder.dequeueOutputBuffer >> 总是返回 -1

//below function called from network when new packet received. //收到新数据包时从网络调用以下函数。

public void decodeAndPlayVideoFrame(byte[] encodedData, int frameType) {
    if (frameType == 1) {
        SPS = encodedData;
    } else if (frameType == 2) {
        PPS = encodedData;
    } else if (frameType == 0) {
        Log.d("EncodeDecode", "enqueueing frame no: " + (frameID));

        try {
            Frame frame = new Frame(frameID);
            int totalDataLength = encodedData.length + SPS.length + PPS.length;
            frame.frameData = new byte[totalDataLength];
            System.arraycopy(SPS, 0, frame.frameData, 0, SPS.length);
            System.arraycopy(PPS, 0, frame.frameData, SPS.length, PPS.length);
            System.arraycopy(encodedData, 0, frame.frameData, SPS.length + PPS.length, encodedData.length);

            queue.add(frame);
            frameID++;
        } catch (NullPointerException e) {
            Log.e("EncodeDecode", "frame is null");
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            Log.e("EncodeDecode", "problem inserting in the queue");
            e.printStackTrace();
        } catch (IllegalStateException e) {
            Log.e("EncodeDecode", "problem inserting in the queue");
            e.printStackTrace();
        }
        Log.d("EncodeDecode", "frame enqueued. queue size now: " + queue.size());

    }
}

// Player thread starts as screen launches and process packets fed into queue. // 播放器线程在屏幕启动时启动并处理送入队列的数据包。

  private class PlayerThread extends Thread {
    //private MediaExtractor extractor;
    private MediaCodec decoder;
    private Surface surface;
    private boolean mConfigured;

    public PlayerThread(Surface surface) {
        this.surface = surface;
    }

    private void initCodec() throws IOException {

        MediaFormat mediaFormat = null;

        decoder = MediaCodec.createDecoderByType("video/avc");
        mediaFormat = MediaFormat.createVideoFormat("video/avc",
                320,
                240);

        try {

            decoder.configure(mediaFormat,
                    surface,
                    null,
                    0);
            frameID = 0;
            mConfigured = true;
            decoder.start();
            Log.d("EncodeDecode", "DECODER_THREAD:: decoder.start() called");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        while (SPS == null || PPS == null || SPS.length == 0 || PPS.length == 0) {
            try {
                Log.d("EncodeDecode", "DECODER_THREAD:: sps,pps not ready yet");
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();

            }
        }

        if (!mConfigured) {
            try {
                initCodec();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
       int i = 0;
        ByteBuffer[] inputBuffers = decoder.getInputBuffers();
        ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
        while (!Thread.interrupted()) {
            Frame currentFrame = null;
            try {
                Log.d("EncodeDecode", "DECODER_THREAD:: calling queue.take(), if there is no frame in the queue it will wait");
                currentFrame = queue.take();
            } catch (InterruptedException e) {
                Log.e("EncodeDecode", "DECODER_THREAD:: interrupted while PlayerThread was waiting for the next frame");
                e.printStackTrace();
            }

            if (currentFrame == null)
                Log.e("EncodeDecode", "DECODER_THREAD:: null frame dequeued");
            else
                Log.d("EncodeDecode", "DECODER_THREAD:: " + currentFrame.id + " no frame dequeued");

            if (currentFrame != null && currentFrame.frameData != null && currentFrame.frameData.length != 0) {
                Log.d("EncodeDecode", "DECODER_THREAD:: decoding frame no: " + i + " , dataLength = " + currentFrame.frameData.length);

                int inIndex = decoder.dequeueInputBuffer(-1);

                if (inIndex >= 0) {
                    Log.d("EncodeDecode", "DECODER_THREAD:: sample size: " + currentFrame.frameData.length + "->" + currentFrame.frameData[0] +  "to" + currentFrame.frameData[currentFrame.frameData.length-1]);

                    ByteBuffer buffer = inputBuffers[inIndex];
                    buffer.clear();
                    buffer.put(currentFrame.frameData);
                    decoder.queueInputBuffer(inIndex, 0, currentFrame.frameData.length, 0, 0);

                    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                    int outIndex = decoder.dequeueOutputBuffer(info, 0);

                    switch (outIndex) {
                        case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                            Log.e("EncodeDecode", "DECODER_THREAD:: INFO_OUTPUT_BUFFERS_CHANGED");
                            outputBuffers = decoder.getOutputBuffers();
                            break;
                        case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                            Log.e("EncodeDecode", "DECODER_THREAD:: New format " + decoder.getOutputFormat());
                            break;
                        case MediaCodec.INFO_TRY_AGAIN_LATER:
                            Log.e("EncodeDecode", "DECODER_THREAD:: dequeueOutputBuffer timed out!");
                            break;
                        default:
                            Log.d("EncodeDecode", "DECODER_THREAD:: decoded SUCCESSFULLY!!!");
                            ByteBuffer outbuffer = outputBuffers[outIndex];
                            decoder.releaseOutputBuffer(outIndex, true);
                            break;
                    }

                    try {
                        Thread.sleep(250);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    i++;
                }
            }
        }

        decoder.stop();
        decoder.release();
        mConfigured = false;

    }
}

I do not know how do you get SPS and PPS and how do you check them...anyway you have to set SPS and PPS to mediaformat before starting the decodec, in your code, you can do something like :我不知道您如何获得 SPS 和 PPS 以及如何检查它们……无论如何,您必须在开始解码之前将 SPS 和 PPS 设置为 mediaformat,在您的代码中,您可以执行以下操作:

   ....
   while (SPS == null || PPS == null || SPS.length == 0 || PPS.length == 0) {
        try {
            Log.d("EncodeDecode", "DECODER_THREAD:: sps,pps not ready yet");
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
    }

   if (!mConfigured) {
        try {
            // init with SPS and PPS
            initCodec(sps,pps);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
   .....

And then set codec specific datas in initCodec before starting the decodec :然后在启动initCodec之前在initCodec设置编解码器特定数据:

private void initCodec(byte[] sps, byte[] pps) throws IOException {

        MediaFormat mediaFormat = null;

        decoder = MediaCodec.createDecoderByType("video/avc");
        mediaFormat = MediaFormat.createVideoFormat("video/avc",
                320,
                240);
        // set the codec specific datas cds-0 = SPS, cds-1 = PPS
        mediaFormat.setByteBuffer("cds-0", ByteBuffer.wrap(sps));
        mediaFormat.setByteBuffer("cds-1", ByteBuffer.wrap(pps));
        try {

            decoder.configure(mediaFormat,
                    surface,
                    null,
                    0);
            frameID = 0;
            mConfigured = true;
            decoder.start();
            Log.d("EncodeDecode", "DECODER_THREAD:: decoder.start() called");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    ...........

Update: From the doc更新:来自文档

...Many decoders require the actual compressed data stream to be preceded by "codec specific data", ie setup data used to initialize the codec such as PPS/SPS in the case of AVC video or code tables in the case of vorbis audio. ...许多解码器需要在实际压缩数据流之前加上“编解码器特定数据”,即用于初始化编解码器的设置数据,例如 AVC 视频中的 PPS/SPS 或 vorbis 音频中的代码表。 The class MediaExtractor provides codec specific data as part of the returned track format in entries named "csd-0", "csd-1" ... MediaExtractor 类在名为“csd-0”、“csd-1”的条目中提供编解码器特定数据作为返回轨道格式的一部分...

These buffers can be submitted directly after start() or flush() by specifying the flag BUFFER_FLAG_CODEC_CONFIG.这些缓冲区可以通过指定标志 BUFFER_FLAG_CODEC_CONFIG 在 start() 或 flush() 之后直接提交。 However, if you configure the codec with a MediaFormat containing these keys, they will be automatically submitted by MediaCodec directly after start.但是,如果您使用包含这些键的 MediaFormat 配置编解码器,它们将在启动后由 MediaCodec 直接自动提交。 > Therefore, the use of BUFFER_FLAG_CODEC_CONFIG flag is discouraged and is recommended only for advanced users . > 因此,不鼓励使用 BUFFER_FLAG_CODEC_CONFIG 标志,仅建议高级用户使用

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

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