Русский Русский English English

ffmpeg сборка в MKV

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

ffmpeg, создание MKV с большим количеством аудиодорожек

Итак исходные данные - имеется файл или набор исходных файлов, содержащих некоторое количество дорожек (аудио/видео/субтитры). Задача - выбрать необходимые дорожки и занести их в один результирующий файл формата MKV.

Тут следует сделать небольшое отступление, почему нельзя воспользоваться выделенным решением с графическим интерфейсом, специально разработанным для работы с MKV-контенером, таким как MKV merge (GUI)? Именно так раньше я и делал. Но оказалось, что ffmpeg умеет делать больше. В частности, видеофайлы со звуковой FLAC-дорожкой гораздо лучше перематываются в видеопроигрывателе, если они изначально собраны с помощью ffmpeg. Кроме того, промежуточные операции над отдельными дорожками могут быть излишними, особенно если все задачи можно поручить разом ffmpeg.

Так в чём собственно загвоздка? А загвоздка как всегда в консоли. С помощью консольного интерфейса очень удобно писать скрипты и ставить преобразование файлов на поток. Но вот дружиться с ним иногда очень непросто. Что самое простое может сделать пользователь?

# ffmpeg -i InputFile.avi -vcodec copy -acodec copy OutputFile.mkv

Указываем исходный файл, видео и аудиокодеки (copy - означает копировать без пересжатия), ну и конечно же выходной файл. Всё просто? Но вот у нас несколько файлов, много дорожек, и мы хотим обработать каждую из них. Для начала посмотрим, что ffmpeg видит в выбранных файлах:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac

Вывод данной команды может быть вроде этого:

Input #0, mpeg, from 'InputFile1.vob':
  Duration: 00:03:28.28, start: 1.000000, bitrate: 4268 kb/s
    Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 6000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc
    Stream #0.1[0x80]: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.2[0x1c0]: Audio: mp2, 48000 Hz, 2 channels, s16, 64 kb/s
Input #1, ac3, from 'InputFile2.ac3':
  Duration: 00:03:28.35, bitrate: 192 kb/s
    Stream #1.0: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
Input #2, flac, from 'InputFile3.flac':
  Duration: 00:03:28.35, bitrate: 705 kb/s
    Stream #2.0: Audio: flac, 48000 Hz, 2 channels, s16, 768 kb/s

Самое интересное из этого списка для нас - это нумерация дорожек. Каждая дорожка имеет два идентификацирующих номера, первый - номер исходного файла (каким по очереди вы его добавили флагом -i), второй - внутри этого файла. Итак, что же мы можем сделать с этим зверинцем? Давайте попробуем 'просто' указать выходной файл:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac OutputFile1.mkv
# ffmpeg -i OutputFile1.mkv
Input #0, matroska,webm, from 'OutputFile1.mkv':
  Duration: 00:03:28.40, start: 0.000000, bitrate: 64 kb/s
    Stream #0.0: Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1: Audio: mp2, 48000 Hz, 2 channels, s16, 64 kb/s

Что же сделал ffmpeg? Трудно сказать... Из исходного списка он выбрал первую видеодорожку (пережал её кодеком mpeg4) и вторую аудиодорожку первого файла... Не логично? Ещё бы! Давайте разберёмься с логикой ffmpeg. По формату результирующего файла (в данном случае - MKV) утилита создаёт минимальную конфигурацию выходного файла, содержащего по одной дорожке каждого типа из присутствующих в исходных файлах (в данном случае это аудио и видеодорожки, субтитров нет). Каждой выходной дорожке назначается конкретная дорожка из исходного файла. Когда вы собираете простой файл, например только видеофайл и аудиофайл, или видеофайл с одной аудиодорожкой - всё очевидно. Но вот когда дорожек много... без пользовательского контроля не обойтись! Итак, давайте дружиться с полезными флагами для работы с дорожками.

Первые полезные флаги, это флаги, полностью отключающие определённый тип дорожек в выходном файле. Если выходной файл указан .mp3, .ac3, .flac или какой-либо другой аудиофайл, то ffmpeg автоматически игнорирует все видеодорожки и субтитры. Но MKV (как и AVI) - это контейнер, который может включать все типы дорожек. Существует три флага, для каждого типа:

