Предыдущая часть здесь.
Что такое сигналинг в контексте WebRTC?
Технология WebRTC как известно, основана на передачи данных - аудио, видео, текста, файлов - от одного конечного узла одноранговой сети другому. Существует термин, описывающий такую схему: p2p (peer to peer). Когда создается WebRTC-агент на одном из узлов сети, он еще ничего не знает об окружающих его других узлах. Он не знает кто хочет подключиться к нему, и какие данные будут передавать ему. Сигналинг - это самый первый шаг в установлении соединения между двумя узлами (далее буду для точности называть пирами). В ходе сингналинга пиры обмениваются между собой различными опциями и значениями, после чего между пирами устанавливается прямая связь без посредников.
Сообщения, которыми обмениваются узлы во время сигналинга, представляет собой простой текст. Обмен сообщениями в ходе сигналинга никак не определяется WebRTC: транспортом может быть протокол HTTP(S) или например, WebSockets.
Сообщения сигналинга представляют собой текст в формате SDP - Session Description Protocol. Сообщение SDP состоит из трех секций, каждая из которых - это набор строк, которые представляет собой ключ-значение. Ключи идут в определенном порядке, это сделано намеренно, чтобы упростить парсинг sdp-сообщения. Определены три секции SDP: описание сессии, тайминги и описание медиа-данных, которыми будут обмениваться пиры. Описание сессии:
v= (версия протокола, всегда 0)
o= (идентификатор источника и сессии, формат такой: имя пользователя, id, версия, IP-адрес)
s= (имя сессии : не менее одного UTF-8 символа. Обычно сюда прописывается `-`)
i=* (краткая информация о сессии)
u=* (URI или описание)
e=* (email адрес)
p=* (телефон)
c=* (другая необязательная информация для соединения)
b=* (ширина канала)
Одно или более описание таймаингов (строки "t=" и "r="; см. ниже)
z=* (сдвиг временной зоны)
k=* (ключ шифрования)
a=* (ноль или более атрибутов сессии)
Ноль или более строк, описывающих медиа-секции (каждая медиа-секция начинается с "m="; см. ниже)
Звездочками обозначены ключи, которые вовсе не обязательны, и обычно при установлении связи webrtc отсутствуют. Описание таймингов:
t= (время когда активен сеанс, должен быть равен 0 0)
Описание медиа-секции:
m= (формат такой: <тип медиа - audio или video&> <порт> <протокол> <тип(ы) payload>)
a=* (необязательный атрибут, которых может быть несколько для каждой из медиа-секций)
В атрибутах медиа-секций описываются маппинг номера формата на поддерживаемые webrtc-пиром видео-аудио кодеки. Например в секции
m=audio 4000 RTP/AVP 111
a=rtpmap:111 OPUS/48000/2
описывается, что пир готов обмениваться аудио данными на порту 4000 используя RTP/AVP payload с типом 111. Атрибут указывает на то, что RTP/AVP данные типа 111 будут кодироваться кодеком OPUS с битрейтом 48000.
Полное и подробное описание SDP можно найти в RFC8866.
Рассмотрим более полный пример SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=-
c=IN IP4 127.0.0.1
t=0 0
m=audio 4000 RTP/AVP 111
a=rtpmap:111 OPUS/48000/2
m=video 4002 RTP/AVP 96
a=rtpmap:96 VP8/90000
Ключи v, o, s, c, t здесь определены, но они не влияют на сеанс WebRTC.
Имеем две медиа-секции ("m="), одна описывает аудио-данные, другая - видео.
Каждая из секций имеет атрибуты, которые описывают отображение типа полезной нагрузки на ее особенности, поддерживаемые пиром, в данном случае описывается, что аудио может кодироваться с помощью opus, видео кодируется в vp8.
Работа SDP и WebRTC вместе
Разберемся далее как работают две технологии вместе. WebRTC для сигналинга использует модель офер-ответ: один webrtc-агент отправляет другому офер для начала соединения, второй агент посылает ответ первому. В процессе формирования ответа, второй агент формирует список поддерживаемых кодеков, а также отклоняет неподдерживаемые. Таким образом оба пира приходят к общему формату обмена медиа-данными.
В WebRTC существует такое понятие как трансиверы - пара интерфейсов RTCRtpSender и RTCRtpReceiver, имеющих некое общее состояние и служащих для описания каким образом будут передаваться медиа. Во многих приложениях (таких как видео-стриминг, конференции, трансляции) всегда присутствуют трансиверы, описываемые в медиа-секциях SDP. Каждая медиа-секция в WebRTC будет иметь атрибут, описывающий направление передачи. Это позволяет WebRTC агенту указать, что "я буду отправлять тебе данные закодированные таким-то кодеком, но обратно принимать ничего не собираюсь". Существует четыре значения направления:
send - означает, что поток будет только отправляться
recv - только для приема
sendrecv - двунаправленный поток
inactive - данный поток отключен
Бонус-подсказка. Что делать, если мы хотим например, организовать приложение по типу - стример - зритель. У зрителя нет камеры или микрофона, да и не нужно, однако если у зрителя создадим PeerConnection и не добавим никакой медиа-трек, то не произойдет события onnegotiationneeded:
let pc = new RTCPeerConnection();
pc.onnegotiationneeded = function() { // Это не сработает, пока не добавится трек
pc.createOffer().then(function(offer) { // создаем sdp-офер
sendOfferToRemotePeer(offer); // отправляем офер другому пиру через http-сервер например
return pc.setLocalDescription(offer); // после этого запускаются ICE-кандидаты
});
};
И соединение не состоится. В таком случае можно либо добавить datachannel в peerconnection, либо добавить трансивер, который будет выступать только приемником:
pc.addTransceiver('video', {direction: "recvonly"});
Другие значения атрибутов медиа-секций
Далее опишу самые распространенные значения атрибутов, использующиеся в WebRTC медиа-секциях, которые вы встретите в SDP.
group:BUNDLE
- бандлинг позволяет использовать одну коннекцию для передачи нескольких видов медиа-трафика. Некоторые простые или следующие устаревшей спецификации WebRTC используют отдельную коннекцию на каждый медиа-поток. Очевидно, что бандлинг должен быть предпочтительным, чем без него (поддержку бандлинга должен реализовывать WebRTC агент, во всех современных браузерах он поддерживается)fingerprint:sha-256
- это SHA-256 хеш сертификата пира, используемого при установке DTLS соединения. О безопасности и DTLS в частности я буду писать в следующих статьях. Это поле используется для проверки сертификата, полученного после рукопожатия с пиром, с которым хотим установить WebRTC-сеанс, чтобы убедиться, что сертификат не подменили в ходе установки DTLS соединения (напомню, что обмен SDP сообщениями происходит до начала установки прямого DTLS-соединения).-
setup:
- поле для контроля DTLS-агента. Это поле определяет, как запускать DTLS-агента - как сервер или как клиент после ICE-соединения. Возможные значения:setup:active - как клиент
setup:passive - как сервер
setup:actpass - спросить другого WebRTC агента
ice-ufrag
- имя пользователя для аутентификации ICE-трафика. При реализации своего webrtc-сервера (называем его агентом), это просто случайная строка определенной длины.ice-pwd
- пароль для аутентификации ICE-трафика. Точно также генерируется из случайных букв.rtpmap
- используется для мапинга кодека на RTP payload тип.fmtp
- дополнительные значения для одного типа payload. Используется для указания настроек кодирования или видеопрофиля.candidate
- здесь указываются ICE-кандидаты. В самом простейшем случае (ICE light) здесь указываются транспортные адреса локальных интерфейсов ICE-агента. Проще говоря, при запуске Webrtc сессии на вашем компьютере собираются все сетевые интерфейсы, кроме loop и тех, которые отключены (DOWN). Далее биндятся на найденые IP-адреса этих интерфейсов слушатели (поднимаются маленькие сервера), система назначает случайные порты. Вот эти вот все "маленькие сервера" и есть ICE-кандидаты, то есть это интерфейсы, через которые потенциально можно соединиться с вашим компьютером (или телефоном) напрямую. Локальные ICE-кандидаты имеют наивысший приоритет. Далее идут по приоритету ICE-кандидаты - STUN и TURN (когда совсем все плохо с NAT). В данное поле атрибута SDP сообщения записываются найденые ICE-кандидаты, по одному на строчку для каждого медиа-потока.ssrc
- Synchronization Source определяет медиа трекlabel
- идентификатор отдельного потока. mslabel - это идентификатор контейнера, который может содержать несколько потоков.
Полный пример SDP-сообщения
v=0
o=- 3546004397921447048 1596742744 IN IP4 0.0.0.0
s=-
t=0 0
a=fingerprint:sha-256 0F:74:31:25:CB:A2:13:EC:28:6F:6D:2C:61:FF:5D:C2:BC:B9:DB:3D:98:14:8D:1A:BB:EA:33:0C:A4:60:A8:8E
a=group:BUNDLE 0 1
m=audio 9 UDP/TLS/RTP/SAVPF 111
c=IN IP4 0.0.0.0
a=setup:active
a=mid:0
a=ice-ufrag:CsxzEWmoKpJyscFj
a=ice-pwd:mktpbhgREmjEwUFSIJyPINPUhgDqJlSd
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:350842737 cname:yvKPspsHcYcwGFTw
a=ssrc:350842737 msid:yvKPspsHcYcwGFTw DfQnKjQQuwceLFdV
a=ssrc:350842737 mslabel:yvKPspsHcYcwGFTw
a=ssrc:350842737 label:DfQnKjQQuwceLFdV
a=msid:yvKPspsHcYcwGFTw DfQnKjQQuwceLFdV
a=sendrecv
a=candidate:foundation 1 udp 2130706431 192.168.1.1 53165 typ host generation 0
a=candidate:foundation 2 udp 2130706431 192.168.1.1 53165 typ host generation 0
a=candidate:foundation 1 udp 1694498815 1.2.3.4 57336 typ srflx raddr 0.0.0.0 rport 57336 generation 0
a=candidate:foundation 2 udp 1694498815 1.2.3.4 57336 typ srflx raddr 0.0.0.0 rport 57336 generation 0
a=end-of-candidates
m=video 9 UDP/TLS/RTP/SAVPF 96
c=IN IP4 0.0.0.0
a=setup:active
a=mid:1
a=ice-ufrag:CsxzEWmoKpJyscFj
a=ice-pwd:mktpbhgREmjEwUFSIJyPINPUhgDqJlSd
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=ssrc:2180035812 cname:XHbOTNRFnLtesHwJ
a=ssrc:2180035812 msid:XHbOTNRFnLtesHwJ JgtwEhBWNEiOnhuW
a=ssrc:2180035812 mslabel:XHbOTNRFnLtesHwJ
a=ssrc:2180035812 label:JgtwEhBWNEiOnhuW
a=msid:XHbOTNRFnLtesHwJ JgtwEhBWNEiOnhuW
a=sendrecv
Вот что мы можем узнать из него:
Имеем две медиа-секции (m=): одна для аудио, другая для видео
Каждая является sendrecv трансивером, то есть агент может как принимать так и отправлять аудио-видео-данные.
Имеем ICE-кандидатов (a=candidate), и параметры аутентификации (a=ice-ufrag, a=ice-pwd), при этом указан атрибут a=group:BUNDLE, что означает, что потоки будут передаваться через одну коннекцию
Имеем отпечаток сертификата для проверки хендшейка DTLS соединения
В общем-то это основное, что нужно знать о сигналинге в WebRTC. В следующих версиях книги https://webrtcforthecurious.com/ обещают описать повторное соединение (renegotiation) и simulcast (передача нескольких потоков разного качества, битрейта для адаптирования к сложным условиям связи данного пира).