我正在Android中进行视频转码,并使用标准方法作为这些示例来提取/解码视频。我在具有不同视频设备的不同设备上测试了相同的过程,我发现解码器输入/输出的帧数有问题。
对于this question中的一些时间码问题,我使用队列来记录提取的视频样本,并在得到解码器帧输出时检查队列,如下代码:(我省略了与编码相关的代码以使其更清晰)
Queue<Long> sample_time_queue = new LinkedList<Long>();
....
// in transcoding loop
if (is_decode_input_done == false)
{
int decode_input_index = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (decode_input_index >= 0)
{
ByteBuffer decoder_input_buffer = decode_input_buffers[decode_input_index];
int sample_size = extractor.readSampleData(decoder_input_buffer, 0);
if (sample_size < 0)
{
decoder.queueInputBuffer(decode_input_index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
is_decode_input_done = true;
}
else
{
long sample_time = extractor.getSampleTime();
decoder.queueInputBuffer(decode_input_index, 0, sample_size, sample_time, 0);
sample_time_queue.offer(sample_time);
extractor.advance();
}
}
else
{
DumpLog(TAG, "Decoder dequeueInputBuffer timed out! Try again later");
}
}
....
if (is_decode_output_done == false)
{
int decode_output_index = decoder.dequeueOutputBuffer(decode_buffer_info, TIMEOUT_USEC);
switch (decode_output_index)
{
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
{
....
break;
}
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
{
....
break;
}
case MediaCodec.INFO_TRY_AGAIN_LATER:
{
DumpLog(TAG, "Decoder dequeueOutputBuffer timed out! Try again later");
break;
}
default:
{
ByteBuffer decode_output_buffer = decode_output_buffers[decode_output_index];
long ptime_us = decode_buffer_info.presentationTimeUs;
boolean is_decode_EOS = ((decode_buffer_info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
if (is_decode_EOS)
{
// Decoder gives an EOS output.
is_decode_output_done = true;
....
}
else
{
// The frame time may not be consistent for some videos.
// As a workaround, we use a frame time queue to guard this.
long sample_time = sample_time_queue.poll();
if (sample_time == ptime_us)
{
// Very good, the decoder input/output time is consistent.
}
else
{
// If the decoder input/output frame count is consistent, we can trust the sample time.
ptime_us = sample_time;
}
// process this frame
....
}
decoder.releaseOutputBuffer(decode_output_index, false);
}
}
}
在某些情况下,如果解码器给出错误值(例如很多 0),队列可以“纠正”PTS。但是,解码器输入/输出的帧数仍然存在一些问题。
在 HTC One 801e 设备上,我使用编解码器 OMX.qcom.video.decoder.avc 来解码视频(使用 MIME 类型 video/avc)。除了最后一帧,采样时间和 PTS 与帧匹配得很好。例如,如果提取器将 100 帧然后 EOS 馈送到解码器,则前 99 个解码帧具有完全相同的时间值,但最后一帧丢失了,我从解码器得到输出 EOS。我测试了由内置摄像头、ffmpeg muxer 或 Windows 上的视频处理 AP 编码的不同视频。他们所有的最后一帧都消失了。
在一些带有 OMX.MTK.VIDEO.DECODER.AVC 编解码器的打击板上,事情变得更加混乱。一些视频具有良好的解码器PTS,并且输入/输出帧数正确(即解码完成时队列为空)。一些视频具有一致的输入/输出帧数,解码器输出中的 PTS 错误(我仍然可以通过队列纠正它们)。对于某些视频,在解码过程中会丢失很多帧。例如,提取器在 7 秒的视频中得到 210 帧,但解码器只输出最后的 180 帧。使用相同的解决方法是不可能恢复 PTS 的。
有什么方法可以预期 MediaCodec 解码器的输入/输出帧数?或者更准确地说,要知道解码器丢弃了哪些帧,而提取器为它提供了具有正确采样时间的视频样本?