Как разбить видео с помощью FFMPEG, чтобы каждый фрагмент начинался с ключевого кадра?

Нам нужно разделить большой живой видеоролик WMV в маленьких кусках одинакового размера. Мы сделали script, который отлично работает, за исключением одного: видеоролики не начинаются с ключевого кадра, поэтому при воспроизведении большинства фрагментов видео они не отображают изображения до тех пор, пока ключевой кадр из исходного видео в конечном итоге достигается.

Нет ли способа сообщить ffmpeg, чтобы выходное видео начиналось с ключевого кадра?

Вот как выглядят наши командные строки прямо сейчас:

ffmpeg.exe -i "C:\test.wmv" -ss 00:00:00 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"
ffmpeg.exe -i "C:\test.wmv" -ss 00:00:05 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0001.wmv"

и т.д.

Ответ 1

В последних сборниках FFMPEG есть новый вариант "сегмента", который делает именно то, что, по вашему мнению, вам нужно.

ffmpeg -i INPUT.mp4 -acodec copy -f segment -vcodec copy -reset_timestamps 1 -map 0 OUTPUT%d.mp4

Это создает серию пронумерованных выходных файлов, которые разбиваются на сегменты на основе ключевых кадров. В моем собственном тестировании он работал хорошо, хотя я не использовал его ни на что более, чем на несколько минут и только в формате MP4.

Ответ 2

Как указано в официальном FFMPEG Docs, мне лучше было определить -ss timestart до -i input_file.ext, потому что он устанавливает (или так я понимаю) начало сгенерированного видео на ближайший ключевой кадр, найденный до, указанный вами метка времени.

Измените свой пример на:

ffmpeg.exe -ss 00:00:00 -i "C:\test.wmv" -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"

Я тестировал этот метод, работая над файлами .flv и .mp4.

Ответ 3

Вот решение, с которым я мог бы работать:

Как было предложено av501 и d33pika, я использовал ffprobe, чтобы найти, где находятся ключевые фреймы. Поскольку ffprobe очень многословен и может занять несколько секунд или даже минут для вывода всех ключевых кадров, и нет возможности охватить диапазон кадров, которые мы хотим от продолжительного видео, я перехожу к 5 этапам:

  • Экспортируйте фрагмент видео из исходного файла, в котором находится двойной размер требуемого фрагмента.

    ffmpeg -i source.wmv -ss 00:00:00 -t 00:00:06 -acodec copy -vcodec copy -async 1 -y  0001.wmv
    
  • Используйте ffprobe для поиска ключевых кадров. Выберите ближайший ключевой кадр после желаемого размера фрагмента.

    ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv
    
  • Из вывода ffprobe получите pkt_dts_time кадра непосредственно перед этим ключевым фреймом.

  • ffmpeg на экспортированном фрагменте шага 1, указав один и тот же файл ввода и вывода и указав -ss 00:00:00 и -t [значение, найденное на шаге 3].

    ffmpeg -i 0001.wmv -ss 00:00:00 -t 00:00:03.1350000 -acodec copy -vcodec copy -async 1 -y 0001.wmv
    
  • Перезапустите на шаге 1, используя -ss [кумулятивная сумма значений, найденных на шаге 3 по итерациям].

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

Ответ 4

Используя новую сборку ffmpeg, можно добиться этого, используя ffprobe и мультиплекс сегмента ffmpeg.

1.Используйте ffprobe и awk, чтобы идентифицировать ключевые кадры как можно ближе к желаемой длине блока.

ffprobe -show_frames -select_streams v:0 -print_format csv **[SOURCE_VIDEO]** 2>&1 | grep -n frame,video,1 | awk 'BEGIN { FS="," } { print $1 " " $5 }' | sed 's/:frame//g' | awk 'BEGIN { previous=0; frameIdx=0; size=0; } { split($2,time,"."); current=time[1]; if (current-previous >= **[DURATION_IN_SECONDS]**){ a[frameIdx]=$1; frameIdx++; size++; previous=current;} } END { str=a[0]; for(i=1;i<size;i++) { str = str "," a[i]; } print str;}'

Где

  • [SOURCE_VIDEO]= путь к видео, которое вы хотите сегментировать.
  • [DURATION_IN_SECONDS]= желаемая длина сегмента в секундах

Вывод представляет собой строку с ключевыми кадрами с разделителями-запятыми.

2.Используйте ключевые кадры, выводимые выше, как входные данные в ffmpeg.

ffmpeg -i [SOURCE_VIDEO] -codec copy -map 0 -f segment -segment_frames [OUTPUT_OF_STEP_1] ​​[SEGMENT_PREFIX] _% 03d. [SOURCE_VIDEO_EXTENSION]

Где

  • [SOURCE_VIDEO]= путь к видео, которое вы хотите сегментировать.
  • [OUTPUT_OF_STEP_1] ​​= строка с ключевыми кадрами с разделителями-запятыми
  • [SEGMENT_PREFIX]= название сегмента.
  • [SOURCE_VIDEO_EXTENSION]= расширение исходного видео (например, mp4, mkv)

Ответ 5

Используйте ffprobe -show_frames -pretty <stream> для определения ключевых кадров.

Ответ 6

Если вы хотите сделать некоторые сценарии и хотите, чтобы я кадры с определенным интервалом, один из способов сделать это -

  • Запустите ffprobe и соберите местоположение I-кадров с выхода

    ffprobe -show_streams

  • Запустите серию команд -ss -t, используя тот же script, чтобы получить требуемые фрагменты.

Затем вы можете выбрать script минимальное количество кадров [скажем, есть два изображения я в пределах 10 кадров друг от друга, вы действительно не хотите, чтобы их было там).

Другой способ сделать это - использовать multiplilesink gstreamer и установить режим на ключевой кадр [однако это будет фрагментироваться на каждом ключевом фрейме, чтобы это не было идеальным)