繁体   English   中英

ffmpeg av_seek_frame

[英]ffmpeg av_seek_frame

我正在尝试使用 ffmpeg 的 av_seek_frame 方法在电影中寻找,但是我在确定如何生成要寻找的时间戳时遇到了最大的麻烦。 假设我想向前或向后寻找 x 帧数并且我知道电影当前在哪一帧,我将如何去做?

这是我如何做到的:

// 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 可以搜索每一帧而不仅仅是关键帧。

简单的回答:你应该有一个 AVFormatContext 对象。 它的duration属性告诉你你的文件在 av_seek_frame 中可以使用的时间戳乘以 1000 的长度,所以把它当作 100%。 然后您可以计算出您想要搜索的视频的深度。

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

不确定这是否非常准确,但以下内容非常简单并且似乎有效:

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM