Вещание IP камер в формате HLS. HLS против RTMP — сухая статистика Hls протокол

Обработку, хранение и передачу видео для своего онлайн-проекта, а не использовать сайты вроде YouTube, он неизбежно приходит к вопросу о том, какой протокол передачи использовать для трансляции видео на устройства пользователей. Выбор невелик, т.к. есть ряд отраслевых стандартов, которые поддерживают те или иные устройства. Кроме того, выбор протокола во многом зависит от «класса» видео - живая трансляция или видео-по-запросу. От выбора протокола также зависит и выбор медиа-сервера, который будет двигателем вашей медиа-машины: будете ли ставить несколько разнородных серверов или построите сеть доставки на одном решении? Поэтому нужно взвесить всё и принимать решение исходя из критериев вашего бизнеса.

В общем, получается уравнение со многими неизвестными. Здесь немаловажна динамика процесса - а куда вообще идёт индустрия? Вдруг я вложусь в поддержку технологии, а она загнётся через год, ведь такое уже бывало. Или поставлю на модную технологию, а её никто не поддерживает?

Мы решили оценить, как менялась доля разных протоколов с течением времени - посмотреть в динамике весь процесс. Данные взяли за последний год.

Исходные данные

Для начала - кто мы такие, чтобы судить о долях рынка? Мы - разработчики веб-сервиса отчетности для медиа-серверов . На рынке работаем четвертый год и к нам приходят компании с разными инфраструктурами, разным количеством серверов и разными потребностями. Получается неплохой слепок состояния отрасли.

Мы сделали небольшой отчет, где можно выбирать диапазон дат и получать данные с графиком по количеству просмотров видео через разные протоколы.

В отчете даются данные по серверам:

  • Wowza Streaming Engine во всех версиях, начиная с 2.2 и до последних 4.х; бОльшая часть - 3.х.
  • Nimble Streamer , работающий с HLS, Smooth, HDS и progressive download - это наша разработка.
  • Windows Media Services - их буквально пару десятков, но они есть, и надо их учитывать
На момент написания статьи сервис обслуживает порядка 1000 серверов из 60 стран мира.

Отчет также периодически обновляется у нас в блоге, он доступен по соответствующему тегу .

Поехали

Отчет за июнь/июль 2014 выглядит примерно так. Из 1.4 миллиарда просмотров больше половины - это HLS. На втором месте - RTMP с четвертью просмотров. RTSP - примерно шестая часть. Остальные находятся в районе статистической погрешности.

Что было год назад за тот же период? Ситуация почти зеркальная. RTMP - почти две трети, RTSP и HLS делят второе и третье места. Правда, и база для измерений была меньше почти в 3 раза - «всего» 500 миллионов просмотров . Серверов в нашем сервисе тоже было поменьше, конечно.

Пройдемся между этими двумя точками.

Итак, июнь - август 2014 года, 3 месяца лета. 800 миллионов просмотров , но доли такие же, август изменений не привнёс.

Сентябрь - ноябрь 2013. Начался новый сезон, HLS начал отъедать долю RTMP. Всего 1.1 миллиарда просмотров , у RTMP примерно половина от общего числа, HLS - четверть.

Декабрь 2013 - февраль 2014. 1.4 миллиарда просмотров , из них на HLS приходится уже больше 40%. RTMP и RTMP делят второе и третье место с четвертью доли. Олимпиада в Сочи дала прирост числа просмотров и одновременно заставила провайдеров вспомнить обо всех клиентах со всеми их экзотическими или старыми девайсами, которые понимают только RTSP - отсюда и скачок этого протокола.

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

Несмотря на то, что динамическая адаптация видеопотока уже сравнительно «старая» технология, существует множество мелких подробностей о том, как добиться лучшего результата. Чтобы и на серверной стороне попроще и подешевле, и чтобы такое видео было совместимо с как можно большим количеством клиентов (Web, iOS, Android, ну и не забываем про Smart TV).

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

Лирическое отступление (максимально кратко и просто про адаптивное видео):

Очевидно, самый простой вариант раздачи видео в интернете - это взять mp4 файл и выложить его на HTTP сервер. Этот вариант плох тем, что у нас всего 1 файл, а клиентов у нас великое множество и такое же множество качественных и не очень интернет-соединений. Если выложим видео 1080p с битрейтом 20 мегабит/с (Blueray качество), его не смогут смотреть смартфоны, а если выложим видео для смартфонов (скажем, 1 мегабит/c и 320x240), оно будет ужасно выглядеть на 55-дюймовом телевизоре.

