Skip to content
anonymous edited this page Apr 1, 2016 · 22 revisions

Советы по использованию FFmpeg

  1. Добавление превью
  2. Создание видео из статичной картинки и музыки
  3. Создание музыкальных лупов

Добавление превью

Добавление превью к видео сводится к трём этапам:

  1. Кодирование основного видео
  2. Кодирование участка, используемого в качестве превью
  3. Склейка превью и основного видеофайла

Этап 1

Ознакомиться с основами кодирования WebM можно в этой статье. Предположим, что выполняет кодирование видеофайла длиной 50 секунд и размерами кадра в исходном файле 1280x720 (16:9) со следующими параметрами:

ffmpeg -i video.mp4 -t 50 -c:v vp9 -b:v 0 -crf 20 \
       -c:a libopus -b:a 96K video.webm

Этап 2

На этом этапе нужно подготовить картинку-превью. Это может быть изображение в любом формате, который может обработать FFmpeg, сконвертированное в pixel format yuv420p. Разрешение и pixel format настоятельно рекомендуется выбирать как у основного видео несмотря на то, что VP8/VP9 видео поддерживает динамически изменяющееся разрешение, потому что браузеры некорректно отображают такое видео. Узнать их можно с помощью MediaInfo или командой

ffprobe -hide_banner -i video.webm

из строки

Input #0, matroska,webm, from 'video.webm':
Metadata:
encoder : Lavf56.15.102
Duration: 00:00:50.000, start: 0.000000, bitrate: 1500 kb/s
Stream #0:0(jpn): Audio: opus, 48000 Hz, stereo, fltp (default)
Stream #0:1: Video: vp9, yuv420p, 1280x720, SAR 1:1 DAR 16:9, 23.98 fps, 23.98 tbr, 1k tbn, 1k tbc (default)

Тогда удостоверившись, что preview.png имеет разрешение 1280x720, создаем превью:

ffmpeg -i preview.png -c:v vp9 -pix_fmt +yuv420p preview.webm

Этап 3

На данном этапе предстоит совместить файл с превью и файл с основным видео. Подробнее про склеивание файлов посредством FFmpeg можно прочитать в официальной вики.

Для того чтобы склеить несколько файлов, нужно создать текстовый файл (расширение не играет роли, но можно сделать .txt, чтобы было удобнее редактировать файл), в который записываются строки в формате file '/путь/к/файлу'. Путь может быть как абсолютный, так и относительный, например

file 'preview.webm'
file 'video.webm'

укажет FFmpeg, что нужно в таком порядке объединить файлы, находящиеся в той же директории, из которой выполнена команда. Далее выполняем склеивание видео, добавляем звук из исходного файла, задавая ему смещение на время показа превью (в данном случае это 0.04):

ffmpeg -itsoffset 0.04 -i video.webm -f concat -i files.txt \
       -map 1:v -map 0:a -c copy out.webm

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

Создание видео из статичной картинки и музыки

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

Однопроходное кодирование видео со статичной картинкой

Самый простой способ — зациклить изображение, сформировав таким образом видеопоток, который затем перекодируется в VP8/VP9 WebM и к которому можно добавить аудиопоток в формате Vorbis/Opus (символ "" ниже экранирует переносы строк в *nix системах, в Windows для этого используется циркумфлекс "^")

ffmpeg -r 1 -loop 1 -i pic.png -i music.flac \
  -map 0:v -map 1:a \
  -c:a libvorbis -q:a 9 \
  -c:v vp8 -b:v 0 -crf 16 -g 360 -vf scale=-1:720 -pix_fmt +yuv420p \
  -shortest output.webm

В первой строке указываются входные файлы. Опция -loop 1 перед -i pic.jpeg сообщает FFmpeg, что входной файл с изображением требуется зациклить; опция -r 1 указывает частоту кадров в секунду (fps), с которой циклится картинка.

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

