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)
顺便说一句,我刚在笔记本电脑上试了一下,视频似乎可以播放,但在几个浏览器上播放时只显示黑屏。
我在尝试使用 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)
顺便说一句,我刚在笔记本电脑上试了一下,视频似乎可以播放,但在几个浏览器上播放时只显示黑屏。