Построение систем доставки видео на основе HTTP Dynamic Streaming от Adobe и OpenSource

В рамках проекта для одного из наших заказчиков в очередной раз встала задача построить систему конвертации/ хранения/ доставки видео в интернет. Типичная такая задача создания своего маленького (или не очень маленького) “Тьюба” только с профессиональным, а не UGC-контентом.

С момента создания первых “Тьюбов” технологии видео в интернете прошли некоторый путь развития, позволяют сейчас делать намного больше, да и требования к современному видео-сайту стали несколько иными.

Наиболее интересными трендами последнего времени, на наш взгляд, являются:

  • возможность смотреть один видео-сайт с разных устройств,
  • технология адаптивного HTTP стриминга

В результате появления и бурного распространения в последнее время мобильных устройств, реально удобных для потребления интернета с них, а также развития беспроводных технологий передачи данных, становится ясным, что нужно иметь вторую версию сайта, оптимизированную для мобильных iOS,im Android и др. платформ. Третья версия сайта, оптимизированная под “10-ти футовый интерфейс”, скоро понадобится, когда станет популярной какая-либо технология WebTV, например GoogleTV.

Все это вписывается в т.н. концепцию “трех экранов”, с которого люди будут потреблять видео (и вообще интернет-контент) в ближайшем будущем — мобильник(планшет) в дороге, PC в офисе на работе, TV в гостиной дома.

Про технологию адаптивного HTTP стриминга хотелось бы поговорить поподробнее, что и является предметом данной статьи.

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

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

Прежде всего, это важно для сервисов прямых трансляций, когда пользователю важнее “не отставать от потока”, т.е., минимизируя вероятность возникновения “пребуферизации”, пусть даже временами жертвуя качеством картинки. Однако для сервисов видео-по-запросу такая технология также крайне полезна — приятно ведь, когда видео стартует быстро, в том качестве, которое позволяет канал и всеми силами пытается не запинаться.

На практике это означает, что мы можем значительно улучшить восприятие сервиса не только пользователям с нестабильным/слабым каналом, где-нибудь глубоко в регионах, но и пользователям wifi-сетей, пользователям, у которых одно соединение является общим для нескольких пользователей в домохозяйстве и т.д. Для пользователей с выделенным качественным каналом данная технология просто автоматически определит скорость его канала и будет отдавать видео в подходящем битрейте — т.е. конечному пользователю больше не нужно знать о том, что такое 360p, 240p или SD, HD и т.д. — все происходит автоматически.

Расплачиваться за все эти преимущества приходится 3-мя вещами —

  • усложнением процедуры стриминга,
  • дополнительными затратами на кодирование,
  • дополнительными затратами на хранение.

Первая причина на данный момент уже не должна приниматься во внимание, поскольку уже есть готовые opensource — кирпичики для строительства таких систем, о чем я дальше и расскажу.

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

На данный момент существует, как минимум, три известные мне реализации технологии адаптивного мультибитрейтного стриминга:

  • Apple HTTP Adaptive Streaming for iPhone/iPad,
  • Microsoft Smooth Streaming for Silverlight,
  • Adobe Dynamic Streaming for Flash.

Для практики разработки видео-сайтов наиболее важным, конечно, является реализация от Adobe, поскольку Apple HTTP Adaptive Streaming работает только для iOS устройств и Safari под MacOS X (хотя мне на днях показали один STB, в котором данный протокол реализован), а Silverlight, скажем так, пока не получил такой популярности как Flash.

Adobe реализовала Dynamic Streaming в рамках rtmp протокола уже довольно давно и только относительно недавно (с появлением flash 10.1) стало возможным использовать HTTP стриминг. Это очень важный шаг, поскольку раньше для использования динамического стриминга нужен был специализированный стриминговый сервер, теперь же бОльшая часть работы перенесена на клиент, а серверная поддержка упрощается вплоть до отдачи обычной HTTP статики.

