Как разбить видео с помощью 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 и установить режим на ключевой кадр [однако это будет фрагментироваться на каждом ключевом фрейме, чтобы это не было идеальным)