В данной статье мы расскажем как разработать многопользовательскую WebRTC видео трансляцию из браузера через свой сервер ретранслятор. Трансляция будет идти из браузера Google Chrome и обычной USB вебкамеры. Для трансляции видео будет создана отдельная HTML — страница Streamer, а воспроизведением будет заниматься HTML-страница Player.

Схема трансляции


Browser1 — транслирующий. Подключается к серверу и транслирует видео с веб камеры.
Browser2, Browser3, Browser4 — зрители. Подключаются и получают видео для воспроизведения. Для ре-трансляции видеопотоков по WebRTC будет использоваться Web Call Server.



Принцип понятен и можно переходить к написанию кода. Создадим две HTML-страницы. Первая будет называться streamer.html и будет отправлять видеопоток на сервер. Вторая будет называться player.html и играть видеопоток с сервера.

Streamer — HTML


<html>
<head>
   <script language="javascript" src="flashphoner.js"></script>
</head>
<body onLoad="init()">
<h1>The streamer</h1>
<div id="localVideo" style="width:320px;height:240px;border: 1px solid"></div>
<input type="button" value="start" onClick="start()"/>
<p id="status"></p>
</body>
</html>

В заготовке html-страницы Streamer мы использовали следующие вещи:

1) Скрипт flashphoner.js

Этот скрипт является основным скриптом JavaScript API и его можно найти в последней сборке Web SDK.

<script language="javascript" src="flashphoner.js"></script>

2) Div элемент localVideo — это блок, в котором будет размещено видео, захваченное с вебкамеры.

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

3) Кнопка Start, по которой будет происходить запуск трансляции с вызовом функции start().

<input type="button" value="start" onClick="start()"/>

4) Обратите внимание, что в body добавлена обработка события onLoad c вызовом функции инициализации.

<body onLoad="init()">

5) Элемент с id = status, используется для отображения статусов.

<p id="status"></p>

Streamer — JavaScript


Переходим к написанию JavaScript — кода. Мы намеренно не используем JQuery, Bootstrap или какие-либо другие фреймворки чтобы оставить код минимальным:

var localVideo;

function init(){
   Flashphoner.init();
   localVideo = document.getElementById("localVideo");
}

function start() {
   Flashphoner.createSession({urlServer: "wss://wcs5-eu.flashphoner.com:8443"}).on(Flashphoner.constants.SESSION_STATUS.ESTABLISHED, function (session) {
       //session connected, start streaming
       startStreaming(session);
   }).on(Flashphoner.constants.SESSION_STATUS.DISCONNECTED, function () {
       setStatus("DISCONNECTED");
   }).on(Flashphoner.constants.SESSION_STATUS.FAILED, function () {
       setStatus("FAILED");
   });
}

function startStreaming(session) {
   session.createStream({
       name: "stream222",
       display: localVideo,
       cacheLocalResources: true,
       receiveVideo: false,
       receiveAudio: false
   }).on(Flashphoner.constants.STREAM_STATUS.PUBLISHING, function (publishStream) {
       setStatus(Flashphoner.constants.STREAM_STATUS.PUBLISHING);
   }).on(Flashphoner.constants.STREAM_STATUS.UNPUBLISHED, function () {
       setStatus(Flashphoner.constants.STREAM_STATUS.UNPUBLISHED);
   }).on(Flashphoner.constants.STREAM_STATUS.FAILED, function () {
       setStatus(Flashphoner.constants.STREAM_STATUS.FAILED);
   }).publish();
}

function setStatus(status) {
   document.getElementById("status").innerHTML = status;
}

В этом примере всего четыре функции:

1) init

Отвечает за инициализацию JavaScript API и берет ссылку на div — элемент localVideo.

2) start

Эта функция создает подключение к серверу по протоколу websockets. Адрес подключения: wss://domain:8443. На месте domain должен быть домен вашего WCS сервера (предполагаем, что сервер уже установлен, настроен и имеет выделенный домен, например webrtc.mycompany.com. Порт TCP 8443 должен быть открыт для входящих подключений).

Из кода этой функции понятно, следующим действием после получения статуса ESTABLISHED, будет вызов функции startStreaming и отправка потока на сервер.

3) startStreaming

Здесь мы создаем новый объект Stream, используя функцию API: session.createStream();. При создании стрима в качестве основных параметров передаются:

? streamName — имя видеопотока
? display — это элемент div, в котором будет отображаться захваченное для данного видеопотока видео с веб-камеры

Имя потока желательно генерировать уникальное, но мы для теста используем просто stream222.

4) setStatus

Данная функция отображает статус сессии (session) или потока (stream) на HTML-странице.

Выносим наш скрипт в отдельный файл streamer.js. Код страницы streamer.html будет выглядеть так:

<html>
<head>
   <script language="javascript" src="flashphoner.js"></script>
   <script language="javascript" src="streamer.js"></script>
</head>
<body onLoad="init()">
<h1>The streamer</h1>
<div id="localVideo" style="width:320px;height:240px;border: 1px solid"></div>
<input type="button" value="start" onClick="start()"/>
<p id="status"></p>
</body>
</html>

