RTSP — это простой сигнальный протокол, который уже много лет не могут ничем заменить, и надо признать, что не особо стараются.


Скажем, есть у нас IP камера с поддержкой RTSP. Всякий, кто щупал трафик акула-кабелем, расскажет, что там сначала идет DESCRIBE, потом PLAY, и вот полился трафик напрямую по RTP или завернутый в тот же TCP канал.


Типичная схема установки RTSP соединения выглядит так:



Понятно, что для браузеров поддержка RTSP протокола избыточна и нужна как собаке пятая нога, поэтому браузеры не спешат ее массово внедрять и вряд ли когда-то к этому придут. С другой стороны, браузеры могли бы создавать прямые TCP соединения, и это бы решило задачу, но тут все упирается в безопасность — где вы видели браузер, который дает скрипту напрямую использовать транспортные протоколы?


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


В этой статье мы разберемся как этого достичь. А так как на дворе круглая цифра, придадим статье актуальности и навесим лейбл 2020, тем более, что так оно и есть.


Итак, какие технологии отображения видео на веб-странице нужно забыть в 2020 году? Flash в браузере. Он умер. Его больше нет — вычеркиваем.


Три годных способа


Инструменты, которые дадут смотреть видеопоток в браузере сегодня, это:


  1. WebRTC
  2. HLS
  3. Websocket + MSE

Что не так с WebRTC


Два слова: ресурсоемкий и сложный.


Ну какая же ресурсоемкость? — отмахнетесь вы, ведь сегодня процессоры мощные, память дешевая, в чем проблема? Ну, во-первых, это принудительное шифрование всего трафика даже если вам это не требуется. Во-вторых, WebRTC — сложная двухсторонняя связь и обмен фидбэками о качестве канала между пиром и пиром (в данном случае, между пиром и сервером) — в каждый момент времени рассчитывается битрейт, отслеживаются потери пакетов, принимаются решения по их досылке, и вокруг этого всего рассчитывается синхронизация аудио и видео, так называемый lipsync, чтобы губы спикера не расходились с его словами. Все эти вычисления, а также входящий на сервер трафик аллоцируют и освобождают гигабайты оперативной памяти в реальном времени и, если что-то пойдет не так, 256-гигабайтный сервер с 48 ядрами CPU легко уйдет в штопор несмотря на все гигагерцы, нанометры и DDR 10 на борту.


Получается стрельба по воробьям из искандера. Нам надо высосать всего лишь RTSP поток и показать его, а WebRTC говорит: да, вперед, но придется за это заплатить.


Чем хорош WebRTC


Задержка. Она действительно низкая. Если вы готовы заплатить производительностью и сложностью за низкую задержку, WebRTC — самый подходящий вариант.


Чем хорош HLS


Два слова: работает везде


HLS — это медленный вездеход в мире отображения Live-контента. Везде работает благодаря двум вещам: HTTP-транспорту и протекции Apple. Действительно, HTTP-протокол вездесущ, я чувствую его присутствие, даже когда пишу эти строки. Поэтому, где бы вы ни были и за каким бы древним планшетом ни серфили, HLS (HTTP Live Streaming) до вас доберется и доставит видео на ваш экран, будьте уверены.


И все бы хорошо, но.


Чем плох HLS


Задержка. Есть, например, проекты видеонаблюдения за стройкой. Объект строится годами, а в это время несчастная камера днем и ночью, 24 часа в сутки снимает строительную площадку. Это пример того, где не нужна низкая задержка.


Другой пример — кабаны. Реальные кабаны. Фермеры штата Огайо страдают от нашествия диких кабанов, которые как саранча поедают и вытаптывают урожай, угрожая тем самым финансовому благополучию хозяйства. Предприимчивые стартаперы запустили систему видеонаблюдения с RTSP-камер, которая в реальном времени наблюдает за угодьями и опускает ловушку при нашествии непрошенных гостей. В данном случае низкая задержка критична и, при использовании HLS (с задержкой 15 секунд), кабаны убегут до того, как ловушка будет активирована.