-an, отключает аудиодорожки; -sn, отключает субтитры; -vn, отключает видеодорожки.

Итак, если мы НЕ хотим видеодорожек в выходном файле, то просто добавляем флаг:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac -vn OutputFile2.mkv
# ffmpeg -i OutputFile2.mkv
Input #0, matroska,webm, from 'OutputFile3.mkv':
  Duration: 00:03:28.36, start: 0.000000, bitrate: 64 kb/s
    Stream #0.0: Audio: mp2, 48000 Hz, 2 channels, s16, 64 kb/s

Не нужны аудиодорожки?

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac -an OutputFile3.mkv
# ffmpeg -i OutputFile3.mkv
Input #0, matroska,webm, from 'OutputFile3.mkv':
  Duration: 00:03:28.40, start: 0.000000, bitrate: N/A
    Stream #0.0: Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc

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

    Stream #1.0: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

Такая дорожка будет определена флагом -map 1:0.

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac -map 1:0 OutputFile4.mkv
Number of stream maps must match number of output streams

Обратите внимание, что ffmpeg не захотел выполнять такую команду. Дело в том, что один раз указав флаг map мы обязаны указать с помощью него все выходные дорожки, явно не отключенные флагами -an, -sn и -vn. То есть одним из вариантов мы можем указать ffmpeg использовать аудиодорожку из второго исходного файла и ни один из видеопотоков:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac -map 1:0 -acodec copy -vn OutputFile4.mkv
# ffmpeg -i OutputFile4.mkv
Input #0, matroska,webm, from 'OutputFile4.mkv':
  Duration: 00:03:28.35, start: 0.000000, bitrate: 192 kb/s
    Stream #0.0: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

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

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac -map 1:0 -acodec copy -map 0:0 -vcodec copy OutputFile5.mkv
Codec type mismatch for mapping #1.0 -> #0.0

Чтобы решить эту проблему следует просто поменять флаги местами:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac  -map 0:0 -vcodec copy -map 1:0 -acodec copy OutputFile5.mkv
# ffmpeg -i OutputFile5.mkv
Input #0, matroska,webm, from 'OutputFile5.mkv':
  Duration: 00:03:28.40, start: 0.000000, bitrate: 192 kb/s
    Stream #0.0: Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

Замечательно, мы только что собрали MKV с выбранными дорожками! Но как же добавить больше дорожек? Попробуем добавить ещё один флаг map, на сей раз для первой аудиодорожки первого файла:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac  -map 0:0 -vcodec copy -map 1:0 -acodec copy -map 0:1 OutputFile6.mkv
Number of stream maps must match number of output streams

Что же не так? Как я уже писал, ffmpeg определяет количество дорожек выходного файла по формату файла и типам дорожек исходных файлов - по умолчанию их будет всего по одной каждого типа! Для того чтобы добавить ещё одну дорожку определённого типа есть следующие флаги

-newaudio, -newvideo, -newsubtitle

в зависимости от типа. Чтож, добавим аудиодорожку!

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac  -map 0:0 -vcodec copy -map 1:0 -acodec copy -map 0:1 -newaudio OutputFile6.mkv
At least one output file must be specified

Что такое, что за нелепая ошибка? Оказывается, данный флаг необходимо указывать после имени выходного файла! Также обратите внимание где указываются параметры для этой дорожки (примечание, здесь и далее длинная команда разделена на строки символом \):

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac \
    -map 0:0 -vcodec mpeg4 \
    -map 1:0 -acodec copy \
    OutputFile6.mkv \
    -map 0:1 -acodec copy -newaudio
# ffmpeg -i OutputFile6.mkv
Input #0, matroska,webm, from 'OutputFile6.mkv':
  Duration: 00:03:28.40, start: 0.000000, bitrate: 384 kb/s
    Stream #0.0: Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.2: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

Нужна ещё одна аудиодорожка? Нет проблем:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac \
    -map 0:0 -vcodec mpeg4 \
    -map 1:0 -acodec copy \
    OutputFile7.mkv \
    -map 0:1 -acodec copy -newaudio \
    -map 2:0 -acodec copy -newaudio
