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

时间:2015-07-10 09:45:40

标签: android video h.264 mediacodec

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

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

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

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

视频是在基线配置文件中编码的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();
    }   
}

1 个答案:

答案 0 :(得分:2)

使用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(默认)

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