惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
SecWiki News
SecWiki News
博客园_首页
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
P
Palo Alto Networks Blog
V
Vulnerabilities – Threatpost
Project Zero
Project Zero
WordPress大学
WordPress大学
NISL@THU
NISL@THU
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
Jina AI
Jina AI
AWS News Blog
AWS News Blog
Scott Helme
Scott Helme
Martin Fowler
Martin Fowler
C
Cybersecurity and Infrastructure Security Agency CISA
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
小众软件
小众软件
I
Intezer
A
Arctic Wolf
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
O
OpenAI News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
G
GRAHAM CLULEY
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
N
News and Events Feed by Topic
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
V2EX - 技术
V2EX - 技术
Stack Overflow Blog
Stack Overflow Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 最新话题
博客园 - Franky
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
P
Proofpoint News Feed
S
Secure Thoughts
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
T
The Exploit Database - CXSecurity.com
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
F
Full Disclosure
Security Latest
Security Latest

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Зачем мне фото- и видеоредакторы с GUI, когда есть FFmpeg?
Леонид · 2026-05-31 · via Все публикации подряд на Хабре

Простой

17 мин

18K

День 1. "Мне больше не придётся крякать Фотошоп и Вегас!".
День 2. Научился блюрить лица и айпишники через командную строку.
День 4. Провёл сравнительное тестирование аппаратных и программных видеокодеков.
День 8. Натравил ИИ-агентов на задачу по монтажу видео.
День 16. Объясняю жене, почему не могу просто запустить Paint и нажать две кнопки.

UPD. В комментариях неравнодушные написали много полезного. В конце статьи привёл список важных дополнений.

Хабр, привет! В последнее время моя работа во многом сосредоточена на вопросах Remote Desktop и стриминга медиа в реальном времени. Я часто использую FFmpeg в работе - и со временем всё чаще и чаще.

В этой статье я покажу примеры того, что я периодически делаю через FFmpeg не покидая командную строку и не переходя в редакторы с тяжеловесным GUI. С компактно изложенными пояснениями об устройстве самого FFmpeg. И с контекстом.

Запасайтесь сэмплами - вперёд!

Навигатор по статье

1. Контекст для введения

2. Базовые примеры

2.1. Транскодирование видео

2.2. Сжатие GIF

2.3. Извлечение кадров из видео

2.4. Извлечение аудиодорожки из видео

3. Более интересные примеры

3.1. Конвертация SVG -> PNG

3.2. Текст на фото

3.3. Прямоугольное выделение на фото

3.4. Размытие области на фото/видео

3.5. Коллаж из кадровой развёртки

3.6. Водяной знак

3.7. Пакетная обработка

4. Внезапные примеры

4.1. Эквалайзер

4.2. Визуализация векторов движения

4.3. Визуализация потерь

4.4. ASCII-кинотеатр

5. Объяснение, что мне это всё дало

6. Заключение

Контекст

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

FFmpeg/libav - это проект с открытым исходным кодом. Суть - кросс-платформенная библиотека на С, объединяющая под одним зонтиком тонны более низкоуровневых инструментов для работы с видео и аудио. Среди этих инструментов: библиотеки, реализующие кодеки (напр. libx264 для видеокодека H.264 или libopus для аудиокодека Opus); абстракции для доступа к аппаратно ускоренным функциям (напр. AVFoundation для платформ Apple или Quick Sync Video для кросс-платформенного доступа к медиа-акселератору внутри десктопных процессоров Intel); самостоятельно реализованные операции над видео и аудио; и другое.

FFmpeg можно использовать как утилиты командной строки (собственно ffmpeg + дополнительно ffplay и ffprobe) и вводить пайплайн на исполнение (прото-промпт), а можно подключать как библиотеки и пользоваться внутренним API самих компонентов.

Компоненты FFmpeg