# ffmpeg -i OutputFile7.mkv
Input #0, matroska,webm, from 'OutputFile7.mkv':
  Duration: 00:00:30.14, start: 0.000000, bitrate: 383 kb/s
    Stream #0.0: Video: vp8, yuv420p, 720x576, PAR 16:15 DAR 4:3, 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.2: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.3: Audio: flac, 48000 Hz, 2 channels, s16

Здорово... но как пользователь определит к чему столько аудиодорожек к видео? Как правило различные аудиодорожки соответствуют различным языкам. Для того чтобы поставить в соответствие дорожку к опредлённому языку есть 3 флага (как всегда - для каждого типа дорожки):

-alang, -vlang, -slang

Для выбора языка необходимо задать его 3-хсимвольный код, например ENG для английского, RUS для русского и ITA для итальянского:

# ffmpeg -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac \
    -map 0:0 -vlang ENG -vcodec mpeg4 \
    -map 1:0 -alang ENG -acodec copy \
    OutputFile8.mkv \
    -map 0:1 -alang RUS -acodec copy -newaudio \
    -map 2:0 -alang ITA -acodec copy -newaudio
# ffmpeg -i OutputFile8.mkv
Input #0, matroska,webm, from 'OutputFile8.mkv':
  Duration: 00:00:21.88, start: 0.000000, bitrate: 384 kb/s
    Stream #0.0(ENG): Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1(ENG): Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.2(RUS): Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.3(ITA): Audio: flac, 48000 Hz, 2 channels, s16

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

# ffmpeg -metadata title="This is my super video" \
    -i InputFile1.vob -i InputFile2.ac3 -i InputFile3.flac \
    -map 0:0 -vlang ENG -vcodec mpeg4 \
    -map 1:0 -alang ENG -acodec copy \
    OutputFile9.mkv \
    -map 0:1 -alang RUS -acodec copy -newaudio \
    -map 2:0 -alang ITA -acodec copy -newaudio
# ffmpeg -i OutputFile9.mkv
Input #0, matroska,webm, from 'OutputFile9.mkv':
  Metadata:
    TITLE           : This is my super video
    ENCODER         : Lavf52.84.0
  Duration: 00:00:21.89, start: 0.000000, bitrate: 383 kb/s
    Stream #0.0(ENG): Video: mpeg4, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0.1(ENG): Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.2(RUS): Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s
    Stream #0.3(ITA): Audio: flac, 48000 Hz, 2 channels, s16

Можно заметить, что мюксеры любят добавлять информацию о себе любимых, не спрашивая пользователя. Поле 'ENCODER' как раз и говорит о том, какой программой был собран контейнер MKV.

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

[matroska @ 0x120c350] Can't write packet with unknown timestamp

вплоть до краха программы... В этом случае следует попробовать обновить ffmpeg или же вместо копирования дорожек выбрать новый кодек лдя пересжатия. Если же перекодирование потоков нежелательно то имеет смысл попробовать воспользоваться MKV Toolmix, или же создавать файлы в промежуточных форматах (например сначала в AVI). Будем надеятся, что со временем эти огрехи программы ffmpeg будут исправлены!

Два видеопотока

Как можно догадаться, дополнительные видеопотоки добавляются в контейнер MKV аналогичным образом. Более того, новые версии MKV Toolnix задают определённый флаг стереорежима для видеодорожки - левый ракурс, правый ракурс, оба ракурса или нет стерео. К сожалению, эта полезная возможность недоступна в текущей версии ffmpeg, поэтому рекомендую закладывать ракурсы в порядке Левый, Правый.

В чём преимущество стереоскопического видео, заданного двумя видеопотоками? В первую очередь, это самый естественный способ использовать один файл для обычного и стереоскопического воспроизведения. Нет стереопроигрывателя - файл будет играться как обычное видео практически любым плеером без дополнительных настроек и без необходимости декодировать в два раза больший видеопоток! Стереопроигрыватель со своей стороны сразу же определит стереоформат видео. Будучи разбитым на 2 видеопотока, декодирование становится возможным как минимум в двух потоках, что позволить более эффективно использовать немногопоточные декодеры (такие как в текущей версии FFmpeg для кодеков x264), а значит плавно воспроизвести видео высокой чёткости на не топовых моделях CPU.