Думаю, большинству хабрапользователей хоть раз да приходилось кодировать видео. Возможно, вы хотели посмотреть свежую серию любимого сериала в метро, а ваш смартфон или иное портативное устройство никак не хотело играть этот кодек, или SmartTV отказывался декодировать звук в видеофайле, либо же вы застряли в 2004 и транскодировали H.264 в MPEG-4 для вашего старого DVD-проигрывателя. Или же, например, сжимали невероятного размера видео, снятое фотоаппаратом, во что-то более-менее приличное по размеру. Наверняка вы замечали, что это не самый быстрый процесс.
Для кодирования видео сделано огромное количество софта, начиная от программ типа «жми крестик чтобы кодировать» и заканчивая софтом, который принимает AviSynth-скрипты на вход. Однако программы, поддерживающие хоть какое-то распределение кодирования, можно пересчитать по пальцам одной руки.
Что вообще следует понимать под распределенным кодированием видео? В моем представлении, есть 2 разных задачи:
- Кодирование большого количества видеофайлов на множестве компьютеров одновременно
- Кодирование одного видеофайла по частям на множестве компьютеров одновременно
Первая задача подразумевает наличие большого количества видеофайлов, сильно больше, чем компьютеров, на которых их можно кодировать, и довольно легко автоматизируется с использованием CLI-кодировщиков, например, HandBrake или FFmpeg и любых средств автоматизации, которые поддерживают распределение, вроде GNU Parallel или PPSS (к слову, о нем мало кто знает, рекомендую!).
Вторая же задача подразумевает наличие малого количества файлов и большого числа компьютеров, и она несколько сложнее: нам необходимо, во-первых, разбить файл на маленькие части, во-вторых, перекодировать их, и в-третьих, собрать обратно.
Обычно, мне нужно кодировать 1-2 10-битных видеофайла, закодированных кодеком H.264, в 8-битные, с пресетом medium, slow или slower. Как правило, на одну минуту видео требуется 6-8 минут кодирования. Хотелось бы быстрее.
Глоссарий
GOP (Group of Pictures) — буквально, группа изображений. Часть кадров от одного I-frame (ключевого кадра) до другого, не включая последний.
Worker — программа, которая выполняет какую-то работу (как правило, самую затратную) в распределенной системе. В нашем случае — кодирование видео.
Готовые решения
Я несколько дней искал живые проекты по распределенному кодированию обоих задач, и вот что я нашел:
1. MediaCoder
MediaCoder — достаточно продвинутый и удобный комбайн, неплохо балансирующий между количеством настроек и легкостью использования. Однако, распределенное кодирование в нем реализовано хуже некуда: декодируется видео локально, а worker отправляется несжатый декодированный фрейм. Рекомендуют гигабитную сеть, так что через интернет это будет работать невероятно медленно. Сервер работает только под Windows (и еще в Wine), worker работает под Windows и Linux. Поддерживается только H.264 и VP8.
2. dvd::rip
Методы распределения в этом проекте, к сожалению, ничем не лучше, чем с использованием обычных средств распределения. Вам нужно будет поднять SSH-сервер на каждой машине и NFS-сервер на машине с файлами. Не умеет кодировать один файл на нескольких компьютерах.
3. Ripbot264
Лучшее, что я видел. Правильно режет файлы (по GOP), удобно настраивается, но, к сожалению, использует средства общего доступа к файлам Windows, что практически исключает работу вне одной сети (программа требует, чтобы все компьютеры были в одной рабочей группе). И работает только в Windows.
Создание своего велосипеда
К сожалению, под Linux ничего сносного не нашел и загорелся идеей сделать свое. Какие требования я выдвигал к распределенной системе кодирования видео?
- Система должна уметь кодировать один файл на нескольких workers (разрезка файла по GOP и последующая склейка обратно)
- Должно поддерживаться как можно большее количество декодеров, энкодеров и контейнеров (как минимум, mkv и mp4)
- По возможности, избегать создание временны́х файлов и минимизировать потребление RAM на workers
Для кодирования видео решил использовать FFmpeg, а для разрезания и склейки файлов mkvmerge, и, соответственно, контейнер matroska (т.к. в процессе испытывания FFmpeg вылезла регрессия в mkv-муксере).
Переписывал я проект 6 раз. Какие идеи были отброшены:
- Использование Job Server (Gearman, Beanstalkd) и Message Broker (RabbitMQ) для передачи кусков видео для кодирования внутри Job. Это, конечно, очень удобно, но хранить куски исходного и перекодированного файла в памяти я себе позволить не мог. К тому же, сначала нужно было получить видео полностью, затем перекодировать его, и только потом отправлять.
- Использование RPC по той же причине
- Использование HTTP-сервера для отдачи и получения файлов
И все вернулось к сокетам. Действительно, лучше сокетов здесь тяжело что-то придумать — сокет можно подать как на stdin FFmpeg, так и на stdout, и никаких временны́х файлов не будет создаваться, и оперативной памяти расходуется по минимуму, и скодированное видео загружается сразу на сервер.
И я это сделал.
github.com/ValdikSS/distvidc — DistVIDc (типа distcc, только для видео)
Я использовал Rage Driven Development, и знатно расслабился, когда получил первую рабочую версию и первый коммит.
Как оно работает?
Есть три скрипта — server, client и worker. Server ждет файл и параметрами кодирования от client, режет его на куски, распределяет между worker, отдает части и принимает переконверченные куски, собирает куски в файл. Worker, соответственно, подключается к серверу, ждет команды и кодирует куски.
Проблемы:
- Видео с переменной кадровой частотой (VFR), скорее всего, будет кодироваться неправильно и рассинхронизироваться
- Эффективность кодирования получается немного меньше из-за того, что на конце разрезанного куска GOP может быть меньше, чем он мог бы быть без разрезки
Положительные стороны:
- Можно подавать на вход и получать на выходе все, что поддерживается проектом FFmpeg. Theora в ogg в VP8 в webm? Без проблем. Богом забытый msmpeg2 в avi в HEVC в mkv? Да запросто!
- Эффективная работа worker. Потребляется всего около 200КБ оперативной памяти!
DistVIDc будет работать как под Linux, так и под Windows (на данный момент, worker работает только под Linux). Буду рад любому, кто заинтересуется проектом.