Ну, раз 1 файл - плохо, давайте выложим десяток файлов, «нарежем» разного видео из одного исходника, будут все битрейты и все размеры кадра, от мобильного до 1080p, с битрейтом от 1 мегабита/c до 20. Прекрасно. Но есть проблемка. Один и тот же смартфон может быть как в домашнем Wi-Fi (то есть быстром), так и в ресторанном (то есть медленном). Одинаковые телевизоры бывают как у людей в Москве, так и у людей на Сахалине.

Тогда пусть плеер проверяет как-нибудь, какая пропускная способность у сети, и, померив ее, выбирает нужный файл для просмотра. Наконец, еще одна нерешенная задача - это как бы еще учесть тот факт, что фильм идет часа 2-3, а интернета то «много», то «мало». Запускать замер пропускной способности сети периодически? Этот способ сработает, но что делать когда нужно переключиться на более или менее «качественный» файл, во время «проседания» или «ускорения» сети? Чтобы это сделать быстро (да еще и не останавливая просмотр того, что уже успело накачаться), нужно заранее знать, с какого места в новом файле нужно запустить скачивание. К несчастью, соотношение смещения от начала файла к времени фильма очень часто нелинейное, из-за переменного битрейта. На быстрых сценах, когда, например, Джеймс Бонд преследует очередного врага, картинка меняется часто и битрейт высокий, а на плавной панораме безоблачного неба картинка почти не меняется и битрейт низкий.