Еще пример: видео-презентации, в которых вам демонстрируют товар и ожидают оперативного ответа. В случае большой задержки, вам покажут товар в камеру, потом спросят “ну как?” и это дойдет только через 15 секунд. За 15 секунд сигнал может 12 раз сгонять до луны и обратно. Нет, такая задержка нам не нужна. Она больше похожа на показ предзаписанного видео, чем на Live. Но ничего удивительного нет, HLS так и работает: он пишет куски видео на диск или в память сервера, а плеер скачивает записанные куски. В результате и получается HTTP Live, который совсем не Live.


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


Здесь надо отметить, что Apple осенью 2019 анонсировала HLS Low Latency, но это уже другая история. Будем смотреть, что получилось, более детально позже. А у нас в запасе есть еще MSE.


Чем хорош MSE


Media Source Extension — это нативная поддержка воспроизведения пакетного видео в браузере. Можно сказать, нативный плеер для H.264 и AAC, которому можно скармливать сегменты видео и который не привязан к транспортному протоколу в отличии от HLS. По этой причине транспорт можно выбрать по протоколу Websockets. Иными словами, сегменты уже не скачиваются по древней технологии Request-Response (HTTP), а весело льются через Websockets соединение — почти прямой TCP канал. Это здорово помогает с уменьшением задержек, которые могут снижаться до 3-5 секунд. Задержка не супер, но подходящая для большинства проектов, не требующих жесткого реального времени. Сложность и ресурсоемкость тоже относительно невысокая — открывается TCP канал и по нему льются почти те же HLS сегменты, которые собираются плеером и помещаются на воспроизведение.


Чем плох MSE


Работает не везде. Как и WebRTC, проникновение в браузеры меньше. Особенно в непроигрывании MSE замешаны айфоны (iOS), что делает MSE малопригодным в качестве единственного решения для какого-либо стартапа.


Полностью доступен в браузерах: Edge, Firefox, Chrome, Safari, Android Browser, Opera Mobile, Chrome для Android, Firefox для Android, UC Browser для Android



В iOS Safari ограниченная поддержка MSE появилась совсем недавно, начиная с 13 версии iOS.



RTSP нога


Мы обсудили доставку по направлению видеосервер > браузер. Кроме этого, потребуются еще две вещи:


1) Доставить видео от IP камеры до сервера.


2) Конвертировать полученное видео в один из вышеописанных форматов / протоколов.


Здесь на сцену выходит серверная часть.


ТаТам… Web Call Server 5 (для знакомых просто WCS). Кто-то должен получить RTSP трафик, правильно депакетизировать видео, конвертировать его в WebRTC, HLS или MSE, желательно без пережатия транскодером, и выдать в сторону браузера в приличном, не испорченном артефактами и фризами виде.


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


Схема доставки


В итоге вырисовывается целостная схема доставки RTSP контента с конвертацией на промежуточном сервере.



Один из самых частых запросов наших индийских коллег “А можно? Напрямую без сервера?”. Нет, нельзя — нужна серверная часть, которая будет делать эту работу. В облаке, на железке, на corei7 на балконе, но без нее никак.


Вернемся к нашему 2020 году


Итак, рецепт приготовления RTSP в браузере:


  1. Взять свежий WCS (Web Call Server).
  2. Добавить по вкусу: WebRTC, HLS или MSE.
  3. Разложить на веб-странице.


    Приятного аппетита!



Нет, еще не все


У пытливых нейронов обязательно возникнет вопрос “Как? Ну как это сделать? Как это будет выглядеть в браузере?”. Вашему вниманию представляется минимальный WebRTC плеер, сделанный на коленке:


1) Подключаем к web-странице основной API скрипт flashphoner.js и скрипт my_player.js который создадим чуть позже.


<script type="text/javascript" src="../../../../flashphoner.js"></script>
<script type="text/javascript" src=my_player.js"></script>  

2) Инициализируем API в теле web-страницы


<body onload="init_api()"> 

3) Кидаем на страницу div, который будет контейнером для видео. Укажем ему размеры и границу.


<div id="myVideo" style="width:320px;height:240px;border: solid 1px"></div>

