Каждый день миллионы зрителей смотрят видео в интернете. Но чтобы видео стало доступно, его нужно не только загрузить на сервер, но и обработать. Чем быстрее это происходит — тем лучше сервису и его пользователям.
Меня зовут Аскар Камалов, год назад я присоединился к команде видеотехнологий Яндекса. Сегодня я коротко расскажу читателям Хабра о том, как с помощью распараллеливания процесса кодирования нам удалось в разы ускорить доставку видео до пользователя.
Этот пост в первую очередь будет интересен тем, кто раньше не задумывался о том, что происходит под капотом видеосервисов. В комментариях можно задавать вопросы и предлагать темы для будущих постов.
Несколько слов о самой задаче. Яндекс не только помогает искать видео на других сайтах, но и хранит видео для собственных сервисов. Будь то авторская программа или спортивный матч в Эфире, фильм на КиноПоиске или ролики в Дзене и Новостях — всё это загружается на наши серверы. Чтобы пользователи посмотрели видео, его нужно подготовить: сконвертировать в необходимый формат, создать превьюшку или даже прогнать через технологию DeepHD. Неподготовленный файл просто занимает место. Причём речь идёт не только об оптимальном использовании железа, но и о скорости доставки контента до пользователей. Пример: запись с решающим моментом хоккейного матча могут искать в поиске уже через минуту после самого события.
Последовательное кодирование
Итак, счастье пользователя во многом зависит от того, как быстро видео станет доступно. А это в основном определяется скоростью транскодирования. Когда жёстких требований к скорости выкладки видео нет, то и проблем нет. Берёте единый, неделимый файл, конвертируете его, выкладываете. В начале своёго пути мы так и работали:
Клиент загружает видео в хранилище, компонент Analyzer собирает метаинформацию и передаёт видео на конвертацию в компонент Worker. Все этапы выполняются последовательно. При этом серверов для кодирования может быть много, но только один занят обработкой конкретного видео. Простая, прозрачная схема. На этом её достоинства и заканчиваются. Такая схема масштабируется только вертикально (за счёт покупки более мощных серверов).
Последовательное кодирование с промежуточным результатом
Чтобы хоть как-то сгладить мучительное ожидание, в индустрии придумали вариант быстрого кодирования. Обманчивое название, потому что на самом деле полноценное кодирование происходит последовательно и так же долго. Но зато с промежуточным результатом. Идея такая: как можно быстрее подготовить и выложить версию видео в низком разрешении, а уже потом — версии более высоких разрешений.
С одной стороны, видео становится доступно быстрее. И это полезно для важных событий. Но с другой — картинка получается размытая, а это раздражает зрителей.
Получается, нужно не только быстро обрабатывать видео, но и сохранять его качество. Это то, чего ждут от видеосервиса пользователи сейчас. Может показаться, что достаточно купить наиболее производительные серверы (и регулярно разом их все апгрейдить). Но это путь в тупик, потому что всегда найдётся видео, которое заставит тормозить даже самое мощное железо.
Параллельное кодирование
Куда эффективнее поделить сложную задачу на множество менее сложных и параллельно их решать на разных серверах. Такой вот MapReduce для видео. В этом случае мы не упираемся в производительность одного сервера и можем масштабироваться горизонтально (за счёт добавления новых машин).
К слову, идея дробить видео на мелкие куски, параллельно обрабатывать и склеивать их — не какой-то секрет. Вы можете найти множество упоминаний этого подхода (например, на Хабре рекомендую пост о проекте DistVIDc). Но от этого в целом не становится легче, потому что нельзя просто так взять уже готовое решение и встроить к себе. Нужна адаптация под нашу инфраструктуру, наше видео и даже нашу нагрузку. В общем, проще написать своё.
Итак, в новой архитектуре монолитный блок Worker c последовательным кодированием мы разделили на микросервисы Segmenter, Tcoder, Combiner.
- Segmenter разбивает видео на фрагменты примерно по 10 секунд. Фрагменты состоят из одной или нескольких GOP (group of pictures). Каждая GOP независима и кодируется отдельно, так что может быть декодирована без ссылки на кадры из других GOP. То есть фрагменты могут воспроизводиться независимо друг от друга. Такое сегментирование уменьшает задержку, позволяя начать обработку раньше.
- Tcoder обрабатывает каждый фрагмент. Он берёт задание из очереди, скачивает фрагмент из хранилища, кодирует в разные разрешения (напомним, что плеер может выбирать версию, исходя из скорости соединения), затем складывает результат обратно в хранилище и помечает фрагмент обработанным в базе данных. Обработав все фрагменты, Tcoder отправляет задачу на формирование результатов для следующего компонента.
- Сombiner собирает результаты вместе: скачивает все фрагменты, сделанные Tcoder, формирует потоки для разных разрешений.
Несколько слов о звуке. У наиболее популярного аудиокодека AAC есть неприятная особенность. Если кодировать фрагменты отдельно, то склеить их бесшовно просто не получится. Переходы будут заметны. У видеокодеков такой проблемы нет. Теоретически можно поискать сложное техническое решение, но эта игра пока что просто не стоит свеч (аудио весит существенно меньше, чем видео). Поэтому у нас параллельно кодируется только видео, а аудиодорожка обрабатывается целиком.
Результаты
Благодаря параллельной обработке видео мы значительно сократили задержку между загрузкой видео к нам и его доступностью для пользователей. Например, раньше на создание нескольких полноценных версий разного качества для FullHD-фильма длительностью полтора часа могло уйти два часа. Теперь всё это занимает 15 минут. Более того, при параллельной обработке мы создаём версию в высоком разрешении даже быстрее, чем версию в низком разрешении при старом подходе с промежуточным результатом.
И ещё кое-что. При старом подходе либо могло не хватать серверов, либо они простаивали без задач. Параллельное кодирование позволяет повысить долю утилизации железа. Теперь наш кластер более чем из тысячи серверов всё время чем-то занят.
На самом деле ещё есть куда стремиться. Например, мы можем существенно сэкономить время, если начнём обрабатывать фрагменты видео ещё до того, как оно к нам поступило полностью. Как говорится, дальше — больше.
Напишите в комментариях, о каких задачах в области работы с видео вы бы хотели прочитать.
Полезные ссылки на опыт коллег по индустрии
- SVE: Distributed Video Processing at Facebook Scale
- Simplifying Media Innovation at Netflix with Archer. Netflix Technology Blog