【问题标题】:Inconsistent sample time / presentation time during video decoding视频解码期间采样时间/呈现时间不一致
【发布时间】:2014-03-13 13:38:29
【问题描述】:

我正在编写一个可以通过摄像头输入编码视频并通过解码-编辑-编码步骤处理视频的应用程序。对于相机,我使用Camera类而不是Intent来配置相机的细节设置。然后我将相机帧提供给编码器(API 16 中的 MediaCodec)和复用器(我使用 ffmpeg 复用器,因为我想在 4.1 设备上工作)。

我通过系统纳秒时间测量相机帧的时间码,并选择一个帧子集以适应所需的 FPS(当前为 15)。时间值中有一些小的“噪音”,例如(以毫秒为单位):0, 60718, 135246, 201049, ... 而不是 0, 66000, 133000, 200000, ...。

在尝试正确配置复用器(如this question)后,我可以制作一个视频(使用 AVC 编解码器),该视频可由设备上的视频播放器播放。播放速度是正确的,所以我认为视频应该有正确的帧时间信息。

但是,当我尝试解码视频以执行视频编辑过程时遇到了问题。我使用标准视频提取/解码步骤为these samples,如下所示:

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);
        extractor.advance();
    }
}
else
{
    Log.v(TAG, "Decoder dequeueInputBuffer timed out! Try again later");
}

当我对视频进行编码时,getSampleTime() 的采样时间值正确。 (例如,它们在我们身上正好是 0、60718、135246、201049、...)。它也是decoder.queueInputBuffer() 的输入中的呈现时间。当解码器继续解码此帧时,我通过以下方式获得帧时间:

int decode_output_index = decoder.dequeueOutputBuffer(decode_buffer_info, TIMEOUT_USEC);
switch (decode_output_index)
{
    ....
    (some negative-value flags in MediaCodec)
    ....
    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);

        ....
    }
}

我希望设置与解码器输入中的时间序列相同的时间序列,但我从解码器输出的 BufferInfo 中得到很多 0。解码后的帧内容似乎是正确的,但是大部分的呈现时间值都是0。只有最后几帧有正确的呈现时间。

我在装有 Android 4.3 的设备上测试了整个相同的过程(即使使用相同的 ffmpeg muxer 而不是 API 18 中的 MediaMuxer),一切看起来都很好。在4.1/4.2设备上,如果我通过设备内置的摄像头APP采集视频,然后解码视频,那么呈现时间也是正确的,虽然时间值也会因为摄像头延迟而产生噪音。

视频或解码过程有什么问题,视频可以正常播放和解码,但采样时间正确且演示时间错误?我可能不得不使用一种解决方法来通过采样时间来衡量演示时间(使用队列很容易),但我想弄清楚我的工作中是否有任何遗漏的部分。

【问题讨论】:

    标签: android decode encode android-mediacodec mediaextractor


    【解决方案1】:

    无法保证MediaCodec 在 Android 4.3 之前正确处理演示时间戳。这是因为直到那时才添加确认 PTS 行为的 CTS 测试。

    我确实记得某些供应商的 AVC 编解码器中的时间戳处理存在问题。我不记得详细信息了,但是如果您在各种 4.1/4.2 设备上运行 EncodeDecodeTest 的缓冲区到缓冲区和缓冲区到表面测试,您会发现一些失败。 (当然,你需要去掉表面对表面的测试。)

    您的时间戳处理代码看起来不错。时间戳不是 H.264 流的一部分,因此它实际上只是作为元数据通过编解码器转发,您似乎正在将其拾取并转发到所有正确的位置。底线是,如果您传入了有效的 PTS 值并获得了良好的视频但 PTS 值是垃圾,那么编解码器中的某些东西对它们的处理不当。

    您需要通过单独传递值来解决它,或者 - 如果输入帧速率始终是常规的 -​​ 简单地计算它。从理论上讲,编码器可以重新排序帧,因此您将时间戳传递到编码器的顺序可能与它们出现的顺序不同......但是由于您知道制作电影时时间戳是递增的,所以您应该如果这在实践中是个问题,就可以对它们进行排序。

    另一方面,如果您在帧到达应用程序时抓取System.nanoTime(),系统中的延迟将导致您在时间戳值中看到的“摆动”。您可以在 Android 4.3 中使用 Surface 输入做得更好,因为 SurfaceTexture 保存的时间戳设置得更接近于捕获帧的时间。 (我知道这对您当前的工作没有用,但想给未来一些希望。)

    【讨论】:

    • 感谢您的回复。对于最后一部分,系统时间确实可能不规则,但是我无法通过相机回调的帧得到更准确的时间测量,所以我仍然使用系统时间并设计了一个简单的算法来“过滤”任意时间序列约为 15FPS。
    • 如果下一个时间戳不大于前一个时间戳,则无法将重新排序的时间戳发送到 ffmpeg。你向 ffmpeg 发送什么时间戳(我看到你说它们是正确的,但只是为了仔细检查)?我记得它需要一些基于 time_base.den 的 pkt.pts 计算
    • 您是否尝试过在这些设备上运行 CTS 测试?解码器输出帧时间等于输入吗?
    • CTS测试对原生相机APP编码的视频正常。时间戳总是递增的,解码器输入/输出是一致的。对于自己制作的视频,提取的采样时间是正确的,成为解码器的输入,但输出不一致。
    • @fadden,在CameraCaptureActivity的Grafika中,当你录制一个视频,即25秒的视频,然后播放时,我们会发现时间戳问题。该文件将是 25 秒的视频,但帧会偏移到更早的时间。所以视频帧实际上在 21 秒后停止,然后最后一帧才出现。你必须在真实播放器上运行视频,而不是在里面应用程序来观察它。知道如何解决这个问题吗?也许是开始录制之前的 updateTexImage() 导致这个偏移?谢谢 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 2018-08-05
    相关资源
    最近更新 更多