Опыт использования WebRTC. Лекция Яндекса

Что лучше использовать при разработке софта — нативные или веб-технологии? Холивар по этому поводу закончится ещё не скоро, но мало кто станет спорить, что нативные функции полезно продублировать для использования в браузерах или WebView. И если когда-то приложения для звонков существовали исключительно отдельно от браузера, то теперь их легко реализовать и в вебе. Разработчик Григорий Кузнецов объяснил, как пользоваться технологией WebRTC для P2P-соединений.


— Как вы все знаете, в последнее время появляется довольно много приложений, в основу которых заложен прямой обмен данными между двумя браузерами, то есть P2P. Это всевозможные мессенджеры, чаты, звонилки, видеоконференции. Также это могут быть приложения, которые производят какие-то распределенные вычисления. Пределы фантазии никак не ограничиваются.
Как же нам сделать подобную технологию? Представим, что мы хотим совершить звонок из одного браузера в другой. И пофантазируем, какие нам нужны шаги, чтобы этой цели достичь. В первую очередь кажется, что звонок — это наша картинка, наш голос, изображение, и нужно получить доступ к медиаустройствам, подключенным к компьютеру: к камере и к микрофону. После того, как вы получите доступ, вам необходимо, чтобы ваши два браузера, два клиента, друг друга нашли. Нужно помочь им как-то соединиться, достучаться, передать метаинформацию.

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

В первую очередь — старая умирающая технология Adobe Flash. Она действительно умирает, и компания Adobe прекратит ее поддержку уже в 2020 году. Технология действительно позволит вам получить доступ к вашим медиаустройствам, внутри нее вы сможете реализовать всю необходимую механику, чтобы помочь браузерам соединиться, чтобы они начали передавать информацию P2P, но вы опять изобретете свой велосипед, потому что нет единого стандарта, единого подхода к реализации данного способа передачи данных.

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

И есть технология WebRTC — с помощью нее работают Google Hangouts, Facebook Messenger. Компания Voximplant использует ее, чтобы вы могли совершать свои звонки. Давайте на ней остановимся подробнее. Это новая развивающаяся технология, она появилась в 2011 году и продолжает развиваться. Что же она позволяет делать? Получить доступ к камере и микрофону. Установить P2P-соединение между двумя компьютерами, двумя браузерами. Естественно, она позволяет передавать медиапотоки в режиме реального времени. Кроме того, она позволяет передавать информацию, то есть любую бинарную дату вы тоже можете передавать P2P, можете сделать свою систему распределенных вычислений.

Важный момент: WebRTC не предоставляет браузерам способ найти друг друга. Мы можем сформировать всю необходимую метаинформацию о нас любимых, но как одному браузеру узнать о существовании другого? Как их соединить? Рассмотрим пример.

8ldclukfmmhiyusud1ftwkmvmi0.png

Есть два клиента. Первый клиент желает совершить звонок второму клиенту. WebRTC дает всю необходимую информацию, чтобы себя обозначить. Но остается открытым вопрос, как одному браузеру найти другой, как эту метаинформацию переслать, как проинициализировать вызов. Это дается на откуп разработчикам, мы можем использовать абсолютно любой способ, взять эту метаинформацию, распечатать на бумажке, отправить курьером, другой ее будет использовать, и все будет работать.

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

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

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

Давайте чуть подробнее рассмотрим, чем же именно обмениваются клиенты, они обмениваются SDP-датаграммой, Session Description Protocol.

zle-oknfv3cz36cwgdbt00ldc4q.png

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

Обратите внимание на вторую строчку. Там указан IP-адрес клиента, 192.168.0.15. Очевидно, это IP-адрес компьютера, который находится в какой-то локальной сети. Если у нас два компьютера, каждый из которых находится в локальной сети, каждый из которых знает свой IP-адрес в рамках этой сети, хотят созвониться. Получится ли у них сделать это с такой датаграммой? Очевидно, нет, они же не знают внешних IP-адресов. Как быть?

6jolxumekywnyr1txfh6vdhjk-a.png

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

0cw3vj67zemxzrbjwe5cr0bfzvc.png

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

Как жить, если есть NAT, если компьютеры торчат наружу под одним IP-адресом, а внутри знают о друг друге по другим?

На помощь приходит фреймфорк ICE — Internet Connectivity Establishment. Он описывает способы обхода NAT, способы установки соединения, если у нас есть NAT.

Этот фреймворк использует приписывание так называемого STUN-сервера.

59zvamcoxdislwvhlj3qkn2k_cq.png

Это такой специальный сервер, обращаясь к которому, вы можете узнать свой внешний IP-адрес. Таким образом, в процессе установки P2P соединения, каждый из клиентов должен сделать по запросу к этому STUN-серверу, чтобы узнать свой IP-адрес, и сформировать дополнительную информацию, IceCandidate, и с помощью сигнального механизма также этим IceCandidate обменяться. Тогда клиенты будут знать друг о друге с правильными IP-адресами, и смогут установить P2P соединение.

Однако бывают более сложные случаи. Например, когда компьютер скрыт за двойным NAT. В этом случае фреймворк ICE предписывает использование TURN-сервера.

