使用AVSEEK_FLAG_ANY的ffmpeg av_seek_frame会导致灰屏

时间:2013-12-22 22:40:16

标签: ffmpeg media-player seek

问题: omxplayer的源代码使用av_seek_frame()标志调用ffmpeg AVSEEK_FLAG_BACKWARD方法。虽然不是100%肯定,但我相信这是寻求最接近的i-frame。相反,我想寻找确切的位置,因此我修改了源代码,使av_seek_frame()方法现在使用AVSEEK_FLAG_ANY标志。现在,当电影加载时,我会得到一个灰色屏幕,通常持续1秒,在此期间我可以听到音频。我在多台计算机上试过这个(我实际上正在同步它们,因此它们同时也是如此)因此它不是一个孤立的事件。我的猜测是,寻找非i帧的计算成本更高,导致初始灰屏。

问题:如何使用ffmpeg,我可以指示音频等到视频准备好后再继续。

2 个答案:

答案 0 :(得分:7)

实际上,AVSEEK_FLAG_BACKWARD表示您希望找到最接近的关键帧,其小于您正在寻找的时间戳

通过使用AVSEEK_FLAG_ANY,您可以获得与您要求的时间戳完全对应 的框架。但是这个帧可能不是关键帧,这意味着它不能被完全解码。这解释了你的“灰色屏幕”,它出现在到达下一个关键帧之前。

因此,解决方案是使用AVSEEK_FLAG_BACKWARD 向后搜索,并从此关键帧读取下一帧(例如使用av_read_frame()),直到找到相应的帧到你的时间戳。此时,您的帧将被完全解码,并且不会再显示为“灰色屏幕”。

注意:由于某种原因,av_seek_frame() AVSEEK_FLAG_BACKWARD使用av_seek_frame()时,我正在寻找的框架会返回 next 关键帧直接在此关键帧之前的那个。否则它返回前一个关键帧(这就是我想要的)。我的解决方案是更改我给{{1}}的时间戳,以确保它在我想要的帧之前返回关键帧

答案 1 :(得分:2)

使用一些代码完成JonesV答案:

void seekFrame(int FrameIndex)
{
    // Seek is done on packet dts
    int64_t target_dts_usecs = (int64_t)round(frameIndex * (double)_video_stream->r_frame_rate.den / _video_stream->r_frame_rate.num * AV_TIME_BASE);
    // Remove first dts: when non zero seek should be more accurate
    auto first_dts_usecs = (int64_t)round(_video_stream->first_dts * (double)_video_stream->time_base.num / _video_stream->time_base.den * AV_TIME_BASE);
    target_dts_usecs += first_dts_usecs;
    int rv = av_seek_frame(_format_ctx, -1, target_dts_usecs, AVSEEK_FLAG_BACKWARD);
    if (rv < 0)
        throw exception();

    avcodec_flush_buffers(_codec_ctx);
}

然后,您可以开始解码AVPacket.dts对原始目标dts的检查,并在AVStream.time_base上计算。一旦达到目标dts,下一个解码帧就应该是所需的帧。