Category Archives: ffmpeg

ffmpeg: склеиваем два видео-файла в один и исправляем ошибку “width not divisible by 2”

Здоровья и благополучия!

1.mov + 2.mov = output.mp4

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

Итак, самый простой и верный вариант.

Имеется два видео-файлика, 1.mov и 2.mov, мы пытаемся их склеить в output.mp4

Лобовой метод с применением фильтра concat как со списком файликов так и с инжектом в команду не прокатил, возможно, версия моего виндового FFMPEG не подходила, возможно, ещё что. Но нам некогда возиться с апгрейдами, надо всё сделать быстро и в срок.
Continue reading

ffmpeg: создаём fade-through (с переходом затемнения) видео из 3-х рисунков

Итак, у нас имеются 3 рисунка (изображения/фотографии и т.п.).

Мы хотим создать видео с переходами типа fade-in fade-out (затеменения) между ними.

Для удобства приведём риснуки к одному размеру, преобразуем их все в jpg (можно использовать и PNG формат, если хочется), и назовём их img0.jpg, img1.jpg, img2.jpg.

Вот как они могут выглядеть к примеру:

img0.jpg

img0.jpg

img1.jpg

img1.jpg

img2.jpg

img2.jpg

Рисунки в видео? Но зачем???

Теперь мы создадим 2-х минутный видео ролик из 1-ых двух риснуков (img0.jpg и img1.jpg)
с переходом затемнения,
причём в этом случае наш img0.jpg исчезнет быстрее.

Вот команда для ffmpeg (всё в одной строке, я разбил на несколько строк для удобства чтения):

ffmpeg -loop 1 -i img0.jpg -loop 1 -i img1.jpg 
-filter_complex 
"[1:v][0:v]blend=all_expr='A*(if(gte(T,1),1,T/1))+B*(1-(if(gte(T,1),1,T/1)))'" 
-y -c:v libx264 -t 2 fade1.mp4

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

ffmpeg -loop 1 -i img1.jpg -loop 1 -i img2.jpg
-filter_complex
"[1:v][0:v]blend=all_expr='A*(if(gte(T,2),1,T/2))+B*(1-(if(gte(T,2),1,T/2)))'" 
-y -c:v libx264 -t 2 fade2.mp4

Параметры:
-loop в обоих случаях говорит, что для одного вывода надо использовать оба файла
-i img[0|1|2].jpg – задание имён входных рисунков.
-filter_complex – вызов сложного фильтра, который собственно и создаёт эффект затемнения и перехода одного рисунка в другой. Лучше не спрашивайте как он работает, обратите внимание на числа после T, я их подобрал экспериментально для 2-х секундного ролика
-y – перезаписывать выходной файл без вопрсов, если он есть, не обязательный параметр
-c:v libx264 – кодек для видео MP4
-t 2 – длительность видео 2 секунды, для меня достаточно, можно естественно увеилчивать этот парметр, и соотв. менять значения чисел после T в фильтре перехода (-filter_complex)
fade[1|2].mp4 – название выходного файла

Примечание: если ffmpeg выдаёт какую-то тупую ошибку при выполнении этих команд, удостоверьтесь, что все ваши рисунки имеют одинаковое разрешение – не размер, а именно разрешение для печати, измеряется в точках на дюйм обычно (например, открыть рисунки в IrfanView, нажать кнопку i и проверить пункт Resolution, его при желании можно тут же изменять).

Итак, в итоге у нас появятся такие вкусняшки:

Кодируем для DVD

Переделать видео для формата pal-DVD 16:9

ffmpeg -y -i fade1.mp4 -target pal-dvd -aspect 16:9 fade1.mpeg 
ffmpeg -y -i fade2.mp4 -target pal-dvd -aspect 16:9 fade2.mpeg 

Создать картинки из видео в папке 1: Частота кадров 25 в секунду (-r 25) (зачем?)

ffmpeg -i fade1.mpeg -r 25 1/vid%03d.png

ffmpeg + php: Конвертируем в mp3 и нормализуем

Итак, наша задача преобразовать практически любой медиа-файл со звуком в mp3 и нормализовать звук.

Наша рабочая лошадка, которая здорово поможет нам в этом конечно же ffmpeg

ffmpeg

Основная команда кодирования медиа-файла в mp3 такая:

ffmpeg -i input.wma -vn -ac 1 -ab 40K output.mp3

Умный движок ffmpeg пытается угадать кодек по расширению файла для конечного файла, а расширение mp3 однозначно его трактует. Так же для входных файлов он сам распознаёт кодеки.

Параметры:
-vn – отключить кодирование видео, полезно, если мы будем пытаться брать звук из видео-файлов
-ac 1 – число каналов 1 (моно), для нашего проекта моно достаточно, можно поставить 2 для стерео
-ab 40K – аудио-битрейт в 40Кбай так же довольно низок, но для нашего проекта для голосовых записей 40К довольно. Можно указать хоть 128K, хоть 196К – смотрите сколько Вам необохидмо по качеству.

input.wma – входной медиа-файл. В принципе может быть абсолютно любым медиа, хоть аудио, хоть видео, ffmpeg распознаёт наверное все современные медиа-форматы, надо только его время от времени обновлять, так как новые кодеки и даже новые версии старых кодеков появляются регулярно.

output.mp3 – выходной MP3-файл. MP3 стандарт де-факто, не будем спорить с этим, хотя OGG тоже не плох, только по нему меньше шума и маркетинга было.

Вот в принципе и всё для кодирования.

Ниже я напишу как можно управлять ffmpeg из php.