Streamer — тестирование


В итоге у нас получилось мини стриминг-приложение, состоящее из трех скриптов:

? streaming.html
? streaming.js
? flashphoner.js

Копируем наши скрипты на web-сервер чтобы начать тестирование. В качестве веб-сервера мы используем Apache 2 со стандартным /var/www/html

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



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



Таким образом, наш стример заработал и WebRTC видеопоток корректно отправляется на сервер, откуда осталось его забрать. Для того чтобы забрать и воспроизвести поток, создадим новую страницу player.html

Player — HTML


Страницу плеера копипастим из стримера, с той лишь разницей, что будем использовать скрипт: player.js вместо streamer.js, и вместо localVideo, зададим id = remoteVideo для нашего div — элемента.

<html>
<head>
   <script language="javascript" src="flashphoner.js"></script>
   <script language="javascript" src="player.js"></script>
</head>
<body onLoad="init()">
<h1>The player</h1>
<div id="remoteVideo" style="width:320px;height:240px;border: 1px solid"></div>
<input type="button" value="start" onClick="start()"/>
<p id="status"></p>
</body>
</html>

Player — JavaScript


В скрипте плеера также есть некоторые отличия, но подход остается прежним: инициализируется Flashphoner API, по кнопке Start открывается соединение с сервером через websocket, ждет события ESTABLISHED и начинает воспроизведение потока.

Список отличий скрипта player.js от скрипта streamer.js:

? Используется remoteVideo вместо localVideo
? После установки соединения вызываем startPlayback вместо startStreaming
? При создании стрима методом createStream(), передаются параметры receiveAudio=true и receiveVideo=true
? Вызывается метод play() вместо publish()

var remoteVideo;

function init(){
   Flashphoner.init();
   remoteVideo = document.getElementById("remoteVideo");
}

function start() {
   Flashphoner.createSession({urlServer: "wss://wcs5-eu.flashphoner.com:8443"}).on(Flashphoner.constants.SESSION_STATUS.ESTABLISHED, function (session) {
       //session connected, start streaming
       startPlayback(session);
   }).on(Flashphoner.constants.SESSION_STATUS.DISCONNECTED, function () {
       setStatus("DISCONNECTED");
   }).on(Flashphoner.constants.SESSION_STATUS.FAILED, function () {
       setStatus("FAILED");
   });
}

function startPlayback(session) {
   session.createStream({
       name: "stream222",
       display: remoteVideo,
       cacheLocalResources: true,
       receiveVideo: true,
       receiveAudio: true
   }).on(Flashphoner.constants.STREAM_STATUS.PLAYING, function (playStream) {
       setStatus(Flashphoner.constants.STREAM_STATUS.PLAYING);
   }).on(Flashphoner.constants.STREAM_STATUS.STOPPED, function () {
       setStatus(Flashphoner.constants.STREAM_STATUS.STOPPED);
   }).on(Flashphoner.constants.STREAM_STATUS.FAILED, function () {
       setStatus(Flashphoner.constants.STREAM_STATUS.FAILED);
   }).play();
}

function setStatus(status) {
   document.getElementById("status").innerHTML = status;
}

В результате получаем WebRTC-плеер, состоящий из двух скриптов:

? player.html
? player.js

Не забываем, что для работы плеера также необходим файл API flashphoner.js, поэтому копируем файлы плеера в ту же папку на web-сервере.

В итоге наше окончательное приложение для WebRTC онлайн трансляций состоит из пяти файлов:

? streamer.html
? streamer.js
? player.html
? player.js
? flashphoner.js

Приступим к тестированию и снова отправим видеопоток через streamer.html. После этого откроем новую вкладку браузера с player.html и нажмем Start.



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



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

3 минуты


Попробуем развернуть трансляцию за 3 минуты, учитывая следующее:

1) Уже установлен Web Call Server 5 для ретрансляции видеопотоков и настроен SSL. Без настройки SSL / HTTPS, трансляция в Google Chrome работать не будет, так как для доступа к веб камере и микрофону соединение должно быть безопасным и страница, на которой вы тестируете трансляцию должна быть открыта по HTTPS. Порт TCP 8443 открыт и готов принимать соединения по протоколу wss://

2) Уже установлен и сконфигурирован HTTP сервер, например Apache. Web-сервер настроен для работы через HTTPS и имеет FTP/SFTP/SSH доступ, через который можно залить / скачать тестовые скрипты.

WCS5 и Apache могут быть установлены на одной системе либо разнесены по разным серверам. Apache в данном случае просто отдает контент в виде HTML-страниц и скриптов, а WCS5 обслуживает подключения с видео.

Чтобы провести тестирование без установки, можно воспользоваться демо-сервером wss://wcs5-eu.flashphoner.com:8443 — он открыт для коннектов и тестирования.

Минута 1


Загружаем тестовые скрипты и распаковываем в папке /var/www/html на веб-сервере.