yd0rj3j5touybbu9ji2yxajvu0w.png

Это такой специальный сервер, который превращает соединение клиент-клиент, P2P, в соединение клиент-сервер-клиент, то есть выступает в роли ретранслятора. Хорошая новость для разработчиков в том, что независимо от того, по какому из трех сценариев пошла установка соединения, находимся ли мы в локальной сети, нужно ли обратиться к STUN- или TURN-серверу, АPI-технология для нас будет идентичной. Мы просто в начале указываем конфигурацию ICE- и TURN-серверов, указываем, как к ним обратиться, и после этого технология делает для нас все под капотом.

u5gmd3hsoofg6zt_-gnop6nx0zs.png

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

Нам предстоит побороться с NAT, это наш главный враг на этом этапе. Но чтобы его обойти, используем STUN-сервер, чтобы узнать свой внешний IP-адрес, и TURN-сервер используем в качестве ретранслятора.

Что же именно мы передаем? Про медиа-потоки.

qd5nsho7njp_zbptyvvgxzgyeyk.png

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

Как же получить доступ к медиа из браузера? Поговорим про API.

xdlilwqq1hvfwsmpzbo2abyu22i.png

Есть метод getUserMedia, который принимает на вход набор констрейнтов. Это специальный объект, где вы указываете, к каким именно устройствам вы хотите получить доступ, к какой именно камере, к какому микрофону. Указываете характеристики, которые хотите иметь, какое именно разрешение, и есть также два аргумента — successCallback и errorCallback, который вызывается в случае успеха или неудачи. В более современных реализациях технологии используются промисы.

Есть также удобный метод enumerateDevices, который возвращает список всех подключенных к вашему компьютеру медиа-устройств, что дает вам возможность показать их пользователю, нарисовать какой-то селектор, чтобы пользователь выбрал, какую конкретно камеру он хочет использовать.

gavpuolkqqbou4mgkr5nkgyn_ga.png

Центральным объектом в API служит RTCPeerConnection. Когда мы выполняем соединение, то берем класс RTCPeerConnection, который возвращает объект peerConnection. В качестве конфигурации мы указываем набор ICE-серверов, то есть STUN- и TURN-серверов, к которым мы будем обращаться в процессе установки. И есть важный ивент onicecandidate, который триггерится каждый раз, когда нам нужна помощь нашего сигнального механизма. То есть технология WebRTC сделала запрос, например, к STUN-серверу, мы узнали свой внешний IP-адрес, появился новый сформированный ICECandidate, и нам нужно переслать его с помощью стороннего механизма, ивент стриггерился.

ouluzpi6ahoovtpo1acf01pblbe.png

Когда мы устанавливаем соединение и хотим проинициализировать вызов, мы используем метод createOffer (), чтобы сформировать начальную SDP, offer SDP, ту самую мета-информацию, которую нужно переслать партнеру.

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

-zttnspmlmdyfjgvn6koame5duw.png

Когда мы получили доступ к медиа, получили медиапоток, мы его передаем в наше P2P-соединение с помощью метода addStream, а наш собеседник узнает об этом, у него стриггерится ивент onaddstream. Он получит наш поток и сможет его отобразить.

e54mot_c6nmcg60na7bj-k7kbro.png

Также вы можете работать с дата-потоками. Очень похоже на формирование обычного peerConnection, просто указываете RtpDataChannels: true и вызываете метод createDataChannel (). Подробно на этом останавливаться не буду, потому что такая работа очень похожа на работу с веб-сокетами.

Пару слов о безопасности. WebRTC работает только по HTTPS, ваш сайт должен быть подписан сертификатом. Медиапотоки тоже шифруются, используется DTLS. Технология не требует установки ничего дополнительного, никаких плагинов, и это хорошо. И не получится сделать шпионское приложение, сайт не будет подслушивать или подсматривать за пользователем, он покажет пользователю специальный промт, запросит у него доступ и получит его, только если пользователь разрешит доступ к аудио- и медиаустройствам.

4dpfmps0b2axjq2kyrshvgf-xaa.png

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

Хочу поделиться набором всяких полезностей, которые помогут вам, если вы желаете работать с WebRTC. В первую очередь это adapter. Технологии все время развиваются, и есть разница в браузерных API. Библиотека adapter нивелирует эту разницу и облегчает работу. Удобная библиотека для работы с дата-потоками — Peerjs. Также можете посмотреть на открытые реализации STUN- и TURN-сервера. Большой набор туториалов, примеров, статей находится на страничке awesome-webrtc, очень рекомендую.

Последняя незаменимая при дебаге полезность — webrtc-internals. Во время разработки вы можете в адресной строке набрать специальную команду — например в браузере Chrome это Chrome://webrtc-internals. У вас откроется страница со всей информацией о вашем текущем WebRTC-соединении. Там будут и последовательности вызовов в методах, и все датаграммы, которыми обмениваются браузеры, и графики, которые каким-то характеризуют образом ваше соединение. В общем, там будет вся информация, которая понадобится при дебаге и разработке. Спасибо за внимание.

© Habrahabr.ru