使用AVSEEK_FLAG_ANY的ffmpeg av_seek_frame会导致灰屏

puk*_*puk 3 ffmpeg seek media-player

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

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

Jon*_*rin 7

事实上,AVSEEK_FLAG_BACKWARD指示要找到最接近的关键帧具有更小的时间戳比你正在寻找的一个.

通过使用AVSEEK_FLAG_ANY,您将获得与您要求的时间戳完全对应的框架.但是这个帧可能不是关键帧,这意味着它不能被完全解码.这解释了您的"灰色屏幕",直到达到下一个关键帧.

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

注意:出于某种原因,当我正在寻找的帧是直接在此关键帧之前的帧时,看起来av_seek_frame()使用AVSEEK_FLAG_BACKWARD返回下一个关键帧.否则它返回前一个关键帧(这就是我想要的).我的解决方案是更改我给出的时间戳,av_seek_frame()以确保它将在我寻找的帧之前返回关键帧.

  • 我认为你在这里错了。某些帧需要对其他帧进行解码。“_keyframe_”实际上是一个_I-frame_(参见[此处](http://en.wikipedia.org/wiki/Video_compression_picture_types))。 (2认同)

cez*_*tko 5

用一些代码完成 JonesV 答案:

void seekFrame(unsigned frameIndex)
{
    // Seek is done on packet dts
    int64_t target_dts_usecs = (int64_t)round(frameIndex
            * (double)m_video_stream->r_frame_rate.den
            / m_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(m_video_stream->first_dts
        * (double)m_video_stream->time_base.num
        / m_video_stream->time_base.den * AV_TIME_BASE);
    target_dts_usecs += first_dts_usecs;
    int rv = av_seek_frame(
        m_format_ctx, -1, target_dts_usecs, AVSEEK_FLAG_BACKWARD);
    if (rv < 0)
        throw exception("Failed to seek");

    avcodec_flush_buffers(m_codec_ctx);
}
Run Code Online (Sandbox Code Playgroud)

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