Нормализация

А как же нормализация?

Один из простейших видов нормализации – приведении максимальной (пиковой) громкости к 0 Дб.
Как это сделать – показано ниже.

ffmpeg не имеет встроенного фильтра нормализации, но у него есть фильтр volume, который позволяет увеличивать громкость аудио-дорожки.

Мы можем написать например так:

ffmpeg -i input.wma -vn -ac 1 -ab 40K -af "volume=5dB" -f mp3 output.mp3

и эта команда при конвертировании увеличит громкость аудио на 5 децибел.
Но если мы будем тупо увеличивать громкость всех аудио на 5 децибел, то слишком тихие аудио не особо прибавят в громкости, а громкие аудио станут просто громче.

Если мы попробуем прибавлять громкость например на 15 децибел, то тогда громкие аудио могут начать просто хрипеть, что не есть хорошо для нас. Где же выход?

Изучить громкость аудио, и прибавить столько децибел, сколько потребуется (или вообще не прибавлять, если звук достаточно громкий).

Для изучения громкости аудио в ffmpeg есть аудио-фильтр volumedetect. Вот как им пользоваться:

ffmpeg -i input.wma -af "volumedetect" -f null /dev/null

Сразу отмечу, что в Windows окончание /dev/null надо поменять на NUL.

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

[Parsed_volumedetect_0 @ 0000000002b03b20] n_samples: 13738752
[Parsed_volumedetect_0 @ 0000000002b03b20] mean_volume: -31.8 dB
[Parsed_volumedetect_0 @ 0000000002b03b20] max_volume: -15.2 dB
[Parsed_volumedetect_0 @ 0000000002b03b20] histogram_15db: 696
[Parsed_volumedetect_0 @ 0000000002b03b20] histogram_16db: 78935

В этом захватывающем воображении выводе от ffmpeg нас будет интересовать только параметр max_volume, который в идеале должен стремиться к 0.

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

Итак, процесс нормализации будет выглядеть так:

1. Изучаем вывод ffmpeg с фильтром volumedetect.
2. Парсим значение с max_volume и сохраняем его в переменной
3. Конвертим медиа-файл в mp3 с установленным фильтром volume=наша_переменнаяdB

На PHP данная задача решается в несколько строк кода и в следующем разделе мы увидим, как это действительно просто.

Кодим конвертер и нормализатор на PHP

В PHP мы будем использвовать вызов системной функции exec, которая будет вызывать ffmpeg с нужными параметрами.

Подразумевается, что ffmpeg установлен в нашей системе (на нашем сервере).

Итак, вот готовый код на PHP:

// пробуем конвертить файл в mp3 с помощью ffmpeg
// ffmpeg -i $rec -vn -ac 1 -ab 40K -f mp3 dest.mp3
 
$dest = uniqid('rec_').'.mp3'; // файл назначения
 
// Далее - наш медиа файл для конвертации и нормализации
// в принципе может быть любым медиа-файлом, который распознаёт ffmpeg,
// а он распознаёт много, если не всё
$src = 'somemediafile.amr';
 
$out = array(); // массив для получения результатов вывода ffmpeg
// вызываем ffmpeg в первый раз для получения инфо о громкости
// обратите внимание на окончание команды 2>&1,
// оно необходимо так как ffmpeg выпоняет вывод в stderr а не в stdout
// а exec перехватывает только вывод от stdout
exec("ffmpeg -i $src -af \"volumedetect\" -f null /dev/null 2>&1", $out);
 
// теперь пытаемся нормализовать звук тупо по max_volume стремящимся к 0dB
// доп.комментарии смотрите после этого листинга
$db = '';
for($i=0; $i < count($out); $i++)
	if(preg_match('/max_volume:\s+-?(\d+)/',$out[$i],$m)) {
		if(intval($m[1]) > 0)
                  $db = "-af \"volume={$m[1]}dB\"";
		break;
	}
 
$cmd = "ffmpeg -i $src -vn -ac 1 -ab 40K $db -f mp3 $dest";
exec($cmd);
 
// проверим наличие сконверженного файла
if(filesize($dest) == 0) {
	@unlink($dest);
	echo 'Ошибка конвертирования записи';
}
else
// теперь в $dest у нас должен остаться конечный нормализованный звук в mp3 формате
  echo 'Ok. Получили файл '.$dest;

В коде есть комментарии, но здесь можно прояснить ещё пару моментов.

Мы используем preg_match(‘/max_volume:\s+-?(\d+)/’,$out[$i],$m) для поиска и вычленения положительного значения max_volume из строки вида [Parsed_volumedetect_0 @ 0000000002b03b20] max_volume: -15.2 dB.

Тут мы отбрасываем любое кол-во пробелов и возможный знак `-` после подстроки max_volume:,
и затем считываем целое значенире в переменную массива $m[1].

Если оно больше нуля, то мы добавляем в вызов второй команды ffmpeg фильтр “-af \”volume={$m[1]}dB\””,
чтобы усилить звук, иначе мы опускаем этот параметр вовсе.

Второе. Чтобы понять, что мы получили конечный файл, мы проверяем его размер. Если попытка конвертации не удалась по какой-то причине (неверный или неизвестный ffmpeg медиа-формат файла), то размер этого файла будет равен 0.

Это ещё не всё

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

Хочется чтобы всё было ОК.

Я присматривался к утилите sox, но она для mp3 требует MAD, а я не люблю этого.

Я люблю когда всё работает out of the box, к чему и сам стремлюсь.

Удачи вам, друзья, до новых встреч!