В третьей строке указываются параметры кодирования аудиопотока. Опция -c:a libvorbis приведена для наглядности, её можно не указывать, так как по умолчанию аудиопоток в контейнере WebM будет в формате Vorbis. Опция -q:a 9 сообщает энкодеру Vorbis, какое качество для аудиопотока следует использовать (0 - наихудшее, в районе 64Kbps, 9 - наилучшее, в районе 320Kbps, см. таблицу тут).

В четвёртой строке указываются параметры кодирования видеопотока. Выбирается кодек видеопотока -c:v vp8 (что также необязательно делать, т.к. он используется по умолчанию для формата WebM), ограничение битрейта открлючается (-b:v 0), устанавливается качество видео (-crf 16) и размер GoP (-g 360), и на видео накладывается фильтр -vf scale=-1:720 (устанавливается размер видео 720 пикселов по высоте, ширина вычисляется автоматически исходя из размера исходного изображения и соотношения сторон). При использовании vp9 с недавних пор также следует указывать -pix_fmt +yuv420p, иначе макаба не сможет сделать превью.

В пятой строке опция -shortest сообщает FFmpeg, что нужно выходному файлу установить длину самого короткого из переданных потоков.

Двухпроходное кодирование видео со статичной картинкой

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

Для того чтобы избежать подёргиваний кадров и ухудшения качества видео, следует применять двухпроходное кодирование. Первым шагом необходимо провести анализ кадров и создать лог-файл первого прохода:

ffmpeg -loop 1 -i pic.jpeg -r 1 -t 3:53.032 -c:v vp8 \
-pass 1 -passlogfile /tmp/test-2pass \
-f webm -y /dev/null

В первой строке в качестве входного файла указываем изображение и сообщаем FFmpeg, что оно должно быть зациклено для формирования потока с количеством кадров в секунду равным 1 и длиной в 3 минуты 53 секунды, который будет закодирован в формат VP8.

Совет. При кодировании необязательно указывать время, достаточно указать число кадров в секунду (-r) и общее количество кадров в потоке с помощью опции -vframes число. Например, видео длиной 3:53.032 и фреймрейтом 1 будет содержать 1x(3x60+54)=234 кадра (следует округлять в большую сторону).

Во второй строке сообщаем FFmpeg, что следует выполнить анализ входного файла и сохранить лог первого прохода в файл /tmp/test-2pass (в Windows следует указывать пути в её формате, также можно использовать переменные окружения типа %USERPROFILE% или %TEMP%, они регистронезависимые). Если не указывать опцию -passlogfile, то по умолчанию файл создастся в том каталоге, из которого вызван FFmpeg.

В третьей строке в опции -f указывается формат выходного файла (в данном случае это webm), а результат работы программы отправляется в /dev/null (NUL в Windows), опция -y сообщает FFmpeg, чтобы он перезаписал существующий файл без запроса.

В результате после первого прохода будет составлен лог-файл, содержащий информацию об анализе кадров в видео. Затем выполняется второй проход, теперь можно в него добавить аудиодорожку:

ffmpeg -loop 1 -i pic.jpeg -i music.mp3 \
-q:a 9 -r 1 -t 3:53.032 -c:v vp8 \
-auto-alt-ref 1 -pass 2 -passlogfile /tmp/test-2pass \
output.webm

В данном выше примере во втором проходе (-pass 2) происходит добавление аудиопотока в формате Vorbis с наилучшим качеством аудио (-q:a 9). Битрейт не устанавливается, используется стандартное значение 200Kbps, для двухпроходного кодирования статических изображений такого битрейта часто достаточно. Также присутствует опция -auto-alt-ref 1, включающая режим создания альтернативных ключевых кадров. Результат записывается в файл output.webm.

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

Создание музыкальных лупов

Луп — это несколько кадров, которые повроряются под музыку в её ритм. Создание лупа делится на несколько этапов:

  1. извлечение кадров;
  2. определение ритма музыки;
  3. вычисление скорости анимации;
  4. определение длительности лупа;
  5. собственно кодирование.