FFmpeg состоит из следующих библиотек (= умеет делать следующее):

  • libavcodec - энкодеры и декодеры для аудио и видео. Все задачи по сжатию, распаковке и смене кодеков - это сюда.

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

  • libavfilter - фильтры для преобразования данных, организованные в виде математических функций. Уместно думать о фильтрах как о графовых нодах. Такое представление позволяет создать цепочку фильтров в виде графа и собрать пайплайн практически любой сложности. Поменять разрешение видео, вырезать из гифки половину цветов, поменять тональность звука - это сюда.

  • libavformat - библиотека для работы с контейнерыми форматами (например, мультиплексоры и демультиплексоры). Вычленить конкретную аудиодорожку из фильма с семью дубляжами, скачанного с торрента - это сюда.

  • libavutil - внутренняя кухня с вспомогательным кодом сложена здесь. Как пользователь командной строки, эту библиотеку вы нигде не заметите.

  • libswresample - набор операций для преобразования аудиоданных собственной разработки. Задумка такая: дать полный набор в виде софтварной реализации, а производители устройств и авторы плагинов могут предоставлять свои аппаратно ускоренные альтернативы. Ресэмплинг (смена частоты дискретизации) аудио - это сюда.

  • libswscale - то же самое, что и в предыдущем пункте, но уже для видео. Масштабирование изображения, смена цветового формата или data layout - это сюда.

FFmpeg - это бэкенд многих аудио- и видеоплееров, например, для VLC. И для других библиотек/фреймворков - тот же GStreamer имеет собственные реализации многих кодеков, но всё остальное подключает через интеграцию с FFmpeg.

Базовые примеры

В этом разделе будет много пересечений с другой статёй про FFmpeg, имеющей схожий фокус. Здесь я говорю про то, за что FFmpeg известен широкому кругу пользователей.
Если Вы знакомы с FFmpeg на базовом уровне, то рекомендую не тратить время и переходить в следующий раздел.

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

  1. Смена кодека. Можно, например, перекодировать видео из H264/H265 в контейнер WebM с кодеком VP9, чтобы видео было совместимо с бОльшим числом пользовательским устройств. WebM - это, например, YouTube, а значит, откроется с любого браузера, в то время как с поддержкой H265 в браузерах могут быть проблемы (напр. Firefox, иногда - Safari).

  2. Повышение коэффициента сжатия. Я чаще всего это вижу при записи экрана: встроенные средства зачастую сохраняют файлы в отличном качестве, но есть и обратная сторона - вес. У меня так со встроенным рекордером в GNOME. Для отправки по сети коллегам, ровно как и для публикации нужно минимизировать размер.

Транскодирование видео

Пожалуй, самое простое, что можно сделать. Задаём видео на вход (input, -i), целевой кодек (video codec, -c:v), и имя выходного файла (без ключей):

ffmpeg -i ./input.mp4 -c:v libvpx-vp9 ./output.webm

Чем FFMPEG хорош - помимо списка кодеков, не влезающих ни в один экран здорового человека, - так это фильтрами и мультиплексированием. Фильтров, как и кодеков, огромное количество и они напоминают определение математической функции из школы: вход, преобразование, выход. И их можно объединять в цепочку, получая за один проход все нужные вам изменения.

Добавим в наш простейший пример фильтр (video filter, -vf) на смену разрешения, потому что для наших (демонстрационных) целей повышенная детализация ни к чему:

ffmpeg -i ./input.mp4 -c:v libvpx-vp9 -vf "scale=640:-1" ./output.webm

-1 вместо вертикального разрешения - это автоматический выбор значения с сохранением пропорций. Например, если видео было в разрешении 1920х1080, то конструкция scale=640:-1 понизит его до 640х360.

Пример. Возьму случайный футаж с одного из прошлых мест работы: запись экрана на NVIDIA Shadowplay в 1080p, простая сцена с примитивной 3D-моделью в VR. Я хочу показать этот футаж прямо здесь, но Хабр позволяет загружать только GIF (с ограничением сверху на размер файла), поэтому пойдём по порядку.

Картинка по цветам довольно сочная, запись в 60 FPS. Аппаратные видеоэнкодеры NVIDIA оптимизированы больше на скорость нежели на степень сжатия, а потому у видео вес немаленький - 36 мегабайт за 10 секунд (при том, что это уже H264).