Фактически нам нужно закодировать ролик в нескольких битрейтах, а дальше, либо нарезать на фрагменты, например, секунд по 5 и положить под быстрый сервер отдачи статики (см. рисунок ниже), либо, если существует сервер или стриминг-модуль, который из mp4 файликов может вырезать нужные фрагменты на лету, тогда на storage-уровень ставим его, а запросы маршрутизируем через какой-либо эффективный кешер. Более того, данный кешер может быть у провайдера CDN услуги. Одним из основных достоинств технологии адаптивного HTTP стриминга и является простота использования внешних CDN и кешеров — можно не заботится о преднаполнении кеша — в случае “cache miss” запрос проксируется на origin-server, одновременно сохраняясь в кеше.

Схема HTTP Dynamic Streaming

Кеширование получается очень эффективным, поскольку в кеше прокси кусочками “оседает” востребованный контент. Причем эти кусочки относительно небольшие и можно придумывать различные стратегии управления кешем — когда “сохранять” кусочек, когда его удалять и т.д. Это дает большую свободу для разработки механизмов оптимизации разработчикам CDN сервисов. В свое время для первых версий системы доставки рутюба мы разрабатывали похожий механизм “кусочного” кеширования, позволивший долгое время проекту эффективно доставлять видео при минимуме оборудования.

С точки зрения программирования для поддержки HTTP Dynamic Streaming ребята из Adobe сделали одну простую вещь — у объекта NetStream появился метод appendBytes. Формат входного массива — байтпоток в FLV-формате. Фактически это дает возможность проигрывать байтпоток, а вопросы его доставки — отдельная задача. Например, можно доставлять эти байты посредством HTTP — кусочков и получится HTTP стриминг.

Системы от Apple и Microsoft реализуют систему доставки и проигрывания внутри своего “черного ящика”, а Adobe дает возможность программировать систему самостоятельно. Сделали они и открытую реализацию такой системы в рамках своего Open Source Media Framework. OSMF — это набор базовых классов, упрощающих написание видео-плейеров на ActionScript3, который, в том числе, включает реализацию поддержки HTTP динамического стриминга. Более того, спецификации данного протокола и подробности реализации Adobe старается раскрывать. Посмотреть можно тут и тут. Можете себе такое представить раньше, когда у Flash не было конкурентов и их будущее представлялось совершенно безоблачным?

Итак, возвращаемся к постановке задачи — мы хотим сделать видеохостинг, десктоп-версия будет использовать flash и технологию HTTP adaptive streaming.

Как собрать такую систему конвертации и доставки видео?

Кодировать будем в mp4, кодеки — x264 и faac, инструменты — ffmpeg, mencoder. Коммерческий софт мы не очень любим, если есть бесплатные альтернативы.

На данный момент мне известно о следующих реализациях данной технологии:

  • коммерческий софт. Коммерческие сервера от Adobe и Wowza имеют реализацию данной технологии, в качестве основы для плейера можно взять OSMF. Вариант, наверное, хороший, но дорогой.
  • есть свободная, но закрытая реализация от самой Adobe. Представляет собой перепаковщик исходных файлов в некий f4f-формат и модуль для apache, работающий с такими перепакованными файлами. Минусы — не получается работать с mp4-файликами, создаваемыми ffmpeg, поскольку исходники закрыты, понять почему — проблема.
  • USP от CodeShop. Плюсы — прекрасная открытая реализация серверной части, минусы — закрыта клиентская и, вообще-то, софт коммерческий, хоть и OpenSource. Если сайт, использующий технологию, коммерческий — показывает рекламу или взимает плату за просмотр, нужно покупать лицензии.

Я работаю в компании, которая занимается видео-технологиями в интернет уже лет 10 и у нас есть определенный опыт и, посмотрев на документацию протоколов Adobe HTTP Dynamic Streaming, мы решили, что для проекта нашего “Тюба” нам проще и быстрее реализовать серверную поддержку самостоятельно. А клиентская реализация протокола в OSMF открыта и под BSD-лицензией.

