ffmpeg av_seek_frame

Я пытаюсь искать в фильме, используя метод av_seek_frame ffmpeg, однако у меня больше всего проблем с определением того, как создать отметку времени для поиска. Предполагая, что я хочу найти x количество кадров вперед или назад, и я знаю, в каком кадре сейчас находится фильм, как мне это сделать?


person Jack Edmonds    schedule 02.02.2009    source источник
comment
Не могли бы вы рассчитать смещение времени, используя частоту кадров?   -  person Mark Renouf    schedule 02.02.2009
comment
Насколько я понимаю, смещение времени должно быть в единицах time_base, но я не совсем уверен, как преобразовать его в эти единицы (или даже если это то, что мне нужно делать). Если это то, что мне нужно сделать, я не уверен, каковы единицы измерения time_base (секунды, кадры, кадры в секунду).   -  person Jack Edmonds    schedule 02.02.2009


Ответы (3)


Вот как я это сделал:

// 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 позволяет искать каждый кадр, а не только ключевые кадры.

person Hugo Maxwell    schedule 05.01.2012

Простой ответ: у вас должен лежать объект 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, с которой вы сможете делать что угодно.

person Community    schedule 09.02.2009

Не уверен, что это очень точно, но следующее очень просто и, кажется, работает:

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_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 флаги)

person Asad-ullah Khan    schedule 21.03.2020
comment
Спасибо! Обнаружил, что для сброса к началу потока на самом деле требуется 3 шага, например avcodec_send_packet(video_dec_ctx, NULL) + av_seek_frame(fmt_ctx, video_stream_idx, 0, AVSEEK_FLAG_ANY) +avcodec_flush_buffers(video_dec_ctx); - person Sam Ginrich; 22.03.2021