Применяем первую команду - и размер сократился до 22.2 МБ. Применяем вторую - и уже 2.4 МБ. Если заменим кодек VP9 (libvpx-vp9) на H265 (libx265) - то размер сократится до 4.3 МБ и 720 КБ соответственно.

Про параметры видеоэнкодеров

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

Иллюстрация треугольника компромиссов при работе с энкодерами (Gemini)

Иллюстрация треугольника компромиссов при работе с энкодерами (Gemini)

Замечали, что YouTube и другие хостинги жмут загружаемое видео подолгу? Для платформодержателя важно сэкономить своё внутреннее дисковое пространство и дать пользователю качественный поток, поэтому ты, дорогой автор, подождёшь...

Никто не запрещает вместо webm сделать формат вывода gif:

ffmpeg -i ./vr-dummy.mp4 -vf "scale=640:-1" ./vr-dummy.gif

Специальные опции не нужны, FFmpeg сам всё выберет исходя из расширения выходного файла. И, в отличие от mp4 и webm, формат gif уже можно пригоден для вставки как вложение в статью на Хабре. Но вес стал снова большим - 25.6 МБ даже в разрешении 640х360. Поэтому...

Сжатие гифки

GIF - это ведь последовательность кадров, а не видео, поэтому формат по определению прожорливый, что нас не всегда устраивает. Вот что можно сделать:

ffmpeg -i ./vr-dummy.mp4 \
-filter_complex "[0:v] fps=6,crop=iw:ih-130:0:90,scale=iw/3:-1,split [a][b];\
[a] palettegen=max_colors=32 [p];\
[b][p] paletteuse=dither=bayer:bayer_scale=3" \
./vr-dummy.gif

Здесь применён специфический фильтр модификации палитры, поэтому команда стала сложнее. Разберу все неочевидные заклинания команды по порядку (объясняю как можно короче):

  • -filter_complex вместо -vf - потому что фильтр для палитры устроен так, что нужно склонировать видеопоток и прогонять его дважды (параллельно). -vf по умолчанию работает только с линейной цепочкой фильтров;

  • [0:v] - указание, что фильтр дальше относится к видеопотоку с индексом 0;

  • fps=6 - урезаем частоту кадров, поскольку для гифки 60 FPS - выбор странный (*"шесть кадров..."(с)*);

  • crop=iw:ih-130:0:90 - убираю лишние элементы сверху (рамка окна) и снизу (панель задач Windows). iw - ширина региона совпадает с шириной видео, 0 - начало региона слева совпадает с началом видео, ih-130 - высота региона меньше высоты видео на 130 пикселей (90 на рамку окна + 40 на панель задач), 90 - начало региона сверху отступает от начала видео на 90 пикселей.

  • scale=iw/3:-1 - уменьшаем ширину в 3 раза от исходника, высоту уменьшаем пропорционально. Исходник имеет разрешение 1920х1080, поэтому гифка будет в 640х360, как и в прошлый раз;

  • split [a][b] - форкаем дорожку с видео и даём им имена a и b. По сути - то же самое, что назвать переменную;

  • [a] - указание, что фильтр далее относится к форку a;

  • palettegen=max_colors=32 - урезаем число цветов с 256 (стандарт в GIF) до 32 и на выходе получаем палитру кадра. Моя гифка от этого сильно пострадает, но сильно облегчится - во многих случаях понижение разрядности для хранения цветов может остаться незамеченным;

  • [p] - урезанную палитру именуем как p;

  • [b][p] - указание, что фильтр далее совмещает форк b и нашу палитру p;

  • paletteuse=dither= - берём кадр и добавляем к ней палитру с дизерингом - сглаживанием резких цветовых переходов. Базовый вариант после знака "равно" здесь none, но он оставляет все цветовые переходы как есть, поэтому...

  • bayer:bayer_scale=3 - самый популярный вариант сглаживания. Параметр изменяется от 0 (цвета ровные, весит больше) до 5 (цвета резкие, весит минимально).

И, наконец, Вы можете увидеть мой старый футаж:

Гифка, сконвертированная с большими потерями качества из видео

Гифка, сконвертированная с большими потерями качества из видео

