Android MediaCodec解码器输入/输出帧计数
我正在Android中进行视频转码,并使用标准方法提取/解码视频。我用不同的视频设备在不同的设备上测试了相同的过程,发现解码器输入/输出的帧数有问题 对于中的一些时间码问题,我使用队列记录提取的视频样本,并在获得解码器帧输出时检查队列,如以下代码: (为了更清楚,我省略了编码相关代码)Android MediaCodec解码器输入/输出帧计数,android,codec,decoder,android-mediacodec,mediaextractor,Android,Codec,Decoder,Android Mediacodec,Mediaextractor,我正在Android中进行视频转码,并使用标准方法提取/解码视频。我用不同的视频设备在不同的设备上测试了相同的过程,发现解码器输入/输出的帧数有问题 对于中的一些时间码问题,我使用队列记录提取的视频样本,并在获得解码器帧输出时检查队列,如以下代码: (为了更清楚,我省略了编码相关代码) Queue sample\u time\u Queue=new LinkedList(); .... //在转码循环中 如果(是否解码输入完成==false) { int decode\u input\u ind
Queue sample\u time\u Queue=new LinkedList();
....
//在转码循环中
如果(是否解码输入完成==false)
{
int decode\u input\u index=decoder.dequeueInputBuffer(超时\u USEC);
如果(解码输入索引>=0)
{
ByteBuffer解码器\输入\缓冲=解码\输入\缓冲[解码\输入\索引];
int sample\u size=extractor.readSampleData(解码器输入缓冲区,0);
如果(样本大小<0)
{
解码器.queueInputBuffer(解码\u流的输入\u索引、0、0、MediaCodec.BUFFER\u标志\u结束\u);
_解码_输入_完成=真;
}
其他的
{
长样本时间=提取器.getSampleTime();
queueInputBuffer(解码输入索引,0,样本大小,样本时间,0);
样本时间队列。报价(样本时间);
提取器;
}
}
其他的
{
DumpLog(标记“Decoder dequeueInputBuffer超时!请稍后重试”);
}
}
....
如果(是解码还是输出完成==false)
{
int decode\u output\u index=decoder.dequeueOutputBuffer(decode\u buffer\u info,TIMEOUT\u USEC);
开关(解码输出索引)
{
案例MediaCodec.INFO\u输出\u缓冲区\u已更改:
{
....
打破
}
案例MediaCodec.INFO\u输出\u格式\u已更改:
{
....
打破
}
case MediaCodec.INFO\u请稍后再试:
{
DumpLog(标记“Decoder dequeueOutputBuffer超时!请稍后重试”);
打破
}
违约:
{
ByteBuffer解码输出缓冲区=解码输出缓冲区[解码输出索引];
long ptime\u us=解码缓冲区\u info.presentationTimeUs;
布尔值是_decode_EOS=((解码_buffer_info.flags&MediaCodec.buffer_FLAG_END_流)!=0);
如果(是解码EOS)
{
//解码器给出EOS输出。
_解码_输出_完成=真;
....
}
其他的
{
//某些视频的帧时间可能不一致。
//作为一种解决方法,我们使用帧时间队列来保护这一点。
long sample_time=sample_time_queue.poll();
如果(样本时间==ptime\U us)
{
//很好,解码器的输入/输出时间是一致的。
}
其他的
{
//如果解码器输入/输出帧计数一致,我们可以信任采样时间。
ptime\u us=采样时间;
}
//处理此框架
....
}
decoder.releaseOutputBuffer(解码输出索引,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编解码器的PAD上,事情变得更加混乱。一些视频具有来自解码器的良好PTS,且输入/输出帧计数正确(即解码完成时队列为空)。有些视频具有一致的输入/输出帧数,解码器输出中的PTS不好(我仍然可以通过队列更正它们)。对于某些视频,解码过程中会丢失很多帧。例如,提取器在7秒视频中获得210帧,但解码器仅输出最后180帧。使用相同的解决方法无法恢复临时技术秘书处
是否有任何方法可以预期MediaCodec解码器的输入/输出帧计数?或者更准确地说,当提取器以正确的采样时间为其提供视频样本时,要知道解码器丢弃了哪些帧?与中相同的基本情况。在4.3之前,没有任何测试确认馈送到编码器或解码器的每一帧都来自另一侧。我记得在某些测试中,某些设备会可靠地丢弃最后一帧,直到4.3中修复了编解码器
我当时没有寻找解决办法,所以我不知道是否存在。如果在发送EOS之前延迟可能会导致某些东西提前关闭
我不相信我曾经看到过一个设备掉了大量的帧。这似乎是一个不寻常的情况,因为在任何以类似方式运行
MediaCodec
的应用程序中,即使没有仔细测试,也会注意到这一点。你能确认每台设备上使用的安卓版本吗?同样,4.3之前的版本没有任何保证。在4.3的开发过程中,在某些测试中,qcom设备上出现了丢失最后一帧的问题,因此如果是4.1/4.2设备,也就不会有什么意外了。我无法访问带有MTK编解码器的设备,因此无法在那里发表评论。你能从PTS值判断它是否以规则的模式丢弃帧吗
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);
}
}
}