4) Добавляем кнопку Play, нажатие по которой будет инициировать коннект к серверу и начинать воспроизведение


<input type="button" onclick="connect()" value="PLAY"/>

5) Теперь создаем скрипт my_player.js – который будет содержать основной код нашего плеера. Описываем константы и переменные


var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS;
var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS;
var session;

6) Инициализируем API при загрузке HTML страницы


function init_api() {
        console.log("init api");
             Flashphoner.init({
    });
}

7) Подключаемся к WCS серверу через WebSocket. Что бы все работало правильно замените "wss://demo.flashphoner.com" на адрес своего WCS


function connect() {
        session = Flashphoner.createSession({urlServer: "wss://demo.flashphoner.com"}).on(SESSION_STATUS.ESTABLISHED, function(session){    
    console.log("connection established");
    playStream(session);
  });
}

8) Далее передаем два параметра: name и display: name — RTSP URL проигрываемого потока. display — элемент myVideo, в который будет смонтирован наш плеер. Здесь также укажите URL нужного вам потока, вместо нашего.


function playStream() {
  var options = {name:"rtsp://b1.dnsdojo.com:1935/live/sys2.stream",display:document.getElementById("myVideo")};  
  var stream = session.createStream(options).on(STREAM_STATUS.PLAYING, function(stream) {
    console.log("playing");
  });
  stream.play();
}

Сохраняем файлы и пробуем запустить плеер. Играет ваш RTSP поток?


Когда мы тестировали, у нас играл этот: rtsp://b1.dnsdojo.com:1935/live/sys2.stream.


А выглядит это так:


Плеер до нажатия на кнопку «Play»



Плеер с запущенным видео



Кода совсем немного:


HTML


<!DOCTYPE html>
<html lang="en">
<head>   
    <script type="text/javascript" src="../../../../flashphoner.js"></script>
    <script type="text/javascript" src=my_player.js"></script> 
</head>

<body onload="init_api()">
<div id="myVideo" style="width:320px;height:240px;border: solid 1px"></div>
<br/><input type="button" onclick="connect()" value="PLAY"/>
</body>
</html> 

JavaScript


//Status constants
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS;
var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS;

//Websocket session 
var session;

//Init Flashphoner API on page load
function init_api() {
    console.log("init api");
        Flashphoner.init({
        });
}

//Connect to WCS server over websockets
function connect() {
    session = Flashphoner.createSession({urlServer: "wss://demo.flashphoner.com"}).on(SESSION_STATUS.ESTABLISHED, function(session){       
        console.log("connection established");
        playStream(session);
    });
}

//Playing stream with given name and mount the stream into myVideo div element
function playStream() {
    var options = {"rtsp://b1.dnsdojo.com:1935/live/sys2.stream",display:document.getElementById("myVideo")};    
    var stream = session.createStream(options).on(STREAM_STATUS.PLAYING, function(stream) {
        console.log("playing");
    });
    stream.play();
}

В качестве серверной части используется demo.flashphoner.com. Полный код примера доступен ниже, в подвале со ссылками. Хорошего стриминга!


Ссылки


Код плеера на github


Интеграция RTSP-плеера в веб-страницу или мобильное приложение