Итого, видео на 6 секунд в виде гифки весит 3.2 МБ против 25.6 МБ в конце предыдущего раздела. Пусть качество и оставляет желать сильно лучшего, понять происходящее можно.

Напоминаю задачу: преобразовать в GIF и влезть в ограничения по размеру от Хабра. Если воображаемому коллеге понадобится полноценный вариант, то я уже вышлю перекодированный исходник в VP9 или H265, где всё чётко и в 60 FPS. Или гифку, но без тотального сжатия.

А шо вы хотели?...

Извлечение кадра из видео

Едем дальше. Интересный пример - вытаскивание скриншота из видео. Конечно, можно вырезать стоп-кадр из окна с видеоплеером ножницами - и не обязательно экранными ;) Но мы так теряем в качестве и получаем произвольное разрешение, зависящее от размеров окна, а не от размеров видео. А можно поступить вот так:

# По таймкоду
ffmpeg -i ./vr-dummy.mp4 -ss 00:00:07.00 -frames:v 1 vr-dummy-frame.jpg
# По номеру кадра
ffmpeg -i ./vr-dummy.mp4 -vf "select=eq(n\,420)" -frames:v 1 ./vr-dummy-frame.jpg

Тяжело определить конкретный номер кадра или таймкод? Не проблема! Давайте сгрузим покадрово целую секунду из видео и выберем себе то, что нам нужно:

mkdir -p ./vr-dummy-frames/ \
&& ffmpeg -ss 00:00:07 -i ./vr-dummy.mp4 -t 1 ./vr-dummy-frames/frame-%03d.jpg

Здесь:

  • -ss - таймкод старта. Видео интересует нас начиная отсюда;

  • -t - длительность в секундах. Альтернатива: использовать -to, описывающий таймкод конца.

Получим ровно 60 кадров:

zaburunovleonid@fedora:~/$ ls ./vr-dummy-frames/
frame-001.jpg  frame-006.jpg  frame-011.jpg  frame-016.jpg  frame-021.jpg  frame-026.jpg  frame-031.jpg  frame-036.jpg  frame-041.jpg  frame-046.jpg  frame-051.jpg  frame-056.jpg
frame-002.jpg  frame-007.jpg  frame-012.jpg  frame-017.jpg  frame-022.jpg  frame-027.jpg  frame-032.jpg  frame-037.jpg  frame-042.jpg  frame-047.jpg  frame-052.jpg  frame-057.jpg
frame-003.jpg  frame-008.jpg  frame-013.jpg  frame-018.jpg  frame-023.jpg  frame-028.jpg  frame-033.jpg  frame-038.jpg  frame-043.jpg  frame-048.jpg  frame-053.jpg  frame-058.jpg
frame-004.jpg  frame-009.jpg  frame-014.jpg  frame-019.jpg  frame-024.jpg  frame-029.jpg  frame-034.jpg  frame-039.jpg  frame-044.jpg  frame-049.jpg  frame-054.jpg  frame-059.jpg
frame-005.jpg  frame-010.jpg  frame-015.jpg  frame-020.jpg  frame-025.jpg  frame-030.jpg  frame-035.jpg  frame-040.jpg  frame-045.jpg  frame-050.jpg  frame-055.jpg  frame-060.jpg

Извлечение аудиодорожки из видео

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

ffmpeg -i ./input_video.mp4 -vn -c:a copy ./audio.mp3

Здесь:

  • -vn - отбросить видео поток вообще (*video null*);

  • -с:a copy - использовать аудио в исходном виде, без нового кодека (*audio codec* по аналогии с video codec, -c:v).

Консольный редактор

В разделе выше было показано то, из-за чего FFmpeg в основном все знают: кодирование/декодирование аудио/видео, смена формата контейнера и другое. А теперь - к коллекции кейсов поинтереснее.

Конвертация SVG в PNG (и не только)

Чем Вы обычно пользуетесь для смены формата изображения, например, из SVG в PNG? Раньше я использовал веб-конвертеры (напр. Convertio). Ещё раньше я использовал Adobe Photoshop. Про всё это я давно забыл и иду в терминал:

ffmpeg -i ./logo.svg ./logo.png

