Распределенное кодирование видео

    image
    Думаю, большинству хабрапользователей хоть раз да приходилось кодировать видео. Возможно, вы хотели посмотреть свежую серию любимого сериала в метро, а ваш смартфон или иное портативное устройство никак не хотело играть этот кодек, или SmartTV отказывался декодировать звук в видеофайле, либо же вы застряли в 2004 и транскодировали H.264 в MPEG-4 для вашего старого DVD-проигрывателя. Или же, например, сжимали невероятного размера видео, снятое фотоаппаратом, во что-то более-менее приличное по размеру. Наверняка вы замечали, что это не самый быстрый процесс.

    Для кодирования видео сделано огромное количество софта, начиная от программ типа «жми крестик чтобы кодировать» и заканчивая софтом, который принимает AviSynth-скрипты на вход. Однако программы, поддерживающие хоть какое-то распределение кодирования, можно пересчитать по пальцам одной руки.

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

    Первая задача подразумевает наличие большого количества видеофайлов, сильно больше, чем компьютеров, на которых их можно кодировать, и довольно легко автоматизируется с использованием CLI-кодировщиков, например, HandBrake или FFmpeg и любых средств автоматизации, которые поддерживают распределение, вроде GNU Parallel или PPSS (к слову, о нем мало кто знает, рекомендую!).

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

    Обычно, мне нужно кодировать 1-2 10-битных видеофайла, закодированных кодеком H.264, в 8-битные, с пресетом medium, slow или slower. Как правило, на одну минуту видео требуется 6-8 минут кодирования. Хотелось бы быстрее.

    Глоссарий

    GOP (Group of Pictures) — буквально, группа изображений. Часть кадров от одного I-frame (ключевого кадра) до другого, не включая последний.
    image
    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/distvidcDistVIDc (типа 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). Буду рад любому, кто заинтересуется проектом.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 34

      +1
      Великолепная вещь! Являясь медиа-компанией периодически кодировать видео приходится всем, кроме водителей и уборщиц. Давно думал о чем-то подобном. Было бы замечательно раскинуть worker`ы и клиенты на все станции, с возможными лимитами на процессор, общей очередью и простым гуем. Вот только все клиенты на винде.
      В любом случае огромное спасибо за работу, ушел смотреть.
        0
        Надеюсь, скоро все будет. Пожалуйста не забывайте, что это всего лишь анонс первого коммита :)
        +3
        Очень очень нужный проект! Вот только указанная проблема с возможной рассинхронизацией все портит. Можно хотя бы перед началом работы определить такие файлы и пускать их в однопоточную обработку, а не через этот комбайн?

        Кстати что-то может испортиться если на разных серверах стоят разные ОС и разные версии ffmpeg?
          0
          Можно хотя бы перед началом работы определить такие файлы и пускать их в однопоточную обработку, а не через этот комбайн?

          Конечно. Это можно посмотреть через mediainfo, например. VFR — нечастое явление в современном видео.
          Вполне возможно, что некоторые VFR-файлы работают правильно, я только на одном проверил.

          Кстати что-то может испортиться если на разных серверах стоят разные ОС и разные версии ffmpeg?

          Предполагается, что в будущем будет статическая сборка ffmpeg с воркером.
            0
            ArjLover, сейчас попробовал другое VFR-видео — все работает, рассинхрона нет.
              0
              Да, спасибо, крепко задумался как бы это попробовать… для серверов не в одном ДЦ скорость передачи исходника может оказаться значительной…
              Но пока наткнулся на еще одну проблему тоже с рассинхроном, может знаешь в чем тут дело. Мне надо нарезать из файла на час-полтора несколько кусочков, с перекодированием конечно. Если нужный кусок начинается в 55 минут, то по команде
              ffmpeg -i source.avi -ss 55:00…
              ффмпег начинает проигрывать этот файл до нужного момента, именно декодировать, конечно в 8 ядер он доползает до нужного места минут за 5, но иногда встречаются файлы которые он умеет только в одно ядро и тогда он доматывает минут за 15-20… Кошмарная потеря времени! На днях узнал что можно поменять опции местами(!) и это приведет к интересному эффекту (они точно курили когда это делали)
              ffmpeg -ss 55:00 -i source.avi…
              эта команда заставляет прыгнуть сразу на нужное место и заняться делом. Но тут у большинства файлов или просто у всех на выходе получается серьезный рассинхрон видео со звуком… А счастье было так близко… Кто-нибудь понимает в чем тут проблема?
                +1
                Ыхыхы.
                На самом деле, все довольно умно, просто не очень очевидно. Параметры до инпута (-i) отвечают за одно, а после — за другое. И в этом случае так же: -ss перед инпутом делает seek() на потоке, а после инпута — что-то вроде seek() внутри декодера.
                Дело в том, что -ss перед -i ищет по GOP, а после -i может искать точно. И их можно комбинировать.

                Например, вы примерно знаете размер GOP, да даже если не знаете.
                ffmpeg -ss 00:53:50 -i source.avi -ss 00:00:10 …

                Т.е. сначала будет примерный seek() файла по времени, а потом точное декодирование 10 секунд.

                Но рассинхрона в любом случае быть не должно, может быть у вас какой-то поломанный файл, или ffmpeg старой версии?

                В общем, man ffmpeg-all.
                  0
                  А, да, вообще для разрезания файла есть -f segment
                  ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment

                  Ну и mkvmerge вам может нарезать почти любой входной файл.
                    0
                    Если я правильно понял, то это stream copy без перекодирования? Как я упомянул выше, мне нужно обязательно перекодировать — с нужным битрейтом и в mp4 для онлайн просмотра.
                      0
                      Вы вполне можете одновременно резать и перекодировать сегменты.
                      ffmpeg -i cut-002.mkv -map 0 -f segment -segment_time 00:00:30 -reset_timestamps 1 -c copy -c:v libx264 -preset faster -crf 20 precure-%02d.mkv

                      На выходе вы получите перекодированные precure-00.mkv, precure-01.mkv…
                      Очень рекомендую еще про опцию map почитать.
                        0
                        Кажется я понял что делает этот комплекс опций. Это не совсем то, мне не надо просто разрезать весь файл на куски. Мне надо сделать несколько кусков по одной минуте с точно определенного места.
                        film.arjlover.net/info/adjutant.ego.prevoshoditelstva.1.avi.html
                        вот фильм, для него отобраны некоторые удачные скриншоты, хочу еще сделать онлайн просмотр точно одной минуты именно с этого места по нажатию на кадр. Это все чтобы люди могли более точно узнать и вспомнить фильм. Думаю segment & map тут лишние — а вот первый вариант должен помочь, а то 6 раз мотать до 90 минут — холостого хода больше чем нужной работы.
                    0
                    Я всегда знал что если один убивает несколько дней(!) на решение проблемы, то обязательно есть другой кто просто знает готовый ответ. Вся сложность — найти этого другого. )))
                    Правильно понимаю? что указанная строчка приземлит меня точно в 54:00 минуту и без рассинхрона? Насколько большим должен быть второй параметр для 100% гарантии? Длина фрейма?
                      0
                      Нет, не точно на 54 минуту. -ss перед input ищет по GOP, «округляя» в левую сторону, т.е. у вас получится где-то, не знаю, 00:53:49, например. Вам нужно подгонять -ss после -i, чтобы вырезать нужную часть.

                      В общем, я вам выше про -f segment написал, вам нужно использовать именно его, он сам все нарежет.

                      У вас там не HLS, случаем? А то ffmpeg умеет HLS делать самостоятельно.
            +1
            >Богом забытый msmpeg2 в avi в HEVC в mkv? Да запросто!
            avi тоже правильно режется для правильной раздачи workerам?
              0
              Да. Все исходные файлы режутся и муксируются в mkv, воркеры получают mkv, потом можно соединить в любой контейнер.
                0
                Простите за занудство, а почему именно mkv? Он разве может содержать любые видео-форматы?
              +1
              Harmonic ProMedia Carbon умеет кодировать фермами, но его цена заоблачна.
                0
                Вот бы мануал в репозитории заиметь. Может Wiki какую-нибудь?
                  0
                  Конечно, все будет.
                    0
                    Прошло почти 3 года, и судя по пословице 4 апреля 2017 всё обязано быть :)
                      0
                      Сервис, который я хотел использовать для аренды вычислительных мощностей, закрылся почти сразу после выхода этой статьи, но я нашел новый. Я задумываюсь над развитием идеи, возможно, запущу публичную демку на взломанных дедиках.
                        0
                        Ну, я уже написал свой распределённый перекодировщик с сервером очередей и куртизанками :). Логика как оказалась довольно проста, нарезал файл, раздал задачи и пусть воркеры трудятся.
                          0
                          Поделитесь с сообществом )
                            0
                            Если работодатель будет не против ;)
                  0
                  как бальзам на душу! На прошлой неделе размышлял о таком!!! При ближайшей необходимости буду пробовать.
                    0
                    Спасибо, очень одобряю! :)
                    Хотелось бы чтобы в скором времени была возможность добавлять свои мощности в некую общую сеть кодировщиков и по принципу торрент-трекеров на основе рейтингов кодировать и «чужое», когда ресурсы простаивают, но и в ответственный момент запустить на кодирование и свои нужды.
                      0
                      Собственно, это и есть конечная идея — сделать проект типа @ home
                      0
                      Подскажите, а у вас не было проблем с конкатенацией MPEG-ов?
                      У нас FFMPEG съедает 1 кадр при конкатенации, хоть ты тресни. Если сливать больше 2 файлов, то начиная с третьего — съедается первый кадр каждой склейки.
                        0
                        Клейте mkvmerge.
                          0
                          Не годится, он клеит только в MKV с переменным битрейтом (несмотря на мои очевидные попытки сказать ему --compression -1:none)
                          А нам нужно MPEG CBR в MPEG CBR
                            0
                            Ну, так, вам нужно перекодировать в mpeg cbr, контейнер тут не при чем. Параметр --compression у mkvmerge означает, если я не ошибаюсь, сжатие потока и/или заголовка контейнера (через zlib и другие методы).
                              0
                              Перекодирование как раз не нужно. Исходники MPEG CBR, несколько кусков. Их нужно склеить без перекодирования, простая конкатенация с рестримом потом (чтобы заголовки нормальными были).
                              Из командных утилит только FFMPEG это умеет делать, но, как оказалось -криво
                                0
                                Скиньте семпл, я посмотрю.
                                  0
                                  Хех. Пока искали ответ снаружи, нашелся внутри. Свежий FFMPEG умеет правильно склеивать, если ему скормить
                                  ffmpeg -i concat:"имяфайла1|имяфайла2|etc..."
                                  то есть использовать протокол, а не фильтр, как в старом.

                      Only users with full accounts can post comments. Log in, please.