安卓软件解码器(OMX.google.h264.decoder)无法解码H264视频。

5
我在尝试使用Android软件解码器(OMX.google.h264.decoder)提取和解码H264视频时遇到了问题,该问题似乎涉及多个设备。
此视频使用Nexus 5硬件解码器(OMX.qcom.video.decoder.avc)可以正常播放。
以下示例代码展示了此问题,并且是使用Android MediaCodec和MediaExtractor类的标准示例。
当第一个缓冲区传递给解码器时,我收到的异常是非法状态异常。
该视频为720x480 20fps,在基本配置文件下进行编码,因此应符合兼容性指南。 这里附带了一个视频样本以配合代码 我非常感谢任何有关正确使用软件视频解码器来处理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解码器只能处理其中的一个。 - Hong
1个回答

3
通过使用ffprobe观察视频,似乎它实际上是高级配置文件而不是基本配置文件:

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

另外,当我在笔记本电脑上尝试播放时,在一些浏览器上似乎可以播放但只显示黑色空白屏幕。

谢谢您指出这个问题。我尝试将格式更改为Intel编码器的基线,但仍然无法播放。我想可能有很多其他设置可能会破坏兼容性。您是否有一个示例.mp4或.h264文件可以与上述代码兼容并正常工作?这将大大帮助我正确配置编码器。 - Michael David Knight
@MichaelDavidKnight 这个链接有一个H.264 MP4视频,应该可以播放 - 我刚在Android 4.2上测试并且用Android浏览器和Chrome浏览器回放了:http://www.quirksmode.org/html5/tests/video.html - Mick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接