Примитивная команда - полезный результат, т. к. SVG может поддерживаться не везде.

На всякий случай отмечу, что FFmpeg по задумке не предназначен для изображений. Например, обратно в SVG сконвертировать не получится. Для этих целей лучше использовать всякие инструменты а-ля Image Magick.

Текст на фото

Рисование кистью как в редакторах, встроенных в запрещённую соцсеть для фоточек, которую я решительно осуждаю и Telegram, конечно, не прокатит. Но наложить текст (хоть для пояснения коллеге прямо на скриншоте, хоть для публикации истории в те же соцсети) вполне себе можно:

ffmpeg -i ./vr-dummy-frames/frame-001.jpg \
-vf "drawtext=\
fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf:\
text='VR Frame #001':\
fontsize=48:fontcolor=white:\
x=(w-text_w)/2:y=h-text_h-30:\
shadowcolor=black:shadowx=3:shadowy=3" \
./vr-dummy-frames/frame-001-text.jpg

Здесь:

  • drawtext - видеофильтр наложения текста;

  • fontfile - чем пишем;

  • text - что пишем;

  • fontsize, fontcolor - как пишем;

  • x и y - где пишем;

  • shadowcolor - дополнительно рендерим тень от текста.

Результат наложения текста на скриншот, полученный в одном из разделов выше

Результат наложения текста на скриншот, полученный в одном из разделов выше

Прямоугольное выделение на фото

Собственно, помимо текста можно подсветить важную область на скриншоте цветным прямоугольником и отправить гневный комментарий своим коллегам без подписей к фото:

ffmpeg -i ./input.jpg \
-vf "drawbox=x=100:y=50:w=200:h=150:color=red@0.8:t=5" \
./output.jpg

Здесь:

  • drawbox - видеофильтр наложения прямоугольной рамки;

  • x, y w и h - где рисуем;

  • color - каким цветом рисуем, @0.8 - прозрачность цвета;

  • t - кистью какой толщины (в пикселях) рисуем. Можно написать fill - прямоугольник будет полностью закрашен.

Пример. Беру один из старых рабочих скриншотов и задаю прямо на нём вопрос коллеге:

ffmpeg -i ~/Pictures/screenshot.png \
-vf "drawtext=\
fontfile=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf:\
text='What the hell is 0\:0?':\
fontsize=48:fontcolor=red:\
x=(w-text_w)/2-400:y=h-text_h-150:\
shadowcolor=black:shadowx=3:shadowy=3,\
drawbox=x=375:y=ih-40:w=120:h=40:color=red@0.5:t=2,\
scale=960:-1" \
~/Pictures/screenshot-note.png

Input -> drawtext -> drawbox -> scale -> Output:

Результат рисования прямоугольной области и текста на скриншоте

Результат рисования прямоугольной области и текста на скриншоте

Координаты, конечно, приходится высчитывать самому - и проходить через несколько итераций. Интерактивного превью у FFmpeg нет. Это безусловный недостаток при работе в командной строке. Для более быстрого итерирования я использую цепочку команд с открытием результата по готовности: ffmpeg -i ./input.mp4 … ./output.mp4 && xdg-open ./output.mp4

Размытие области на фото/видео

Обратите внимание на скриншот из примера выше. Там сверху размытая область. Это я заблюрил айпишник внутреннего хоста в рабочем ЦОДе.

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

А как убрать? Открыть видеоредактор, руками выбрать область размытия и разместить эффект, например, отдельной дорожкой с ограничением по времени? Да нет же:

ffmpeg -i ~/Pictures/screenshot-note.png \
-filter_complex "[0]crop=50:20:470:20[ip];\
[ip]scale=iw/5:ih/5,scale=iw*5:ih*5:flags=neighbor[px];\
[0][px]overlay=470:20" \
~/Pictures/screenshot-note-blur.png

Блюров в FFmpeg несколько, даже есть отдельные фильтры. Я выбрал самый простой вариант - пикселизацию через downscale.

Это пример для фото. Для видео одно замечание: если нужно блюрить только конкретный временной отрезок, то к фильтру overlay нужно добавить параметр enable: overlay=470:20:enable='between(t,2,4)

