Android MediaCodec解码器输入/输出帧数

时间:2014-03-14 04:25:09

标签: android codec decoder mediacodec mediaextractor

我正在Android中进行视频转码,并使用标准方法these samples来提取/解码视频。我在使用不同视频设备的不同设备上测试相同的过程,我发现解码器输入/输出的帧数有问题。

对于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);
        }
    }
}

在某些情况下,队列可以“纠正”#34;如果解码器给出错误值(例如,很多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解码器的输入/输出帧数?或者更确切地说,知道解码器丢弃了哪些帧,而提取器为其提供了正确采样时间的视频样本?

1 个答案:

答案 0 :(得分:1)

the other question相同的基本故事。在4.3之前,没有测试确认馈送到编码器或解码器的每个帧都出现在另一侧。我记得有些设备会在某些测试中可靠地丢弃最后一帧,直到编解码器在4.3中被修复。

我当时没有搜索变通方法,所以我不知道是否存在变通方法。在发送EOS之前延迟可能会有所帮助,如果它导致某些事情提前关闭。

我不相信我曾经看过一个设备掉落了大量的帧。这似乎是一个不寻常的案例,因为即使没有经过仔细的测试,任何以类似方式行使MediaCodec的应用程序都会引人注意。