2

我正在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 解码器的输入/输出帧数?或者更准确地说,要知道解码器丢弃了哪些帧,而提取器为它提供了具有正确采样时间的视频样本?

4

1 回答 1

2

与其他问题相同的基本故事。在 4.3 之前,没有测试确认输入到编码器或解码器的每一帧都从另一侧出来。我记得有些设备会在某些测试中可靠地丢弃最后一帧,直到编解码器在 4.3 中修复。

我当时没有寻找解决方法,所以我不知道是否存在。如果它导致某些东西提前关闭,那么在发送 EOS 之前延迟可能会有所帮助。

我不相信我见过设备丢掉大量帧。这似乎是一个不寻常的案例,因为MediaCodec即使在没有仔​​细测试的情况下,在任何以类似方式锻炼的应用程序中都会很明显。

于 2014-03-17T05:58:57.843 回答