Коллаж из кадровой развёртки

Как получить превью в виде склейки нескольких кадров? Вырезать кадры по очереди и выдр...высматривать пиксели в Paint при стыковке? Избавьте:

ffmpeg -ss 00:00:18 -i ./vr-block1.mp4 -frames:v 1 \
-vf "fps=1/8,crop=iw-600:ih-130:400:90,scale=480:-1,\
tile=4x1:padding=4:margin=4:color=white" \
./vr-block1-preview.jpg

Здесь:

  • fps=1/8 - частота сбора кадров для коллажа;

  • crop и scale - управление областью видео, из которого берём кадры для коллажа;

  • tile - фильтр создания склейки, padding и margin - границы итоговой склейки, color - цвет границы.

Коллаж из покадровой развёртки: 18-я, 26-я, 34-я и 42-я секунды видео

Коллаж из покадровой развёртки: 18-я, 26-я, 34-я и 42-я секунды видео

Это, кстати, отлично сочетается с пунктом про извлечение кадров.

Водяной знак

Выше я размещал иллюстрации от Gemini. В правом нижнем углу у каждого сгенерированного изображения всегда стоит водяной знак в виде логотипа ИИ от Google. Как он это делает, не генерирует же?

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

ffmpeg \
-i ./vr-dummy-frames/frame-001-text.jpg \
-i ~/Downloads/habr-pill.png \
-filter_complex "[0] scale=720:-1 [src];\
[1] format=rgba,colorchannelmixer=aa=0.8 [watermark];\
[src][watermark] overlay=W-w-20:H-h-20" \
./vr-dummy-frames/frame-001-text-watermark.jpg

Здесь:

  • -i дважды - входное изображение и водяной знак;

  • -filter_complex - потому что у нас два входных потока, линейный фильтр не работает;

  • [0] scale=720:-1 [src] - уменьшаю размер входного изображения и называю поток src;

  • [1] format=rgba,colorchannelmixer=aa=0.8 [watermark] - делаю alpha-premultiply для водяного знака, 80% непрозрачности, и называю поток watermark;

  • [src][watermark] overlay - совмещаем оба потока прямым наложением (прозрачность здесь учитывается);

  • W-w-20:H-h-20 - координаты размещения водяного знака. W и H - ширина левого слоя (=операнда), w и h - правого. Указание через заглавные и строчные буквы - соглашение в фильтре overlay

И вот один из скриншотов выше с добавлением водяного знака:

Добавление водяного знака на скриншот из раздела выше

Добавление водяного знака на скриншот из раздела выше

Пакетная обработка

Как вы себе представляете групповую операцию для любого из сценариев выше? Или, скажем, для снимков фотосессии? У вас много файлов в неподходящем вам виде: слишком много весят, слишком большое разрешение, не тот формат, ...

Через GUI, скорее всего, придётся открывать каждый файл и сохранять его в другом качестве. В веб-конвертерах иногда есть возможность выбора нескольких файлов, но это надо уходить в браузер, прокликать до папки с исходниками, затем - выбрать, куда сохранить, как назвать...

А что если...

