不确定这是否超级准确,但以下内容非常简单并且似乎有效:
int n_seconds = 10; // seek forward 10 seconds
// time_base is in seconds, eg. the time base may be 1/1000th of a second,
// so just multiply by the reciprocal (den = denominator, num = numerator)
int64_t ts = av_rescale(
n_seconds,
format_ctx->streams[video_stream_index]->time_base.den,
format_ctx->streams[video_stream_index]->time_base.num
);
// even though it mentions in docs that you shouldn't use this because it is a
// work in progress, it's been around for more than a decade now, ffplay/ffmpeg/ffprobe
// all use it...it is the most consistent and easiest to use. the way I am using
// it here is to seek to the nearest keyframe (not frame!). I would not recommend
// using it in any other way:
// eg. AVSEEK_FLAG_ANY/FRAME/BACKWARD (BACKWARD is ignored anyways)
// 0 as flag seeks to keyframes only. I have set the max timestamp to the same value so
// that we only look for nearest keyframes behind us
int err = avformat_seek_file(pFormatContext, video_stream_index, 0, ts, ts, 0);
这会寻找最近的关键帧!这可能离你想要的很远。但是,它只会落后于目标时间戳,因此您可以使用 av_read_frame 直到到达您想要的位置,使用 AVframe->pts * AVStream->timebase 来计算帧的时间(使用 av_rescale 来执行此操作)。
另外请注意,如果您需要向后搜索(即您已经使用av_read_frame 阅读的帧后面的帧),或者您将在一个帧上多次调用av_read_frame,您必须发送/receive the packet/frame with avcodec_send_packet 和 avcodec_receive_frame 分别,否则编解码器上下文将不同步(我认为这是问题所在?)。您不能只是空白地读取数据包。在您寻找到您正在阅读的位置后面的新位置之后,您还应该avcodec_flush_buffers(您可能应该在每次寻找时都调用它,但我不确定性能)。
文档参考:
int avformat_seek_file (..., int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags)