ffmpeg av_seek_frame

时间:2009-02-02 20:39:48

标签: c ffmpeg

我试图使用ffmpeg的av_seek_frame方法在电影中寻找但是我在确定如何生成时间戳以寻求时遇到最大问题。假设我想要向前或向后搜索x个帧,我知道电影当前在哪个帧上,我将如何进行此操作?

3 个答案:

答案 0 :(得分:16)

我是这样做的:

// Duration of one frame in AV_TIME_BASE units
int64_t timeBase;

void open(const char* fpath){
    ...
    timeBase = (int64_t(pCodecCtx->time_base.num) * AV_TIME_BASE) / int64_t(pCodecCtx->time_base.den);
    ...
}

bool seek(int frameIndex){

    if(!pFormatCtx)
        return false;

    int64_t seekTarget = int64_t(frameIndex) * timeBase;

    if(av_seek_frame(pFormatCtx, -1, seekTarget, AVSEEK_FLAG_ANY) < 0)
        mexErrMsgTxt("av_seek_frame failed.");

}

AVSEEK_FLAG_ANY可以搜索每一帧,而不仅仅是关键帧。

答案 1 :(得分:9)

简单回答:你应该有一个AVFormatContext对象。它的duration属性告诉您文件在时间戳上乘以1000可以在av_seek_frame中使用的时间长度,因此将其视为100%。然后,您可以计算您想要寻找的视频的距离。

如果你想前进一帧,只需调用av_read_frame和avcodec_decode_video,直到它用非零值填充got_picture_ptr。在调用avcodec_decode_video之前,请确保来自av_read_frame的数据包来自视频流。 avcodec_decode_video然后将填写AVFrame结构,您可以使用它来执行任何操作。

答案 2 :(得分:1)

不确定这是否超级准确,但以下内容非常简单并且似乎可行:

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,您必须分别使用avcodec_send_packetavcodec_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)