for f in ./vr-dummy-frames/*.jpg; do
  ffmpeg -v quiet \
    -i "$f" -i ~/Downloads/habr-pill.png \
    -filter_complex "[0] scale=[src];\
    [1] format=rgba,colorchannelmixer=aa=0.6 [watermark];\
    [src][watermark] overlay=W-w-20:H-h-20" \
    "$f-watermark.jpg";
done

Это я наложил водяной знак - точно так же, как в разделе выше, но для всех 60 кадров, полученных из раскадровки здесь.

Сравните количество действий на одну фотографию с условным Adobe Lightroom.

Полный расколбас

Ну а теперь немного н*ркомании. Неочевидные команды, дающих интересные визуализации. Сложно представить себе сценарии применения многих команд в работе, но в личных целях возможно всё ;)

И заодно возможности FFmpeg наглядно оценим...

Эквалайзер

Предлагаю поностальгировать по временам Winamp и добавить к MP3 визуализацию:

ffmpeg -i ~/Music/Queen_We_Will_Rock_You.mp3  \
-filter_complex  \
"[0:a]showfreqs=s=1280x720:mode=bar:ascale=log:fscale=log:colors=0x1D9E75,format=yuv420p[v]" \
-map "[v]" -map 0:a -c:v libx264 -c:a copy we_will_rock_you_vis.mp4
Визуализация первой секунды "Queen - We Will Rock You"

Визуализация первой секунды "Queen - We Will Rock You"

Или ещё развёрнутее:

ffmpeg -ss 00:00:22.0 -i ~/Music/Queen_-_We_Will_Rock_You_47828511.mp3 -t 08 \
-filter_complex "
[0:a]showspectrum=s=640x360:slide=scroll:color=rainbow:scale=log[spec];
[0:a]showwaves=s=640x180:mode=cline:colors=0x00ff88[wave];
[0:a]avectorscope=s=320x540:zoom=1.5:draw=line[vec];
[spec][wave]vstack[left];
[left][vec]hstack
" \
-map 0:a -c:a libopus \
-c:v libvpx-vp9 -crf 18 \
we_will_rock_you_portrait_cut.webm
Визуализация "Queen - We Will Rock You", в этот раз - поближе к припеву

Визуализация "Queen - We Will Rock You", в этот раз - поближе к припеву

Визуализация работы кодека

Видеокодеки - это магия. Они умеют так много всего, что это сложно описать, ещё сложнее - представить визуально. Но FFmpeg умеет раскладывать кодек на дельты с векторами движения:

ffmpeg -flags2 +export_mvs \
-i ./town-cut.mp4 \
-vf "scale=iw/3:-1,hue=s=0,codecview=mv=pf+bf+bb:qp=1" \
-pix_fmt gray \
./town-cut-vis.gif

Визуализация потерь

Здоровый человек ничего не знает про видеокодеки - за исключением того, что видео весят меньше последовательности картинок. Но какие при этом происходят потери, пусть даже если глазу зрителя они не видны? Узнаем лобовым сравнением трёх показателей качества на примере опции -crf:

# Готовим три варианта
ffmpeg -i ./superposition_cut.mp4 -c:v libx264 -crf 18 ./superposition_crf18.mp4
ffmpeg -i ./superposition_cut.mp4 -c:v libx264 -crf 28 ./superposition_crf28.mp4
ffmpeg -i ./superposition_cut.mp4 -c:v libx264 -crf 40 ./superposition_crf40.mp4
# Визуализируем дифф всех трёх на одном экране
ffmpeg -i ./superposition_cut.mp4 -i superposition_crf18.mp4 -i superposition_crf28.mp4 -i superposition_crf40.mp4 -filter_complex   "[0][1]blend=all_mode=difference,format=gray,curves=all='0/0 0.01/0.5 0.1/0.9 1/1'[d1];
   [0][2]blend=all_mode=difference,format=gray,curves=all='0/0 0.01/0.5 0.1/0.9 1/1'[d2];
   [0][3]blend=all_mode=difference,format=gray,curves=all='0/0 0.01/0.5 0.1/0.9 1/1'[d3];
   [d1]drawtext=text='CRF 18 (diff x10)':fontsize=28:fontcolor=white:x=10:y=10[a];
   [d2]drawtext=text='CRF 28 (diff x10)':fontsize=28:fontcolor=white:x=10:y=10[b];
   [d3]drawtext=text='CRF 40 (diff x10)':fontsize=28:fontcolor=white:x=10:y=10[c];
   [a][b][c]hstack=inputs=3"   superposition_crf_diff.mp4

Вот исходник (пережатый для вставки в виде гифки):

Запись работы бенчмарка Unigine Superposition

Запись работы бенчмарка Unigine Superposition

А вот что получим при визуализации потерь:

Сравнение потерь записи работы бенчмарка Unigine Superposition при разных crf (18, 28 и 40 соот-но)

Сравнение потерь записи работы бенчмарка Unigine Superposition при разных crf (18, 28 и 40 соот-но)

Из интересного: при -crf 40 потери видны даже на статичном тексте. Вот этот вариант отдельно:

Своего рода тепловая карта потерь кодека при перекодировании с -crf 40

Своего рода тепловая карта потерь кодека при перекодировании с -crf 40

ASCII-кинотеатр

Напоследок - пример, который больше не про FFmpeg. Я хочу выводить видео прямо в терминал, а для этого понадобится обвязка на Python. Полный код оставлю в комментариях, а здесь - команда запуска: python3 ./ascii-cinema.py ~/Downloads/rick-astley.gif

Трейлер GTA 4 в каждый дом!

Трейлер GTA 4 в каждый дом!

А что это всё даст?

У такого подхода, как работа (с медиа) в командной строке, есть много достоинств - и, конечно, свои недостатки. Изложу своё видение в этом разделе.

Дело совсем не в задачах по типу "вырезать из скриншота квадрат x:y:w:h". Я начинал с FFmpeg по прямому назначению: сменить кодек у аудио или видео, понизить разрешение видео для уменьшения размеров и т. д. А затем заметил, что за время на открытие программы с GUI зачастую можно решить вопрос через терминал "от и до".

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

Действие

Через указатель с клавиатурой

Через клавиатуру с хоткеями

Переключение вкладок

Перевод курсора через весь экран с необходимостью точного позиционирования

Alt+1/2/3/4

Выделение текста

Зажатие ЛКМ и перенос курсора руками

Зажатие Shift с навигацией по тексту с помощью клавиатуры.

Скажем, Shift+End - выделить всё от текущей позиции курсора до конца строки

Управление положением окна на экране

Ручное изменение размеров через хэнлды рамок окна

Win+Up/Down/Left/Right
(для GNOME)

Перенос окна между мониторами

Ручной перенос окна через зажатие ЛКМ

Win+Shift+Left/Right
(для GNOME)

Перенос окна между рабочими пространствами (desktops, workspaces)

Переход в меню десктопов через системный хоткей, затем - ручной перенос окна через зажатие ЛКМ

Win+Alt+Shift+Left/Right
(для GNOME)

... и много чего ещё, ведь современные популярные IDE (и не только) имеют массу хоткеев для ускорения работы.

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

Про Claude Code

Кстати, по той же причине я использую Claude Code в терминале через tmux:

  • На одной половине основного монитора - терминал с LLM;

  • На другой - командная строка (для сборки проекта, использования git и минимального редактирования файлов через nano);

  • На втором мониторе - IDE с проектом (для чтения проекта и больших работ с файлами);

  • Ну а на третьем мониторе - браузер (для всего остального есть Mastercard).

Иногда пользуюсь плагином для VS Code, если не хочется вылезать из IDE :)

Хоткеи и минимизация мыши в своей работе (напр. с помощью tmux) дали мне выигрыш во времени и энергии в разы:

  • Времени - потому что управление клавиатурой быстрее;

  • Энергии - потому что мозг остаётся в потоке (конечно, после того, как наработается привычка).

А в режим работы с курсором можно переходить тогда, когда альтернатив нет - например, для ревью MR в GitLab.

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

Да, это потребует привыкания и перестройки. Да, не хватает наглядности и интерактивности. Но вы же прибегаете к использованию регулярных выражений, а не обрабатываете строки каждый раз через стандартную библиотеку языка C, верно?...

Заключение

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

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

Такие дела, братюни! Не пренебрегайте инструментами командной строки, и уж тем более добротной визуализацией данных ;) Счастливо!


UPD. Дополнения от комментаторов

Как водится, комментарии к статье иногда бывают не менее полезными, чем сама статья. Рекомендую прочитать следующие дополнения:

  1. @Emelian продемонстрировал в своём комментарии, как FFmpeg можно использовать для работы с субтитрами. У данного автора также есть статьи на тему изучения иностранных языков, где этот кейс раскрывается с практической задачей: тык.

  2. @andreymal сделал серию важных уточнений к содержимому статьи.

  3. @KPbICMAHзацепился за мой подход по скачиванию видео с YouTube через веб-плагины и сделал отличное дополнение про использование yt-dlp. Утилита исключительно полезная, рекомендую к прочтению. Также есть потенциал для автоматизации в цепочке с FFmpeg ;)