Точно извлекайте кадры каждые N секунд с помощью FFmpeg

Точно извлекайте кадры каждые N секунд с помощью FFmpeg

Использование

Я извлекаю изображения из видео с помощью ffmpeg.

Я выгружаю одно уменьшенное изображение каждые 10 секунд включительно, которое объединяю в монтажи с помощью imagemagick. Эти монтажи снова используются для показа предварительного просмотра видео при наведении ползунка в веб-видеоплеере. (Вычисление того, какое изображение в монтаже показывать).

Команда

Поэкспериментировав, я пришел к следующей команде, в которой скорость важнее качества:

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -r 0.1 \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12" \
    -color_range 2 \
    f%09d.jpg

Кажется, это сработало нормально. Кадры сбиваются на ± 0,5 - 1 секунду тут и там, но это терпимо.

Проблема

Проблема в том, что ffmpegв начале видео появляется одно лишнее изображение. Например, файлы:

file             time
f000000001.jpg   00:00:00
f000000002.jpg   00:00:00
f000000003.jpg   00:00:10
f000000004.jpg   00:00:20
f000000005.jpg   00:00:30
...

Иногда первое и второе отличаются на несколько миллисекунд.

Насколько я знаю (сейчас), я могу просто удалить первое изображение и продолжить работу с остальными, но не уверен, почему это происходит и является ли это ошибкой или чем-то еще.

Другими словами: мне нужно знать, является ли «эффект» двух первых кадровнадежныйчтобы я мог удалить его ffmpegи в других версиях.

Поскольку я использую изображения для показа 10-секундного кадра из видео в указанное времяон выключен на 10 секундесли я не удаляю первое сгенерированное изображение. Если по какой-то причине это должно произойти, тонетсоздайте дубликат при запуске, другой версии или чего-то еще, удаление первого изображения создаст ту же проблему.

Монтаж

(Если интересно, то монтажи создаются примерно так):

montage -tile 5x -geometry +0+0 -background none [file1  - file50 ]  montage01.jpg
montage -tile 5x -geometry +0+0 -background none [file51 - file100]  montage02.jpg
...

Команда, которую я использую сейчас, основываясь на ответе (shell):

# Set on call or global:
file_in=sample.mp4
pix_fmt=yuvj420p
sec_snap_interval=10
nr_start=1
pfx_out=snap


ffmpeg \
    -loglevel warning \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$file_in" \
    -pix_fmt "$pix_fmt" \
    -filter:v "
        scale_cuda=
            w = -1 :
            h = 100,
        thumbnail_cuda = 2,
        hwdownload,
        format = nv12,
        select = 'bitor(
            gte(t - prev_selected_t, $sec_snap_interval),
            isnan(prev_selected_t)
        )'
    " \
    -vsync passthrough \
    -color_range 2 \
    -start_number "$nr_start" \
    "$pfx_out%09d.jpg"

решение1

При использовании -r 0.1устанавливается выходная частота кадров 0,1 Гц, но это не гарантирует получение кадров из входного видео ровно каждые 10 секунд (я не уверен, почему).

Одним из способов решения этой проблемы является использованиевыберите фильтр.

Пример (без ускорения GPU):

ffmpeg -i input.mp4 -vf "select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" -vsync 0 f%09d.jpg

  • bitor(gte(t-prev_selected_t\,10)когда 1разница между "прошедшими" временными метками больше 10 секунд.
    Когда выражение оценивается как 1, кадр "выбирается" и передается на выход.
  • bitorс isnan(prev_selected_t)пропусками первого кадра, где prev_selected_tесть NaN(не имеет значения).
  • -vsync 0применяется «сквозная передача» — каждый кадр передается со своей временной меткой от демультиплексора к мультиплексору.

Вот пример с scale_cudaи thumbnail_cuda:

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12,select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" \
    -vsync 0 \
    -color_range 2 \
    f%09d.jpg   
  • Из-за использования thumbnail_cudaфильтра нам придется разместить selectфильтр в конце.

Тестирование:
Создание синтетического видео со счетчиком кадров при 10 кадрах в секунду:

ffmpeg -y -f lavfi -r 10 -i testsrc=size=128x72:rate=1:duration=1000 -vf setpts=N/10/TB -vcodec libx264 -pix_fmt yuv420p input.mp4

Выходные кадры после выполнения вышеуказанной команды:

введите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесь
введите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесь

Как видите, выбранные кадры следуют ровно каждые 10 секунд.

Связанный контент