wget  --no-check-certificate https://flashphoner.com//downloads/examples/webrtc-broadcasting-example-0.5.15.1977.2088.tar.gz
tar -xzf webrtc-broadcasting-example-0.5.15.1977.2088.tar.gz

Минута 2


Открываем скрипты streamer.js и player.js на редактирование, находим и меняем:

wss://wcs5-eu.flashphoner.com:8443

на

wss://domain.com:8443

здесь domain.com — доменное имя вашего сервера, на котором установлен WCS5

Минута 3


Открываем streamer.html в браузере по https:// и нажимаем Start. Открываем в новой вкладке player.html и нажимаем Start.

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

Ссылки


1. Начальная установка и конфигурация сервера Web Call Server 5
2. Web SDK для WCS5
3. Скачать исходный код трансляции
4. Статистика WebRTC в chrome://webrtc-internals

Web Call Server 5 — это стриминг медиасервер с поддержкой WebRTC, RTMP и других протоколов для разработки web приложений и мобильных приложений с низкой задержкой видео. Сервер включает в себя Web SDK, Android SDK и iOS SDK для разработки кросс-платформенных видеочатов, онлайн видеотрансляций, трансляций с IP-камер и других приложений потокового видео для браузеров и мобильных устройств.
Поделиться с друзьями
-->

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


  1. Anonym
    03.03.2017 22:27

    За 3 минуты и $2000


    1. flashphoner
      04.03.2017 09:00
      -3

      Расчеты некорректны. Ежемесячная подписка стоит 75 usd. В месяце в среднем 720 часов, т.е. 43200 минут. Получается 0.17 центов в минуту. Тогда правильно писать так:

      За 3 минуты и 0.51 цента.

      или так

      За 3 минуты и 30 копеек.


      1. OtshelnikFm
        04.03.2017 10:17
        +1

        $95 в месяц
        при оплате за год

        или
        $145 в месяц
        при оплате за месяц


        Да, разочарование по ценам. Сказали бы в самом начале что — «откройте кошельки и если они не пустые — читайте дальше».


  1. devlev
    03.03.2017 23:22

    Вот я ещё в Гугл не залез но что то мне подсказывает что реализовать сервер ретранслятор можно и на node js?


    1. flashphoner
      04.03.2017 08:45
      -5

      Node.js — это Websockets. Т.е. на нем можно реализовать сигналинг с обменом SDP между браузерами. WebRTC он ретранслировать не умеет.


      1. VolCh
        04.03.2017 11:12

        В nodejs вы имеете доступ к хосту практически такой же как у Си программы. В частности возможность работы с сетевыми сокетами.


        1. nwalker
          05.03.2017 05:58
          -1

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


        1. flashphoner
          05.03.2017 16:29

          Ок. Node.js может работать с native кодом и биндить порты в частности. PHP или JSP тоже умеют работать с C-кодом, но это не означает автоматической поддержки WebRTC. Есть реализация Node.js+Websockets. Там можно сделать похожий сигналинг между браузерами, но это уже P2P — немного другое, там стрим будет кодироваться отдельно для каждого зрителя, и браузер-транслятор скоро повиснет.


        1. ice2heart
          06.03.2017 08:00

          Есть опенсурсная штука, называется Kurento-media-server позволяет получать и модифицировать webrtc как душе угодно, но ресурсоёмкая штукенция.


  1. MKhan98
    05.03.2017 20:32

    На сегодняшний день технология WebRTC сырая, как мокрые плавки. Снижение нагрузки на сервер на 98% за счет децентрализации — гениально, но сначала надо бы всем гигантам, начиная с браузеров, заканчивая интернет-провайдерами, собраться всем месте за столом и договориться дать добро этой технологии. Иначе время на подключение между пирами будет всегда долгим и не стабильным.


    1. flashphoner
      06.03.2017 12:09

      На сегодняшний день технология WebRTC сырая, как мокрые плавки.

      Лет 5 назад это было так. А сегодня технология уже достаточно зрелая и работает в продакшенах. При этом P2P технология не ограничивается, можно и VOD по WebRTC раздавать при желании. И кстати да, проблемы с подключением P2P — обратная сторона децентрализации. При подключении напрямую к серверу таких проблем не возникает. Подключение проходит быстро. Все потому, что не нужно долго сканировать сеть в поисках пригодных внешних IP для приема трафика.


      1. MKhan98
        06.03.2017 12:55

        Тогда чем передача потока от сервера к клиенту по WebRTC отличается от такой же передачи по Вебсокетам?
        Отличия я ищу сейчас не в простоте настройки (у меня есть время на это), а в нагрузке на сервера.


        1. flashphoner
          06.03.2017 21:34

          WebRTC по нагрузке в любом случае будет выше чем по Websockets. Дело в том, что там не просто льется видео, а работает много сервисов поверх потока, таких как RTCP feedback, ICE, шифрование. А основное отличие в том, что WebRTC идет по UDP с динамическим битрейтом и контролем сети и задержек, а Websockets идет по TCP и как получится. Т.е. если важно снизить нагрузку на сервер и некритична задержка, лучше использовать Websockets, если конечно получается его нормально отобразить на стороне браузера.