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