Итак, поехали. Берём кусок анимы, который требуется залу̀пить, и сохраняем как набор картинок. Если он уже залуплен в исходнике, то берём только одну итерацию лупа.

ffmpeg -ss 59.893000 -i "[HorribleSubs] Kono Subarashii Sekai ni Shukufuku wo! - 04 [720p].mkv" -vf crop=840 -t 0.918 -q 0 "%3d.jpg"

Чтобы избежать потерь цвета при преобразовании YUV → RGB и обратно, для картинок не следует использовать формат PNG. Если не нравится жпег, можно взять WebP (это ключевой кадр от VP8).

Хинт: знак «%» в cmd.exe используется для указания переменных среды, и для передачи «%3d.jpg» его нужно удвоить (%%3d.jpg).

Проверяем:

mpv "av://lavfi:movie=%3d.jpg:loop=0"

Если получилось коряво, то удаляем или добавляем кадры и пробуем снова. Итоговое кол-во кадров запоминаем (у меня получилось 22), оно понадобится для вычисления fps.

Далее определяем ритм музыки. Для этого можно использовать bpm detect'оры (встроенный в mixxx, bpmdetect и т.д), но у них это получается не всегда. Надёжнее всего считать вручную: поставить трек играть минуту и нажимать в такт клавиши, а потом считать нажатия (echo -n "строка" | wc -c покажет кол-во символов).

Итак, посчитали. BPM (кол-во ударов в минуту) трека «Tobidase! Bankikki, Which Means Fly Out! Bun Kick [Casual Killer Remix]» — 150. Вычисляем FPS по формуле:

fps = bpm / minutes_in_second * nubmer_of_frames = 
      150 / 60 * 22 = 55

Проверяем:

mpv "av://lavfi:movie=%3d.jpg:loop=0,setpts=N/(55*TB)"

Если получилось слишком быстро, то делим FPS на двойку пока не получится нормально. Если медленно, то наоборот удваиваем. Анимация должна чётко ложиться на музыку без съезжания.

Определившись с началом куска аудиотрека (у меня — 140.23 секунды), можно привинтить его к filtergraph'у на входе mpv:

mpv "av://lavfi:movie=%3d.jpg:loop=0,setpts=N/(55/2*TB)[out0];
     amovie='03 - Tobidase! Bankikki, Which Means Fly Out! Bun Kick [Casual Killer Remix].mp3':sp=140.23,asetpts=PTS-STARTPTS[out1]"

В процессе воспроизведения можно сдвигать звук на 100мс клавишами «-» и «=».

Длительность лупа должна быть кратна тактам. Количество последних чаще всего получается степенью двойки. Берём, например, 64 такта и пробуем их залупить:

60 / 150 * 64 = 25.6
mpv "av://lavfi:movie=%3d.jpg:loop=0,setpts=N/(55/2*TB)[out0];
      amovie='03 - Tobidase! Bankikki, Which Means Fly Out! Bun Kick [Casual Killer Remix].mp3':sp=140.23,asetpts=PTS-STARTPTS[out1]" \
    -loop -length 25.6

Эксперементировать с этим удобно в mixxx. У меня получилось 96 тактов или 38.4 секунды.

Всё проверив и подогнав, скармливаем полученный filtergraph ffmpeg'у и жмём двумя проходами:

ffmpeg -f lavfi -i "movie=%3d.jpg:loop=0,setpts=N/(55/2*TB)" -t 38.4 -c:v vp9 -pass 1 -f null -
ffmpeg -f lavfi -i "movie=%3d.jpg:loop=0,setpts=N/(55/2*TB)[out0];
      amovie='03 - Tobidase! Bankikki, Which Means Fly Out! Bun Kick [Casual Killer Remix].mp3':sp=140.23,asetpts=PTS-STARTPTS[out1]" \
    -t 38.4 -c:v vp9 -pass 2 -crf 25 -b:v 0 -c:a libvorbis -q:a 5 out.webm