Чтобы справиться с этой задачей, необходимо заранее проиндексировать все файлы (составить пары «время сцены в фильме / позиция от начала файла». Эти пары называют сегментами. После этого можно будет, зная текущее время просмотра, определить, из какого места в другом файле можно скачать следующий сегмент. Для «бесшовного» переключения сегменты из разных битрейтов выравнивают по времени.

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

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

  • HTTP Live Streaming (или HLS, придуман Apple, используется во многих устройствах)
  • HTTP Dynamic Streaming (сокращенно Adobe HDS)
  • MPEG-DASH (стандарт опубликован в конце января)
  • Smooth Streaming (изобретен в Microsoft)

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

Посмотрим подробнее на подготовку адаптивного видео на примере HLS, как наиболее просто устроенного и наиболее широко поддерживаемого устройствами формата.

Манифестом в HLS служит группа плейлистов из одного «мастер-плейлиста» и нескольких «плейлистов потока». Проще всего будет показать это на примере. Допустим, у нас есть очень короткий фильм (всего 3 сегмента по 10 секунд, для простоты), для которого мы сделали 3 битрейта видео- 500 kbps , 1000 kbps и 2000 kbps . В файловой системе сервера он может быть расположен например так:

/master-playlist.m3u8 /500K/ /500K/playlist-500K.m3u8 /500K/segment0.ts /500K/segment1.ts /500K/segment2.ts /1000K/ /1000K/playlist-1000K.m3u8 /1000K/segment0.ts /1000K/segment1.ts /1000K/segment2.ts /2000K/ /2000K/playlist-2000K.m3u8 /2000K/segment0.ts /2000K/segment1.ts /2000K/segment2.ts

Файл master-playlist.m3u8 внутри выглядит так (некоторую информацию я убрал для простоты изложения):

#EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=500,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=640x480 500K/playlist-500K.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=640x480 1000K/playlist-1000K.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2000,CODECS="mp4a.40.2, avc1.640028",RESOLUTION=640x480 2000K/playlist-2000K.m3u8

Те, кто знаком с форматом m3u без труда поймут что тут к чему. В файле содержится три строки-ссылки на другие плейлисты m3u8, а в комментированной значком "#" строке над каждой ссылкой указаны данные соответствующего битрейта. BANDWIDTH, CODECS, RESOLUTION - в общем термины говорят сами за себя. Легко заметить, что отличается только BANDWIDTH, хотя в реальности там все параметры могут быть разными. Задача клиента - понять по этим параметрам, какой плейлист ему в данный момент годится.

Допустим, клиент знает, что у него сейчас «хороший» интернет и предпочитает высокий битрейт (2000К). Клиент выкачивает плейлист 2000K/playlist-2000K.m3u8, который внутри выглядит следующим образом:

#EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.8849, segment0.ts #EXTINF:9.9266, segment1.ts #EXTINF:9.9266, segment2.ts #EXT-X-ENDLIST

Видны ссылки на отдельные сегменты, их длительность в секундах указана строкой выше, для нулевого сегмента, например: "#EXTINF:9.8849". Скачав этот плейлист, клиент начинает проигрывание с первого сегмента по третий. Во время просмотра сегмента обычно качается следующий и так далее. Если клиент почувствует, что очередной сегмент выкачивается слишком медленно, клиент может остановить закачку и начать качать такой же сегмент (для того же места в фильме) из другого плейлиста, например, 500K/playlist-500K.m3u8. Когда скорость интернета восстановится, клиент может опять переключиться на закачку сегментов из плейлиста 1000K или 2000K.

Простота HLS позволяет раздавать его практически с любой серверной платформы (серверной «логики», собственно, при раздаче никакой и не требуется). Отдельные сегменты при необходимости легко кэшируются любым доступным образом.

Теперь посмотрим, какие инструменты доступны для создания и упаковки видео в HLS. Этот процесс состоит из трех основных этапов:

Этап 1. Подготовка исходных файлов (.mov или.mp4 с H.264) с нужным битрейтом и размером кадра.

Тут есть множество вариантов, но самый очевидный - это использовать бесплатный ffmpeg . Есть и множество видеоредакторов с GUI, для MacOSX и Windows. Для примера, описанного выше на этом этапе вам нужно получить три файла.mp4 или.mov, со средними битрейтами 500К, 1000К и 2000К. Важно брать для всех них один и тот же исходник, чтобы в конце всего процесса получились сегменты, одинаковые по времени.

Например, если у вас есть исходный файл movie.mp4 (предполагаем, что его битрейт не ниже 2000К), тогда достаточно будет запустить ffmpeg примерно так (ключи обозначают что звуковую дорожку можно взять как есть, а видео битрейт изменить):

$ ffmpeg -i movie.mp4 -acodec copy -vb 500K movie-500K.mp4 $ ffmpeg -i movie.mp4 -acodec copy -vb 1000K movie-1000K.mp4 $ ffmpeg -i movie.mp4 -acodec copy -vb 2000K movie-2000K.mp4

Этап 2. Создание однобитрейтных плейлистов.

Дальше нужно из каждого movie-*K.mp4 сделать набор из плейлиста m3u8 и сегментов *.ts. Важно, чтобы сегменты получились синхронными между разными битрейтами. Скажу сразу, ffmpeg умеет «нарезать» mp4 в m3u8 + сегменты, но только в рамках одного битрейта. К сожалению, мастер-плейлист потом придется создавать руками. Это не очень сложно (в минимальном варианте достаточно любого текстового редактора), но если вы случайно являетесь Apple iOS или OSX разработчиком, то могу посоветовать пакет HTTP Live Streaming Tools (для MacOSX). В него входит несколько программ, из которых нам пригодятся две: mediafilesegmenter и variantplaylistcreator . Первая превращает mp4 файл в плейлист m3u8 и «нарезает» сегменты, вторая собирает несколько однобитрейтных плейлистов в мастер-плейлист.

Итак, создаем три плейлиста из трех файлов полученных на предыдущем шаге (предполагается, что файлы movie-*.mp4 лежат в текущей папке).

$ mkdir 500K $ mediafilesegmenter -I -f 500K -B segment movie-500K.mp4 $ mkdir 1000K $ mediafilesegmenter -I -f 1000K -B segment movie-1000K.mp4 $ mkdir 2000K $ mediafilesegmenter -I -f 2000K -B segment movie-2000K.mp4

Небольшие пояснения:

Ключ -I требует создавать специальный файл (variant plist), который потребуется позже в программе variantplaylistcreator.

Ключ -f 500K обозначает каталог (500K), в который должны складываться “нарезанные” сегменты.

Ключ -B segment сообщает как должны называться файлы сегментов (префикс, который будет дополнен цифрой и расширением.ts).

На этом этапе у вас должны образоваться 3 папки, заполненные файлами сегментов, 3 файла-плейлиста по одному в каждой папке и 3 файла с расширением.plist. Однобитрейтные плейлисты по умолчанию называются prog_index.m3u8. Можно заодно проверить, как они воспроизводятся на клиентах, для этого нужно выложить их на HTTP сервер и запустить на клиенте просмотр на любой из этих prog_index.m3u8.

Этап 3. Собираем три отдельных плейлиста в единый мастер.

Для этого используется variantplaylistcreator . Запускается он так (для нашего примера):

Variantplaylistcreator -r -o movie.m3u8 \ 500K\prog_index.m3u8 movie-500K.plist \ 1000K\prog_index.m3u8 movie-1000K.plist \ 2000K\prog_index.m3u8 movie-2000K.plist

Ключ -r требует указывать так называемые resolution tags (размер кадра видео). Вообще он не обязателен, но если вы упаковываете в один мастер-плейлист несколько видео с разным разрешением (допустим, мобильное качество, 480p и 1080p), то стоит его указать. В этом случае клиент прямо из мастер-плейлиста узнает, какие разрешения доступны.

Ключ -o movie.m3u8 обозначает имя выходного файла (мастер-плейлиста).

Остальные части командной строки - это пары плейлист-plist для каждого битрейта, которые были получены на предыдущем этапе.

Теперь у нас появился мастер-плейлист movie.m3u8 . Можно выкладывать текущий каталог и подкаталоги на HTTP сервер и запускать на клиенте просмотр файла movie.m3u8. Кстати, файлы *.mov больше не нужны, для сокращения занятого под контент места их можно убрать из папки.

На этом у меня пока все, но если данная тема интересна уважаемому сообществу, могу ее продолжить и в будущих постах рассказать, как добавить в HLS альтернативные звуковые дорожки и субтитры, а также как из HLS сделать MPEG-DASH совместимый со Smart TV. Спасибо за внимание.

Flussonic Media Server поддерживает раздачу видео по протоколу HLS.

Многие из доступных возможностей нестандартны для HLS, но мы поддерживаем их для вашего удобства.

Поддерживаемые кодеки: H264, H265, MPEG2 video, AAC, MP3, MPEG2 audio, AC-3.

Flussonic Media Server позволяет получать по HLS прямой эфир, видео по запросу, видео из архива (catchup и timeshift).

Простое воспроизведение HLS

Если у вас есть простой live поток или файл (одно видео, один звук), то URL для воспроизведения через HLS очень простой:

http://flussonic-ip/STREAMNAME/index.m3u8

где flussonic-ip - это пример адреса + порта вашего Flussonic Media Server.

Flussonic Media Server также принимает playlist.m3u8 в конце URL для обратной совместимости с другими серверами.

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

Мультиязычный HLS

Если вы хотите воспроизвести ваш мультиязычный поток на iPhone, вам нужно использовать тот же http://192.168.2.3:8080/STREAMNAME/index.m3u8

Но если вы хотите посмотреть мультиязычный поток используя VLC или приставку, нужно включать video.m3u8.

URL для плеера: http://flussonic-ip/STREAMNAME/video.m3u8

Это связано с тем, что, согласно требованиям Apple HLS, для каждого отдельного языка нужно указывать отдельный плейлист с audio only вариантом. В MPEG-TS другой механизм: рядом с видео укладываются все аудиодорожки, и плеер сам выбирает, что будет проигрывать. Чтобы видео можно было посмотреть на iPhone, оно должно соответствовать требованиям Apple. Но VLC и приставки, в нарушение стандарта HLS, ожидают старый вариант MPEG-TS, преобразованный в HLS. Поэтому нужно включать video.m3u8 .

Добавление «Audio only» для Apple

Apple требует, чтобы у всех ваших потоков был вариант без видео, только звук.

Они считают, что если пользователь смотрит видео по 3G и, оказавшись в зоне неуверенного приема, лучше у него будет только звук, чем буфферизация.

Вы можете включить эту опцию в Flussonic Media Server:

stream ort { url udp: // 239.0.0.1 : 1234 ; add_audio_only ; }

Помните, что это может сделать ваш index.m3u8 адрес невоспроизводимым в VLC или приставке. В таком случае используйте video.m3u8 .

Отдельные битрейты для приставок

Когда у вас есть мультибитрейтный мультиязычный контент и вы хотите проиграть его на приставке, которая не поддерживает мультибитрейтный HLS плейлисты, вы можете запросить с Flussonic Media Server отдельные плейлисты с одним видео и всеми звуковыми дорожками, как с опцией mono:

http: // flussonic - ip / STREAMNAME / video1 . m3u8

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

Если вы хотите доставлять мультиязычные мультибитрейтные потоки на приставки, не понимающие стандарт Apple для мультиязычности, используйте video.m3u8:

http: // flussonic - ip / STREAMNAME / video . m3u8

Это мультибитрейтный плейлист, который отдает список плейлистов с разными качествами: video1.m3u8, video2.m3u8 и т.д.

DVR catchup playback

Когда ваш поток уже записан на сервере нашим DVR , вы можете воспроизвести видео через HLS, используя время начала и конца передачи (например, из EPG).

http: // flussonic - ip / STREAMNAME / archive -1508403742-3600 . m3u8

Этот плейлист будет т.н. variant, если в потоке будет больше одной звуковой дорожки или больше одного битрейта. Он будет давать список сегментов начиная с UTC 1362504585 (2013, Март, 5, 17:29:45 GMT) и один час вперед.

Mono URL даст список сегментов содержащих все дорожки в mpeg-ts:

http: // flussonic - ip / STREAMNAME / mono -1362504585-3600 . m3u8

Более конкретный videoN плейлист даст список сегментов с N видео дорожкой и всеми звуковыми дорожками:

http: // flussonic - ip / STREAMNAME / video1 -1362504585-3600 . m3u8

и variant видео плейлист со списком videoN плейлистов:

http: // flussonic - ip / STREAMNAME / video -1362504585-3600 . m3u8

Rewind playlist

Есть специальный плейлист "rewind-N.m3u8" с большим «скользящим» окном, позволяющий перематывать и ставить на паузу HLS потоки на долгие часы.

http: // flussonic - ip / STREAMNAME / rewind -7200 . m3u8

7200 - длина HLS плейлиста в секундах. Это означает, что ваши клиенты могут поставить эфир на паузу на 2 часа или перемотать на начало футбольного матча без обращения по специальными архивным ссылкам.

Предоставление услуг IP-телевидения через сеть Интернет и локальные компьютерные сети приобретает всё более массовые формы. На территории стран СНГ почти не осталось крупных провайдеров не транслирующих видео через multicast в свои локальные сети, то-есть предоставляющие услугу IPTV . Но предоставление услуги тв за пределами своей локальной сети связанно с некоторыми аппаратными издержками и сложностью обеспечения необходимого качества вещания.

HTTP Live Streaming также известный как HLS , является протоколом связи реализованным компанией Apple. Его особенность в том, что общий поток разбивается на последовательность малых загрузочных файлов, каждая загрузка загружает один небольшой фрагмент транспортного потока. Когда поток воспроизводится, клиент может выбрать один из нескольких различных альтернативных потоков, содержащих тот же материал, записанных для различных скоростей передачи данных, что позволяет адаптироваться к доступной скорости передачи данных. В начале сеанса потоковой передачи, загружается расширенный M3U (m3u8) плэйлист, содержащий метаданные для различных подпотоков, которые доступны. Так как при запросах используются только стандартные операции HTTP, HTTP Live Streaming способен обходить любой брандмауэр или прокси-сервер, который пропускает стандартный HTTP-трафик, в отличие от UDP-протоколов, таких как RTP.

HLS основан на HTTP. HLS также определяет стандартный механизм шифрования с использованием AES и способ распределения ключа безопасности с использованием HTTPS или HTTP-куки, которые вместе обеспечивают простую систему защиты авторских прав.

Принцип работы HLS

Теперь выясним в чём же преимущества и недостатки этой технологии. Преимущества несомненны и очевидны. Это, в первую очередь, адаптивность скорости передачи данных к свойствам линии и принимающего устройства, во-вторых, встроенные механизмы защиты авторских прав. В-третьих, не требуется роутер с ограничением ширины multicast-потока по WI_FI, что помогало бы избежать поглащения всей ширины канала multicast-потоками, в случае вещания IP-телевидения c помощью multicast. Также не требуется дополнительное устройство с функцией UDP-proxy для конвертации multicast-потока в HTTP, что часто требуется для мобильных устройств, хотя сказывается на аппаратной нагрузке на роутера или другое устройство осуществляющее функцию UDP-proxy в локальной сети абонента. Стандарт HLS стал довольно распространён и поддерживается почти всеми современными видео плэерами и приставками для IPTV.

IPTV приставка

Существенным минусом является наличие у абонентов мультимедийных приставок и smart-tv приставок с устаревшими прошивками или устаревших конструкций, которые не поддерживают стандарты HLS или же поддерживают их некорректно. Также одной из проблем является невозможность правильно выбрать качество для стабильной трансляции в условиях изменения характеристик линии в интервалах времени меньших, чем длительность запрашиваемого видеофрагмента.

Как показала практика, лучшим транспортом для видео по сравнению с RTMP, является HLS. Причины этого:

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

    Упрощение всей серверной инфраструктуры. В силу идеи видео отдается по кусочкам, через 80 порт по http. За отдачу статики может отвечать сам nginx. Отдача статики (видео кусочки по 50кБ) задача для nginx очень легкая.

    Поскольку количество кусочков постоянно, старые удаляются, новые добавляются, жесткий диск никогда не переполнится.

    Распространенность больше, чем у RTMP. HLS с кодировкой видео H.264 поддерживается iOS и работает без лишних действий. По данным на 1 июля 2014 с хабры подключений потокового видео с транспортом HLS - 55%, RTMP - 26%, RTSP - 15% и MPEG-DASH менее 1%.

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

    Гораздо проще, чем вещание в RTSP в принципе. Так как нет таких процедур, как push (публикация потока) или pull (получение потока).

    Гораздо более http friendly формат.

Недостатки следующие:

    Все таки не все устройства поддреживают этот формат. Android версий меньше 4.2 официально не поддерживает кодек H.264 и транспорт, но на Android вместо браузера для просмотра можно использовать стороннее приложение - например MX Player

    Все зависит от камеры. Если камера глючная, например Dlink DCS-3010, то вся система будет работать из рук вон плохо (ffmpeg постоянно отваливается). Например камеры AXIS M1011-W, HIKVISION DS-2CD2412F-IW работают в такой связке хорошо (до месяца без нареканий (дольше просто не тестировал)). Так же большое значение имеет прокладка кабеля. В этом смысле будем рассматривать идеальный вариант.

Что такое транспорт HLS

Видео поток в кодировке h.264 (Кстати: profile baseline понимают Android устройства), делится на кусочки с расширением *.ts , например по 5 секунд, создается плэйлист в live.m3u8 , с последовательным описанием этих кусочков. Предварительно определется длина плэйлиста например 10 кусков. Когда появляется 11 кусочек видео, 1 кусок видео удаляется, плейлист пересоздается. Более подробно можно посмотреть на сайте разработчика .

Для работы системы настроим изображение с камер так, как мы хотим видеть на сайте, формат картинки и качество изображения. На сервере перекодировать не будем. Камера для того и придумана чтобы отдавать такое изображение какое нужно. В камерах обычно есть несколько профилей. Можно настроить один профиль для H.264, для HLS, а второй c MPEG4 для MPEG-DASH. Так же можно настроить различное качество, для широкого и узкого канала интернет. Думайте сами - решайте сами.

Важно! Камера должна на выходе иметь изображение, которое не нужно перекодировать.

Структурная схема для высокой нагрузки

Камера(rtsp) ----->

-----> одно подключение FFmpeg(rtsp->hls) -> Nginx(nginx-rtmp-module) ----->

-----> одно подключение к промежуточному proxy nginx c большим кэшем =====>

=====> много клиентов JWPlayer(hls)

Наш сервер подключается с помощью ffmpeg к камере и регистрируется в приложении nginx hls. nginx создает кусочки и плэйлист в определенной директории. Далее отдает эти кусочки на прокси сервер. Клиенты подключаются к прокси серверу с помощью JWPlayer .

Настройка nginx application

Соберем nginx с nginx-rtmp-module. Эта процедура детально описывается в статье .

Допустим у нас несколько камер, разделим их по порядковому номеру. Опишу конфигурацию nginx для 2 камер. Статические изображения кэшируем на 5 минут в локальном кэше, если картинка не грузится в течении 5 секунд отдаем статическую заставку.

# nano /etc/nginx/nginx.conf

Отредактируем конфигурацию nginx

user www - data ; worker_processes auto ; pid / run / nginx . pid ; error_log / var / log / nginx / nginx_error . log debug ; env PATH ; events { # multi_accept on ; } http { access_log / var / log / nginx / access . log ; error_log / var / log / nginx / error . log ; include mime . types ; default_type application / octet - stream ; sendfile on ; keepalive_timeout 65 ; proxy_cache_path / var / www / cache / local levels = 1 : 2 keys_zone = nginx_local_cache : 1 m inactive = 30 m max_size = 512 M ; proxy_temp_path / var / www / cache / local / tmp ; server { listen 80 ; # rtmp stat location / stat { rtmp_stat all ; rtmp_stat_stylesheet stat . xsl ; } location / stat . xsl { # you can move stat . xsl to a different location root / etc / nginx ; } location / { rtmp_control all ; } error_page 500 502 503 504 / 50 x . html ; location = / 50 x . html { root html ; } include cameras_http_locations . conf ; } } rtmp { access_log / var / log / nginx / rtmp_access . log ; server { listen 1935 ; ping 30 s ; notify_method get ; include cameras_rtmp_applications . conf ; } }

Создадим путь для кэш # mkdir /var/www/cache/local Поправим права для кэш:

# chmod -R 755 /var/www/cache/local # chown -R www-data:www-data /var/www/cache/local`

Создадим http locations для камер:

# nano cameras_http_locations.conf

types { application / vnd . apple . mpegurl m3u8 ; video / mp2t ts ; } # отдаем изображение с камеры 1 - /1/img/ # для всех камер разные, т.к. ip адреса камер разные "http://192.168.0.2/GetImage.cgi?CH=1" # отдаем изображение с камеры 2 - /2/img/ location / 1 / img / { proxy_cache nginx_local_cache ; proxy_cache_key $ request_uri ; expires 1 m ; # кэшируем на 1 минуту add_header Cache - Control public ; # для кэширования на проксе proxy_ignore_headers Cache - Control ; # для удаления заголовков с камеры proxy_pass "http://192.168.0.3/GetImage.cgi?CH=1" ; proxy_set_header Authorization "Basic " ; error_page 502 504 404 @ fallback_img ; } # отдаем плэйлист - /1/hls/live.m3u8 или /3/hls/live.m3u8 # плэйлист кэшируется 10 секунд на проксе location ~* / hls / . * \ . m3u8 $ { rewrite "/(.*)/hls/(.*)$" / hls - $ 1 / $ 2 break ; # переделываем запрос / 1 / hls / в / hls - 1 / root / tmp / ; expires 10 s ; add_header Cache - Control public ; } # отдаем кусочек видео с камер - /1/hls/live-12345678.ts или /2/hls/live-12345678.ts # кэширование на локальном компьютере не требуется # кусочек кэшируется 3 минуты на проксе location ~* / hls / . * \ . ts $ { rewrite "/(.*)/hls/(.*)$" / hls - $ 1 / $ 2 break ; root / tmp / ; expires 3 m ; add_header Cache - Control public ; } # именованный location если картинки нет location @ fallback_img { rewrite (. + ) / fallback . jpg break ; root / etc / nginx / ; }

Создадим файл hls конфигурации сервера rtmp с applications для наших камер:

# nano cameras_rtmp_applications.conf

chunk_size 4000 ; application hls_1 { live on ; sync 10 ms ; exec_static ffmpeg - i rtsp : //admin:[email protected]:554/live1.sdp -c copy -f flv -an rtmp://localhost:1935/hls_1/live 2>>/var/log/nginx/ffmpeg_1.log; hls on ; hls_path / tmp / hls - 1 / ; # путь хранения кусочков на сервере hls_fragment_naming timestamp ; # использовать timestamp для именования кусочков } application hls_2 { live on ; sync 10 ms ; exec_static ffmpeg - i rtsp : //admin:[email protected]:554/live1.sdp -c copy -f flv -an rtmp://localhost:1935/hls_2/live 2>>/var/log/nginx/ffmpeg_2.log; hls on ; hls_path / tmp / hls - 2 / ; hls_fragment_naming timestamp ; }

Содержимое директории /tmp/hls-1/

$ ls / tmp / hls - 1 / live - 10458360. ts live - 13292010. ts live - 16129440. ts live - 18963270. ts live - 10930050. ts live - 13767390. ts live - 16602660. ts live - 19435050. ts live - 11405250. ts live - 14239260. ts live - 17072820. ts live . m3u8 live - 11878560. ts live - 14710860. ts live - 17544960. ts live - 12348630. ts live - 15182550. ts live - 18020160. ts live - 12821760. ts live - 15658740. ts live - 18492750. ts

Пример файла live.m3u8

#EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:35 #EXT-X-TARGETDURATION:5 #EXTINF:5.224, live - 16602660. ts #EXTINF:5.246, live - 17072820. ts #EXTINF:5.280, live - 17544960. ts #EXTINF:5.251, live - 18020160. ts #EXTINF:5.228, live - 18492750. ts #EXTINF:5.242, live - 18963270. ts

Решение проблемы с отваливающимися камерами

Самое правильное решение, поменять глючную камеру. Это помогает в 90% случаев. Если нет возможности и нужно как то жить дальше, то поможет следующее решение.

Это решение состоит из двух - взаимодополняющих:

    Запускать отдельный процесс nginx на каждую камеру и общий процесс по отдаче статики. То есть для двух камер написать отдельные конфиги с rtmp серверами и один общий с http. Тогда глючная камера не будет влиять на общий процесс.

    Если поток с камеры нарушается в результате ее глючности (перегрева, плохой прокладки витухи, недостаточное питание по PoE и т.п.), то камера отвалится, дочерний процесс ffmpeg будет отбраковывать пакеты и nginx перестанет писать кусочки видео. А когда процесс ffmpeg завершится, nginx удалит все файлы из директории с кусочками. Этот момент очистки папки мы вычисляем по cron и рестартим необходимый процесс nginx.

В для каждой камеры создается в /etc/init.d/ создается исполняемый скрипт, копия nginx , c именем camera_1 и camera_2

# cp /etc/init.d/nginx /etc/init.d/camera_1 # cp /etc/init.d/nginx /etc/init.d/camera_2 # chmod +x /etc/init.d/camera_1 # chmod +x /etc/init.d/camera_2

Редактируем скрипт запуска nginx.

nano / etc / init . d / nginx

Меняем переменную DAEMON_OPTS . Основной демон nginx будет отдавать всю статику. А так же будет запускать и останавливать демоны отвечающие за камеры./ init . d / camera_1 stop fi if [ - f "/etc/init.d/camera_2" ]; then / etc / init . d / camera_2 stop fi

Добавляем в функцию do_reload:

# reload cameras if [ - f "/etc/init.d/camera_1" ]; then / etc / init . d / camera_1 reload fi if [ - f "/etc/init.d/camera_2" ]; then / etc / init . d / camera_2 reload fi

Редактируем скрипт запуска nginx для камеры 1 camera_1 и для камеры 2 camera_2 по примеру.

# nano /etc/init.d/camera_1

Меняем переменные DAEMON_OPTS и DESC

DESC = "camera_1 for CAMERA-1" DAEMON_OPTS = "-c /etc/nginx/nginx_1.conf"

Редактируем скрипт запуска nginx для камеры 2 camera_2 по примеру.

В /etc/nginx/nginx_0.conf с http locations я пишу волшебные строки:

# DIR-PROCESS-NAME /tmp/hls-1/ camera_1 # DIR-PROCESS-NAME /tmp/hls-2/ camera_2

В них указано через пробел искомое слово DIR-PROCESS-NAME директория и наименование процесса который нужно перезагрузить.

Проверка:

# service nginx start # service camera_1 restart * Restarting camera_1 for CAMERA - 1 configuration nginx # service camera_2 restart * Restarting camera_2 for CAMERA - 2 configuration nginx

Скрипт который перезагружает камеры. Он проходится по папкам с кусочками, ищет, где нет файлов *.m3u8 . Если в папке нет файлов, ищет соответствующий демон по конфигу основного демона, по строчке DIR-PROCESS-NAME . Перезагружает его.

# nano /script/cameras_reloader.sh

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

#!/bin/bash PATH = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin mask = "*.m3u8" dir = "/tmp/hls-*" function find_process(){ process_str = $(cat /etc/nginx/nginx_0.conf | grep "# DIR-PROCESS-NAME" | grep $1 | cut -d" " -f4) echo $process_str } for hls_dir in $dir ; do find_result = $(find $hls_dir -name $mask -type f) if [ -z $find_result ] ; then process = $(find_process $hls_dir ) service $process restart fi done sleep 15s

Сравнение HLS с MPEG-DASH

MPEG-DASH это аналог HLS созданный компанией Google, в качестве транспорта для MPEG-4. Этот транспорт малораспространен и практически не поддерживается. Его идеалогия такая же, разбить поток на кусочки, только кусочков больше, отдельные куски для видео, отдельные для аудио. В nginx-rtmp-module этот формат настраивается аналогично HLS.

Пробуйте, копируйте, дерзайте!

Если статья была вам полезна просьба щелкнуть по рекламе. Спасибо!