FFmpeg — мощное ПО со большим набором возможностей. В статье я постараюсь рассказать о немного необычном применении фильтров ffmpeg и о том что можно сделать используя исключительно их. Видео ниже сделано с помощью 1 команды ffmpeg (ни один графический редактор не пострадал).
Я буду использовать последнюю версию ffmpeg, собранную из git. Так что, если вы хотите чтобы мои примеры работали, рекомендую собрать из ветки master. Сборка и установка тривиальна и описана на сайте ffmpeg.org, при ./configure не забудьте указать "--enable-libfreetype".
Для начала нам нужно создать фон для нашего видео и мы будем использовать средства ffmpeg. Дело в том что ffmpeg позволяет использовать в качестве входного источника не только реальные устройства (например, видео-камеры или sdi-карты) и файлы, но и может использовать собственное виртуальное устройство, которое называется lavfi.
FFmpeg содержит в себе несколько готовых фильтров, которые генерируют видеоизображение. Среди них:
- testsrc — тестовый экран с таймером и меняющуйся строкой
- cellauto — заполнение экрана случайными черно-белыми ячейками
- life — заполнение экрана алгоритмом типа игры life
- mandelbrot — фрактальное множество Мандельброта
- rgbtestsrc — 3 цветных полосы
- smptebars — несколько цветных полос
Полный список фильтров можно посмотреть командой
ffmpeg -filters
Вот как они выглядят:
Поскольку я указал что буду использовать исключительно ffmpeg, то и это изображение я сгенерировал с помощью 1 команды ffmpeg. Вот она:
ffmpeg
-f lavfi -i "testsrc=s=320x240" #Указываем все входные lavfi источники и ставим им размер в 320x240
-f lavfi -i "cellauto=s=320x240"
-f lavfi -i "life=s=320x240"
-f lavfi -i "mandelbrot=s=320x240"
-f lavfi -i "rgbtestsrc=s=320x240"
-f lavfi -i "smptebars=s=320x240"
-filter_complex
"[0:0]pad=iw*6:ih[a]; #Делаем падинг изображений с помощью фильтра pad
[1:0]pad=iw:ih[b];
[2:0]pad=iw*2:ih[c];
[3:0]pad=iw*3:ih[d];
[4:0]pad=iw*4:ih[e];
[5:0]pad=iw*5:ih[f];
[a][b]overlay=w[x]; # Склеиваем изображения с помощью фильтра overlay
[x][c]overlay=w[y];
[y][d]overlay=w[z];
[z][e]overlay=w[v];
[v][f]overlay=w" -vframes 1 all_filters.jpg
Хотя я сделал картинку, с помощью этой команды можно также объединить 6 видео в ряд или сделать «мозайку», нужно лишь подставить в качестве входных источников видео-файлы и изменить параметры для фильтров pad и overlay. Пробежимся по опциям:
- -f lavfi — входной формат для ffmpeg — виртуальное устройство lavfi
- -i "<filter-name>=<filter-options>" — использовать в качестве входных файлов фильтр с этими опциями
- -filter_complex — далее мы укажем граф фильтров
- [0:0]pad=<pad-options> — используем входной источник 0, поток 0 и применяем к нему фильтр pad
- iw/ih — внутренние алиасы ffmpeg, которые указывают ширину и высоту входного изображения
- iw*6:ih[a] — увеличиваем ширину изображения в 6 раз и присваиваем этому потоку имя «a»
- [a][b]overlay=w[x] — применяем фильтр overlay к потокам «a» и «b» и присваиваем этому потоку имя «x»
- vframes 1 — количество видеокадров 1, т.к. хотим получить картинку, а не видео
- all_filters.jpg — имя файла для сохранения картинки, расширения ffmpeg понимает, так что если укажите .mp4, ffmpeg выдаст вам mp4 контейнер
И так, создадим фон для нашего видео:
ffmpeg2 -f lavfi -i "color=lightblue" -t 30 -y blue.ts
Тут все тоже самое что и в примере с картинками, за исключением того, что здесь мы создаем видео и вместо фильтров-генераторов, используем простой фильтр цвета. Помимо количества кадров, вы можете задать продолжительность видео через ключ "-t". По умолчанию ffmpeg задаст fps выходного потока равным 25. Поскольку я указал расширение выходному файлу .ts, то ffmpeg будет использовать контейнер MPEG2TS, а кодек mpeg2video.
Теперь добавим снежинку. Для этого будем использовать фильтр drawtext. Да, да, мы нарисуем снежинку текстом, вернее специальным юникод символом "❄" (U+2744).
Команда, которая это делает:
ffmpeg -f lavfi -i "color=lightblue"
-vf "drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=50:text='❄':fontcolor=white:x=sin(n/10)*30+w/2:y=n"
-t 5 -y snow.gif
Опции фильтра drawtext:
- fontfile — путь до файла шрифта
- fontsize — размер шрифта
- text — отображаемый текст
- fontcolor — цвет шрифта
- x,y — координаты текста
Как можно заметить, вся соль в координатах. Дело в том, что ffmpeg позволяет менять параметры некоторых своих фильтров «на лету», например, в зависимости от номера кадра или таймстемпа. В данном примере, помимо уже знакомых w и h, я использовал очередной внутренний алиас ffmpegа «n» — номер текущего кадра и функцию синуса чтобы придать снежинке движение из стороны в сторону. В фильтрах ffmpeg вы можете использовать тригонометрические и арифметические функции, такие как trunc и random. Полный список функций можно посмотреть вот здесь.
Теперь осталось только добавить еще фильтров (нужно больше фильтров!) для создания других снежинок и изменить их размер и движения.
ffmpeg -f lavfi -i "color=lightblue"
-vf "drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=10:text='❄':fontcolor=white:x=sin(n/10)*30:y=3*n,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=16:text='❄':fontcolor=white:x=cos(n/9)*3+w/3:y=n/2-h/16,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=19:text='❄':fontcolor=white:x=sin(n/7)*10+w/9:y=n/3-h/20,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=15:text='❄':fontcolor=white:x=cos(n/25)*20+w/2:y=n-h/9,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=20:text='❄':fontcolor=white:x=sin(n/19)*4+w-w/3:y=n-h/20,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=17:text='❄':fontcolor=white:x=cos(n/10)*9+w/7:y=n/2-h/18,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=16:text='❄':fontcolor=white:x=sin(n/20)*23+w-w/7:y=2*n-h/15,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=19:text='❄':fontcolor=white:x=cos(n/15)*15+w/5:y=n-h/6,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=20:text='❄':fontcolor=white:x=sin(n/15)*50+w-w/9:y=n-h/10,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=12:text='❄':fontcolor=white:x=cos(n/30)+w/2:y=n*2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=16:text='❄':fontcolor=white:x=cos(n/9)*3+w/3:y=n/2-h/6,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=19:text='❄':fontcolor=white:x=sin(n/7)*10+w/9:y=n/4-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=15:text='❄':fontcolor=white:x=cos(n/25)*20+w/2:y=n/5-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=20:text='❄':fontcolor=white:x=sin(n/19)*4+w-w/3:y=n-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=17:text='❄':fontcolor=white:x=cos(n/10)*9+w/7:y=n/2-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=16:text='❄':fontcolor=white:x=sin(n/20)*23+w-w/7:y=2*n-h/3,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=19:text='❄':fontcolor=white:x=cos(n/15)*15+w/5:y=n/6-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=20:text='❄':fontcolor=white:x=sin(n/15)*50+w-w/9:y=2*n-h/2,
drawtext=fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:fontsize=12:text='❄':fontcolor=white:x=cos(n/22)+w/2:y=2+h"
-qscale 1 -s 720x560 -t 12 -y snow.mp4
На видео выше результат выполнения этой команды. К сожалению, мне не удалось заставить параметр fontsize принимать в качестве значения результат функции random, скорее всего это пока не поддерживается. Напоследок укажу еще один нюанс с которым я с столкнулся — параметр text тоже не принимает значения вида text=n. Для того чтобы отображать номер кадра используйте конструкцию «text=%{expr\:n}». На этом у меня все, надеюсь вы узнали для себя что нибудь новое.
Автор: ptQa