Данный материал - это вольный перевод онлайн-книги webrtcforthecurious.com. Вся книга будет разбита на отдельные статьи, которые будут выходить в разное время. В основном я это сделал для себя в будущем, так как в свободное от работы время почти целиком и полностью занимаюсь проектом в основе которого именно WebRTC.
Книга довольно поверхностно объясняет как работает WebRTC "под капотом", для подробностей надо читать RFC. Ссылки на RFC различных используемых протоколов буду приводить. Стоит особо отметить главу "Отладка", где неплохо описываются идеи того, как отлаживать различные проблемы с сетью, задержками и прочим.
Итак, часть 1 - вводная.
Что такое Webrtc?
Web Real-Time Communication или сокращенно WebRTC, является и протоколом и API одновременно. WebRTC как протокол - это множество правил для безопасного обмена информацией (обычно медиа) в режиме дуплекс между двумя агентами в сети. WebRTC как API в свою очередь позволяет разработчикам работать с протоколом. API формально определено только для JavaScript.
Такое же разделение и в отношении HTTP и Fetch API: WebRTC-протокол - это как HTTP, а WebRTC API - это как Fetch API в данном случае.
Помимо JavaScript протокол WebRTC реализован также и на других языках програмирования. Можно найти множество реализаций серверов, библиотек, реализующих протокол, примером может стать реализация на go pion/webrtc. Пишется реализация и на rust: https://github.com/webrtc-rs/webrtc (проект довольно интересный, потому что это переписываение pion/webrtc на rust).
Протокол WebRTC поддерживается в IETF в группе rtcweb. API WebRTC задокументировано в W3C как webrtc-pc.
Приемущества WebRTC
Далее приводится список того, что даст вам WebRTC. Список не полный, это просто примеры того, что заслуживает внимания. Не волнуйтесь, если какие-то слова вам не знакомы, в течение следующих частей разберем их подробнее.
Итак, приемущества WebRTC:
-
Открытый стандарт
-
Множество различных реализаций
-
Можно работать прямо из браузера
-
Обязательное шифрование
-
NAT Traversal
-
Перепрофилированная существующая технология, то есть не изобретали колес, когда делали WebRTC
-
Контроль за перегруженностью
-
Задержка (latency, имеется в виду задержка аудио и/или видеопотока) в пределах 1 секунды
WebRTC это набор разных технологий
Это тема, для объяснения которой потребуется целая книга. Для начала разобъем ее на четыре части:
-
Сигналинг
-
Соединение пиров
-
Безопасность
-
Общение пиров
Эти четыре шага идут друг за другом, каждый предыдущий шаг должен успешно завершиться чтобы начался следующий.
Интересный факт в WebRTC это то, что каждый шаг использует множество других протоколов!
WebRTC объединияет множество существующих технологий. В этом смысле WebRTC - это скорее комбинация в различных конфигурациях хорошо изученных технологий, которые существуют с начала 2000-х годов.
Каждому из этих шагов посвящена отдельная часть, но пока что будет полезным рассмотреть каждый шаг "с высоты птичьего полета".
Сигналинг или как агенты находят друг друга в сети
Когда запускается WebRTC-агент, он не знает с кем ему соединиться, и какого рода информацией он будет обмениваться. Сигналинг (Signaling) решает эту проблему! Сигналинг нужен для того, чтобы два агента могли найти и вызвать друг друга в сети перед тем, как начать обмен информацией.
Сигналинг использует существующий протокол SDP (Session Description Protocol). SDP это простой текстовый протокол. Каждое SDP-сообщение состоит из пар ключ-значение, расположенных в строгом порядке (rfc4566), которые в свою очередь составляют набор медиа-секций. SDP-сообщения, которыми обмениваются WebRTC-агенты содержит такую информацию как:
-
адреса IP и порты агентов, по которым можно соединиться с агентом (это т.н. ICE-кандидаты)
-
сколько аудио и видео треков агент желает отправить
-
какие аудио и видео кодеки поддерживает каждый из агентов
-
значения используемые во время соединения (
uFrag
/uPwd
). -
значения используемые для безопасности (отпечаток сертификата)
Отметим, что сигналинг обычно работает как бы в сторонке; то есть приложения не используют WebRTC для обмена SDP сообщениями. Тут подходит любой способ обмена этими сообщениями: REST, Websocket, да хоть письмом по почте можно отправить другому пиру SDP-сообщение, а тот в свою очередь отправит свое. В своем приложении для тестов я вообще использовал firebase для сигналинга.
Установка соединения и NAT Traversal с помощью STUN/TURN
Теперь у обоих сторон WebRTC агентов достаточно информации о том, чтобы соединиться друг с другом. Далее используется другая устоявшаяся технология под названием ICE.
ICE (Interactive Connectivity Establishment) - это протокол предваряющий WebRTC. ICE позволяет установить связь между двумя агентами. Эти агенты могуть быть в одной и той же сети, а могут быть и на противоположных концах света. ICE решает проблему установления прямой связи без использования промежуточного сервера.
Настоящая магия здесь это т.н. NAT Traversal и STUN/TURN сервера. Обе эти концепции необходимы для соединения с ICE агентом из другой сетки. Далее мы изучим этот вопрос глубже.
Как только связь между двумя агентами установлена, WebRTC переходит к установлениею шифрованного канала передачи. Далее этот канал будет использован для передачи аудио/видео и данных.
Шифрование передачи информации с помощью DTLS и SRTP
После того как мы установили дуплексную связь между двумя пирами через ICE, нам необходимо установить шифрованное соединение для обеспечения безопасности при передаче данных. Это обеспечиватся двумя протоколами, которые также предваряют WebRTC: DTLS (Datagram Transport Layer Security), который просто добавляет слой TLS над UPD. (TLS - криптографический протокол используемый для безопасного обмена через https). Второй протокол - это SRTP (Secure Real-time Transport Protocol).
Сначала WebRTC выполняет DTLS-"рукопожатие" через соединение установленное ранее через ICE. В отличие от HTTPS WebRTC не использует CA для сертификатов. Вместо этого просто сверяет отпечатки сертификатов, полученных в ходе обмена SDP-сообщениями на этапе сигналинга. Установленное DTLS соединение далее используется для DataChannel, для обмена простыми данными - бинарными или текстовыми, например сообщения в чате.
Для видео/аудио в WebRTC используется другой протокол: RTP. Для шифрования RTP-пакетов используется протокол SRTP. SRTP сессия инициализируется с помощью ключей шифрования полученных в ходе DTLS сессии (rfc5764). Далее мы обсудим, почему для медиа-данных используется свой собственный протокол.
Теперь все готово! У нас есть двунаправленный и безопасный канал. Если у вас стабильное соединение между вашими WebRTC-агентами, то вышеописанный комплекс процедур достаточен чтобы начать им (агентам) общаться. Однако в жизни все не так идеально, как кажется: мы постоянно будем сталкиваться с потерей пакетов в сети, ограниченной пропускной способностью сети. Дальше мы подумаем, как справляться со всеми этими проблемами.
Общение между пирами через RTP и SCTP
Сейчас мы имеем два WebRTC-агента с безопасным двунаправленным соединением. Давайте начнем взаимодействие! И снова мы используем уже существующие протоколы: RTP (Real-time Transport Protocol), и SCTP (Stream Control Transmission Protocol). Используйте RTP для обмена аудио/видео шифрованным по протоколу SRTP и SCTP для обмена DataChannel-сообщениями, шифрованными с помощью DTLS.
RTP сам по себе очень минимален, но предоставляет все необходимое для стриминга в реальном времени. Важно то, что RTP предоставляет разработчику гибкость в управлении потерями пакетов, задержками и перегрузками так, как он (разработчик) пожелает. Далее мы будем обсуждать по этой теме в части про медиа.
Последний протокол в стеке протоколов WebRTC - это SCTP. SCTP дает множество возможностей для управления доставкой сообщений. Например, это может быть ненадежная неупорядоченная доставка сообщений, для того, чтобы получить задержку, удовлетворительную для систем реального времени.
WebRTC это набор протоколов
WebRTC решает множество проблем коммуникаций в реальном времени. На первый взгляд этот протокол может показаться овер-инжинирингом. Гениальность WebRTC - это так сказать скромность. При создании протокола не было цели решить все идеально. Вместо этого WebRTC взял множество существующих технологий, направленных на решение конкретной задачи, и объединил их вместе. На рисунке 1 ниже представлена диаграмма WebRTC агента. Как видно из схемы, WebRTC-агент на самом деле представляет собой оркестрацию различных протоколов.
Рис.1. WebRTC Agent DiagramКратко: как работает WebRTC (API)
В этой части показано как JavaScript API отображается на протокол. Это не демонстрация WebRTC API, а скорее некоторый набросок для создания у вас ментальной модели, как все работает вместе. Если вы не знакомы с каким-либо из пунктов, не переживайте, можете вернуться сюда когда узнаете больше!
new RTCPeerConnection
RTCPeerConnection
- это основа для установления
WebRTC-сессии. Объект RTCPeerConnection реализует "под капотом" все
протоколы, упомянутые выше. Здесь инициализируются все подсистемы,
и пока что ничего больше не происходит.
addTrack
Метод addTrack
создает новый RTP-поток. Для потока
генерируется случайный Synchronization Source (SSRC). Созданный RTP
поток будет затем описан в Session Description-сообщении внутри
медиа-секции после вызова createOffer
метода. Каждый
вызов addTrack
создает новый SSRC и добавляет
медиа-секцию в SDP-сообщение.
Сразу после того, как SRTP сессия установлена, зашифрованные медиа-пакеты начнут отправляеться через ICE.
createDataChannel
createDataChannel
создает новый SCTP-поток, если
еще не был добавлен. По умолчанию SCTP выключен, но
инициализируется как только одна из сторон потребует data
channel.
Сразу после того, как DTLS сессия установлена, SCTP пакеты начнут отправляться через ICE.
createOffer
createOffer
генерирует Session Description для
отправки удаленному пиру.
Вызов createOffer
ничего не меняет на локальном
пире.
setLocalDescription
setLocalDescription
фиксирует все, что менялось в
созданном RTCPeerConnection
для локального пира.
Методы addTrack
, createDataChannel
и
другие осуществляют временные изменения до тех пор, пока метод
setLocalDescription
не будет вызван. В этот метод
нужно передавать строку session description сгенерированную
методомcreateOffer
.
После вызова setLocalDescription сгенерированное SDP-сообщение также отправляется на удаленный пир (выше обусждалось, что это можно делать любым способом), и далее на удаленном пире SDP-сообщение (offer) передается в метод setRemoteDescription. Удаленный пир в свою очередь отправляет свой локальный SDP в ответ (answer), который также нужно передать локально в setRemoteDescription.
addIceCandidate
addIceCandidate
позволяет WebRTC-агенту добавить
больше удаленных ICE-кандидатов.
ontrack
ontrack
- это колбек (функция обратного вызова),
который срабатывает как только RTP-пакет был получен от удаленного
пира.
oniceconnectionstatechange
oniceconnectionstatechange
- это также колбек,
который отражает состояние ICE агента. Любые проблемы с сетью
отражаются через этот колбек.
onstatechange
onstatechange
- этот колбек служит для отслеживания
окончания сбора всех ICE-кандидатов. Как только аргумент этого
обратного вызова станет null, все ICE-кандидаты собраны.
В следующей части разберем Signaling и SDP.