Комментарии (14)


  1. andreymal
    05.12.2019 17:44

    А есть какой-нибудь относительно стандартный протокол передачи видео через вебсокет для MSE, или все велосипедят кто во что горазд?


    1. Videoman
      05.12.2019 18:12

      В кратце:
      MSE принимает буфера в виде чанков в формате fragmented mp4. Это подмножество стандарта ISO BMFF (base media file format). Каждый такой фрагмент, это самостоятельный файл с заголовком и данными. Данные представляют из себя аудио и видео семплы медиа-последовательности определенной длины.
      Для подготовки данных на проигрывание с помощью MSE вам нужно подготовить такие файлы на диске или в памяти, на сервере или оборачивать напрямую на клиенте. Каждый новый буфер для проигрывая с MSE, представляет из себя именно такой файл.
      Других альтернатив нет.


      1. andreymal
        05.12.2019 18:17

        Я это всё знаю, но на вопрос о стандартном протоколе передачи видео через вебсокет это не отвечает.


        1. Videoman
          05.12.2019 18:27

          MSE создавался вместе с MPEG-DASH, в котором стандартный подход, это чтение чанков в файлах по HTTP. По-этому ответ — нет, придется делать самому, если нужно.


    1. fpn Автор
      05.12.2019 21:47

      Есть. Только не протокол, а формат. И даже в виде RFC tools.ietf.org/html/rfc6184.
      Спецификация подробно расписывает как должны формироваться RTP видео пакеты для передачи по сети и как должны распаковываться. Собственно IP камеры по этой спецификации и работают (в идеальном мире) — пакуют по ней видео и заворачивают в TCP. Плееры получают и депакетизируют. Т.е. ничего не мешает паковать и заворачивать в Websockets. Только на JavaScript/TypeScript будет парсить тяжеловато чтобы спустить в MSE. Т.е. стандарт как-бы есть и можно использовать, но слишком тяжелый — легче реализовать упрощенный формат.


      1. Videoman
        06.12.2019 12:07

        На самом деле очень многое мешает использовать MSE в отличии от WebRTC. H.264 поток от камеры должен быть не абы какой, а сильно ограниченный (начиная, по-моему, с 76 версии Хром начал действовать жестко по стандарту).
        Сегменты скармливаемые в буфера MSE должны начинаться и заканчиваться по RAP-фреймам (для H.264 это IDR — не путать с I). Если это не так, то Хром, например, не видит весь сегмент. До этого на практике было достаточно I-фреймов.
        К сожалению многие камеры или кабельные сети формируют поток только из I-фреймов, или так, что IDR-фреймы идут раз в несколько десятков минут, и без перекодирования их никак не скормить MSE.


  1. Tarson
    05.12.2019 20:44

    Иными словами, сегменты уже не скачиваются по древней технологии Request-Response (HTTP), а весело льются через Websockets соединение — почти прямой TCP канал

    Путаница у вас какая-то. Внизу всего два транспортных протокола TCP и UDP. Как раз первый тормозной, второй быстрый — дейтаграмммы. А HTTP он текстовый сверху TCP. А сам TCP и есть Request-Response. Оно не древнее, а надежное с гарантией доставки, поэтому и медленное.
    Я просто как раз сейчас видео гоняю с телефона на комп по UDP (а раньше TCP юзал ) и посты на эту тему делал недавно.


    1. fpn Автор
      05.12.2019 21:29

      Путаницы нет. Внизу под обоими протоколами HTTP и Websockets работает ровно один протокол — TCP. И сравнение в скорости доставки идет между этими двумя протоколами. В случае HTTP — Request Response, в случае Websocket — поток текстовых сообщений. Второй способ быстрее, несмотря на то, что оба TCP.


  1. neco
    06.12.2019 10:56

    а про создание WCS то будет? как создать сервер и прикрутить к нему камеру?


    1. fpn Автор
      06.12.2019 23:10
      +1

      Про создание сервера WCS в DigitalOcean было здесь
      Можно и на обычный сервер установить, но придется чуть дольше повозиться.


      1. neco
        06.12.2019 23:33

        и не в дигитал океане а у себя?
        а под виндой? :)


        1. fpn Автор
          07.12.2019 18:54

          у себя на Linux — см системные требования. рекомендуем Centos 7 или Ubuntu 18.04, как стабильность, проверенную временем и дорогами. Java рекомендуем 8 или 12.
          на Windows — только в каком-либо гипервизоре, в WSL тоже должно работать.


  1. Lordick
    06.12.2019 11:09

    Буквально вчера удалось скрестить html5_rtsp_player и какой-то случайно нагугленный rtsp вебсокет прокси сервер на дотнете (осторожно, китайская ссылка), работает аднака.


  1. CrazysAlien
    07.12.2019 20:47

    Ivideon, как я понял, скрестили WebRTC и HLS и даже сделали на этом свой бизнес, обещая (и реализуя) реалтайм и малую ресурсоёмкость.