Результатом является проект OpenHttpStreamer, который мы решили выложить в OpenSource под LGPL.
Официальная страничка проекта — /OpenHttpStreamer
Исходники доступны на GitHub — https://github.com/inventos/OpenHttpStreamer.
Мы пробовали собирать под Ubuntu 10.10, Fedora 12, Debian(Squeeze). Из особенностей — нужны scons, g++ версии 4.3.0 и выше и boost >=1.39.

Как пользоваться:
$ git clone https://github.com/inventos/OpenHttpStreamer.git
$ cd OpenHttpStreamer/mp4frag
$ scons configure && scons
$ sudo scons install

Если все прошло успешно в /usr/local/bin/mp4frag будет собранная утилита создания статических фрагментов

$ mp4frag
Allowed options:
--help produce help message
--src arg source mp4 file name
--video_id arg (=some_video) video id for manifest file
--manifest arg (=manifest.f4m) manifest file name
--fragmentduration arg (=3000) single fragment duration, ms
--template make template files instead of full fragments
--nofragments make manifest only

Закодируем ffmpeg’ом какой-нибудь ролик в двух качествах — 400 и 700 кбит/сек (примерно)

$ ffmpeg -y -i test.mpg -acodec libfaac -ac 2 -ab 96k -ar 44100 -vcodec libx264 -vpre medium -g 100 -keyint_min 50 -b 300k -bt 300k -threads 2 test-q1.mp4
$ ffmpeg -y -i test.mpg -acodec libfaac -ac 2 -ab 96k -ar 44100 -vcodec libx264 -vpre medium -g 100 -keyint_min 50 -b 600k -bt 600k -threads 2 test-q2.mp4

Получили два mp4 файлика — test-q1.mp4 и test-q2.mp4, из которых генерируем статические фрагменты:

$ mp4frag --src test-q1.mp4 --src test-q2.mp4 --manifest=test.f4m

Результатом работы является описатель файла (“манифест”) — test.f4m и статические файлики-фрагменты в папках 0/ и 1/

Выкладываем test.f4m 0/ 1/ под DocRoot любого веб-сервера, способного отдавать статику, и нам остается написать на флеше простой плейер, используя в качестве движка OSMF, или взять готовый плейер.

Для быстрого теста можно воспользоваться нашей сборкой OSMF-плейера.
Для этого
1. под DocRoot нашего сервера (там же, где и test.f4m) помещаем такой crossdomain.xml:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="inventos.ru" />
</cross-domain-policy>

2. переходим в браузере по ссылке /OpenHttpStreamer?url=http://your_http_host/test.f4m

Как обычно, если что-то не получается, смотрим логи нашего сервера — есть ли обращения от плейера к crossdomain.xml, test.f4m, файликам сегментов. Удобно проверять, все ли верно — самим «подергать» нужные адреса —
wget -O - -S «http://your_http_host/test.f4m»
wget -O - -S «http://your_http_host/1/Seg1Frag1»

В заключение пару слов о том, что сейчас лежит в репозитарии. Пока написан только статический перепаковщик — mp4frag. Модуль для nginx в разработке. Мы уже продумали архитектуру и алгоритмистику, активно программируем и собираемся выпустить буквально на этой-следующей неделе — надеюсь, я по этому поводу еще напишу.

Модуль динамической генерации нам нужен, чтобы контент хранить в первозданном (mp4 после конвертации ffmpeg) виде, поскольку это достаточно универсальный формат, подходящий для других целей (стриминга на другие платформы). Есть интересный вариант — использование fragmented mp4, но он менее универсален.

Мы придумали простой способ — проиндексировать mp4 для быстрого доступа к нужным фрагментам и положить индексы в отдельном файлике рядом с самим контентом. Размер индекса будет всего около 1% от исходных файлов. Из-за того, что даже получив быстрый доступ к нужному фрагменту mdat в исходном mp4, нам нужно еще перемикшировать данные, добавив flv — заголовки ( особенность реализации OSMF), мы теряем в производительности по сравнению со статикой. Однако, проксируя запросы на данный модуль через быстрый web-cache и кешируя ответы, мы достигаем как высокой производительности, так и большой универсальности.

У нас есть похожие новости по этим темам:
Наверх

7 комментариев