【问题标题】:Android Software Decoder (OMX.google.h264.decoder) Fails to decode H264 videoAndroid 软件解码器 (OMX.google.h264.decoder) 无法解码 H264 视频
【发布时间】:2015-09-29 00:26:53
【问题描述】:

我在尝试使用 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();
    }   
}

【问题讨论】:

  • 测试了大约六个 H.264 视频。此 Google 解码器仅适用于其中一个。

标签: android video h.264 android-mediacodec


【解决方案1】:

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

流 #0:0(eng):视频:h264(高)(avc1 / 0x31637661)、yuv420p、720x480 [SAR 1:1 DAR 3:2]、515 kb/s、24.66 fps、1000k tbr、1000k tbn,2000k tbc(默认)

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

【讨论】:

  • 感谢您指出这一点。我尝试在我们的英特尔编码器中将格式更改为基线,但它仍然无法播放。我想还有很多其他设置可能会破坏兼容性。您是否有权访问与上述代码兼容并可以使用的示例 .mp4 或 .h264 文件?正确配置编码器对我有很大帮助。
  • @MichaelDavidKnight 这个链接有一个应该可以工作的 h.264 mp4 视频 - 我刚刚在 Android 4.2 上对其进行了测试,并使用 Android 浏览器和 chrome 浏览器播放:quirksmode.org/html5/tests/video.html
猜你喜欢
  • 1970-01-01
  • 2015-11-29
  • 2012-08-03
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
相关资源
最近更新 更多