Android 软件解码器 (OMX.google.h264.decoder) 解码 H264 视频失败

Android Software Decoder (OMX.google.h264.decoder) Fails to decode H264 video

我在尝试使用 Android 软件解码器 (OMX.google.h264.decoder) 提取和解码 H264 视频时遇到问题。该问题似乎涉及多个设备。

此视频使用 Nexus 5 硬件解码器 (OMX.qcom.video.decoder.avc) 可以正常播放。

下面的示例代码展示了这个问题,是使用 android MediaCodec 和 MediaExtractor 类 的一个相当标准的示例。

我得到的异常是第一个缓冲区传递给解码器时的非法状态异常。

该视频采用 Baseline 配置文件编码为 720x480 20fps,因此应该符合兼容性准则。

Here is a video sample to accompany the code

如果能提供有关如何让软件视频解码器正确处理 H264 视频的任何指导,我将不胜感激。

public void doMp4Test()
{
    try
    {
        //String filename = "webserver_h264.mp4";
        String filename = "toodee-720p.mp4";
        MediaExtractor extractor = new MediaExtractor();
        extractor.setDataSource(Constants.RootDirectory + File.separator + filename); 

        MediaCodec decoder = null;

        for (int i = 0; i < extractor.getTrackCount(); i++)
        {
            MediaFormat format = extractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/"))
            {
                extractor.selectTrack(i);
                decoder = MediaCodec.createByCodecName("OMX.google.h264.decoder");
                // decoder = MediaCodec.createDecoderByType("OMX.qcom.video.decoder.avc"); // working decoder type
                decoder.configure(format, m_surface, null, 0);
                break;
            }
        }

        if (decoder == null)
        {
            Log.e("DecodeActivity", "Can't find video info!");
            return;
        }

        decoder.start();

        ByteBuffer[] inputBuffers = decoder.getInputBuffers();
        ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
        BufferInfo info = new BufferInfo();
        boolean isEOS = false;
        long startMs = System.currentTimeMillis();

        while (!Thread.interrupted())
        {
            if (!isEOS)
            {
                int inIndex = decoder.dequeueInputBuffer(10000);
                if (inIndex >= 0)
                {
                    ByteBuffer buffer = inputBuffers[inIndex];
                    int sampleSize = extractor.readSampleData(buffer, 0);
                    if (sampleSize < 0)
                    {
                        Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                        decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        isEOS = true;
                    }
                    else
                    {
                        int flags = 0;// extractor.getSampleFlags();
                        decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), flags);
                        extractor.advance();
                    }
                }
            }

            int outIndex = decoder.dequeueOutputBuffer(info, 10000);
            switch (outIndex)
            {
            case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
                outputBuffers = decoder.getOutputBuffers();
                break;
            case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
                break;
            case MediaCodec.INFO_TRY_AGAIN_LATER:
                Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
                break;
            default:
                ByteBuffer buffer = outputBuffers[outIndex];
                Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);

                // We use a very simple clock to keep the video FPS, or the
                // video
                // playback will be too fast
                while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs)
                {
                    try
                    {
                        Thread.sleep(10);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                        break;
                    }
                }
                decoder.releaseOutputBuffer(outIndex, true);
                break;
            }

            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
            {
                Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                break;
            }
        }

          decoder.stop();
          decoder.release();
          extractor.release();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }   
}

用 ffprobe 看视频,看起来它实际上是高配置而不是基本配置:

Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x480 [SAR 1:1 DAR 3:2], 515 kb/s, 24.66 fps, 1000k tbr, 1000k tbn, 2000k tbc (default)

顺便说一句,我刚在笔记本电脑上试了一下,视频似乎可以播放,但在几个浏览器上播放时只显示黑屏。