WebRTC предоставляет набор стандартных API-интерфейсов, которые позволяют веб-приложениям напрямую предоставлять функции аудио- и видеосвязи в реальном времени. Большинство браузеров и операционных систем поддерживают WebRTC, и вы можете напрямую инициировать аудио- и видеозвонки в реальном времени на стороне браузера.В этой статье используется точка зрения новичков в WebRTC для создания веб-версии аудио- и видеозвонков 1V1 в реальном времени.
Для совершения аудио- и видеовызова вам необходимо понимать четыре модуля: сбор аудио- и видеоданных, сервер STUN/TURN, сервер сигнализации и P2P-соединение между терминалами. Используйте API WebRTC для сбора аудио и видео, взаимодействуйте с сигнальным сервером и WebRTCRTCPeerConnection
Метод может реализовать вызов 1V1, простой процесс выглядит следующим образом:
Далее мы по очереди объясним их функции и основные API.
Аудио и видео коллекция
Использование WebRTCgetUserMedia
Получите объект медиапотока, соответствующий камере и микрофону.MediaStream
, медиапотоки могут передаваться по WebRTC и совместно использоваться несколькими одноранговыми узлами. Назначьте объект потока srcObject элемента видео для локального воспроизведения аудио и видео.
Атрибуты | значение |
---|---|
width | ширина видео |
height | высота видео |
aspectRatio | Доля |
frameRate | частота кадров |
facingMode | зеркальный режим |
resizeMode | режим размера |
API:navigator.mediaDevices.getUserMedia
参数:constraints
返回:promise,方法调用成功得到MediaStream对象。
const localVideo = document.querySelector("video");
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
navigator.mediaDevices
.getUserMedia({
video: {
width: 640,
height: 480,
frameRate:15,
facingMode: 'enviroment', // 设置为后置摄像头
deviceId : deviceId ? {exact:deviceId} : undefined
},
audio: false
})
.then(gotLocalMediaStream)
.catch((error) => console.log("navigator.getUserMedia error: ", error));
управление соединением
Знайте, как записывать локальные аудио и видео, а затем научитесь устанавливать соединение с другим концом для передачи аудио и видео данных.
RTCPeerConnection
Это унифицированный интерфейс для WebRTC для реализации сетевого подключения, управления мультимедиа и управления данными. Чтобы установить P2P-соединение, вам нужно использоватьRTCPeerConnection
Несколько важных классов в:SDP
,ICE
,STUN/TURN
.
-
Информация описания сеанса RTCSessionDescription (SDP)
SDP — это возможности каждого конца, включая тип аудиокодека, протокол передачи и т. д. Эта информация должна быть передана при установлении соединения.Обе стороны знают, поддерживает ли видео звук и какой метод кодирования, и их можно получить через SDP.
Например, для передачи видео я использую кодировку H264. Другая сторона может декодировать только H265, поэтому они не могут общаться.
Описание SDP разделено на две части: описание уровня сеанса (уровень сеанса) и описание уровня мультимедиа (уровень мультимедиа), на конкретный состав которых можно ссылаться.RFC4566, те, что отмечены звездочкой (*), являются необязательными. Общие из них следующие:
Session description(会话级别描述)
v= (protocol version)
o= (originator and session identifier)
s= (session name)
c=* (connection information -- not required if included in all media) One or more Time descriptions ("t=" and "r=" lines; see below)
a=* (zero or more session attribute lines) Zero or more Media descriptions
Time description
t= (time the session is active)
Media description(媒体级别描述), if present
m= (media name and transport address)
c=* (connection information -- optional if included at session level)
a=* (zero or more media attribute lines)
При анализе SDP каждая строка SDP начинается сkey=...
форма, после анализа того, что ключ является, может быть два способа, пожалуйста, обратитесь кRFC4566:
a=<attribute>
a=<attribute>:<value>
Иногда это не должно быть двоеточие (:)<attribute>:<value>
, на самом деле в значении будут и двоеточия, например:
a=fingerprint:sha-256 7C:93:85:40:01:07:91:BE
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=ssrc:2527104241 msid:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS
Взгляните на конкретные примеры:
alert(pc.remoteDescription.sdp);
v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
//下面的媒体描述,在媒体描述部分包括音频和视频两路媒体
m=audio 49170 RTP/AVP 0
a=fmtp:111 minptime=10;useinbandfec=1 //对格式参数的描述
a=rtpmap:0 PCMU/8000 //对RTP数据的描述
...
//上面是音频媒体描述,下面是视频媒体描述
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
...
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
-
Кандидат ICE RTCIceCandidate
Наиболее удобным методом двухточечного соединения WebRTC является прямое IP-соединение между двумя сторонами, но в практических приложениях две стороны будут разделеныNAT
Устройство вызывает проблемы с получением адреса.
WebRTC проходитICE
Платформа определяет наилучший путь для установления сетевых соединений на обоих концах, ограждая разработчиков от сложных технических деталей.
(НАТ иICE
Фреймворк — это черный ящик для разработчиков, использующих WebRTC, для оптимизации чтения эта часть размещена в конце как дополнительные знания)
Разработчики должны знать:
- принцип
Два узла обмениваются кандидатами ICE, чтобы договориться о том, как они сами хотят подключиться.После того, как обе стороны согласуют взаимно совместимый кандидат, SDP-кандидат используется для создания и открытия соединения, через которое начинается потоковая передача мультимедиа.
- два API
onicecandidate
: Запускается после того, как локальный агент создает предложение SDP и вызывает setLocalDescription(offer) и передает информацию о кандидате на удаленный конец через сервер сигнализации в обработчике событий.
addIceCandidate
: Вызывается после получения информации о кандидате, отправленной сервером сигнализации, для добавления агента ICE к машине.
API:pc.onicecandidate = eventHandler
pc.onicecandidate = function(event) {
if (event.candidate) {
// Send the candidate to the remote peer
} else {
// All ICE candidates have been sent
}
}
API:pc.addIceCandidate
pc.addIceCandidate(candidate).then(_=>{
// Do stuff when the candidate is successfully passed to the ICE agent
}).catch(e=>{
console.log("Error: Failure during addIceCandidate()");
});
сигнальный сервер
На информацию WebRTC SDP и ICE нужно полагаться信令服务器
Осуществлять передачу и обмен сообщениями, устанавливать P2P-соединение, а затем проводить аудио- и видеозвонки и передавать текстовую информацию. WebRTC не может общаться без сигнального сервера.
обычно используетсяsocket.io
Возможности связи в реальном времени для создания серверов сигнализации.socket.io
Кросс-платформенный, кросс-терминальный и кросс-языковой, нам удобно реализовывать каждый конец сигнализации на каждом конце и подключаться к нашему серверу.
Эта картинка выражает роль сервера сигнализации во всем процессе вызова.
Посмотрите, как создать сигнальный сервер socket.io с помощью кода.
var express = require("express");
var app = express();
var http = require("http");
const { Server } = require("socket.io");
const httpServer = http.createServer(app);
const io = new Server(httpServer);
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("message", (room, data) => {
logger.debug("message, room: " + room + ", data, type:" + data.type);
socket.to(room).emit("message", room, data);
})
socket.on("join", (room) => {
socket.join(room);
})
});
Сквозные P2P-соединения
-
процесс подключения
Процесс установления сетевого соединения между A и B выглядит следующим образом:
- A инициирует вызов WebRTC к B
- Создайте объект peerConnection и укажите адрес Turn/Stun в параметре
var pcConfig = {
iceServers: [
{
urls: "turn:stun.al.learningrtc.cn:3478",
credential: "mypasswd",
username: "garrylea",
},
{
urls:[
"stun:stun.example.com",
"stun:stun-1.example.com"
]
}
],
};
pc = new RTCPeerConnection(pcConfig);
- Вызов
createOffer
Метод создает локальное описание сеанса (предложение SDP), предложение SDP содержит всю информацию о кодеках и параметрах, которые были прикреплены к сеансу WebRTC, поддерживается браузером.MediaStreamTrack
информация, иICE
Прокси-сервер, предназначенный для отправки по сигнальному каналу потенциальной удаленной конечной точке для запроса соединения или обновления конфигурации существующего соединения. - Вызов
setLocalDescription
метод устанавливает предложение в описание локального сеанса и передает его на уровень ICE. Затем отправьте описание сеанса на B через сигнальный сервер.
API:pc.createOffer
参数:无
返回:SDP Offer
API:pc. setLocalDescription
参数:offer
返回:Promise<null>
function sendMessage(roomid, data) {
if (!socket) {
console.log("socket is null");
}
socket.emit("message", roomid, data);
}
const offer = await pc.createOffer()
await pc.setLocalDescription(offer).catch(handleOfferError);
message.log(`传输发起方本地SDP`);
sendMessage(roomid, offer);
- После создания pc.setLocalDescription(offer) на стороне A событие icecandidate отправляется на
RTCPeerConnection
,onicecandidate
Событие будет запущено. Сторона B получает новую информацию об адресе кандидата ICE, отправленную сигнализацией с удаленной страницы, локальная машина может вызватьRTCPeerConnection.addIceCandidate()
чтобы добавить агента ICE.
//A端
pc.onicecandidate = (event) => {
if (!event.candidate) return;
sendMessage(roomid, {
type: "candidate",
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
});
};
//B端
socket.onmessage = e => {
if (e.data.hasOwnProperty("type") && e.data.type === "candidate") {
var candidate = new RTCIceCandidate({
sdpMLineIndex: data.label,
candidate: data.candidate,
});
pc.addIceCandidate(candidate)
.then(() => {
console.log("Successed to add ice candidate");
})
.catch((err) => {
console.error(err);
});
}
}
- Как вызывающий абонент A получает локальный медиапоток и вызывает
addtrack
Способ присоединиться к аудио и видео потокуRTCPeerConnection
Объект передается на другой конец, и другой конец срабатывает, когда он присоединяетсяontrack
событие.
媒体流加入媒体轨道
API:stream.getTracks
参数:无
返回:媒体轨道对象数组
const pc = new RTCPeerConnection();
stream.getTracks().forEach((track) => {
pc.addTrack(track, stream);
});
const remoteVideo = document.querySelector("#remote-video");
pc.ontrack = (e) => {
if (e && e.streams) {
message.log("收到对方音频/视频流数据...");
remoteVideo.srcObject = e.streams[0];
}
};
- Как вызывающая сторона B получает информацию о сеансе, отправленную A с сервера сигнализации, и вызывает
setRemoteDescription
Метод передает предложение на уровень ICE и вызывает метод addTrack для присоединения к RTCPeerConnction. - B вызывает метод createAnswer для создания ответа, вызывая
setLocalDeacription
Ответ метода устанавливается для локального сеанса и передается на уровень ICE.
socket.onmessage = e => {
message.log("接收到发送方SDP");
await pc.setRemoteDescription(new RTCSessionDescription(e.data));
message.log("创建接收方(应答)SDP");
const answer = await pc.createAnswer();
message.log(`传输接收方(应答)SDP`);
sendMessage(roomid, answer);
await pc.setLocalDescription(answer);
}
- AB имеет свой собственный SDP и SDP другой стороны и достиг соглашения об обмене медиа.Собранный ICE завершает обнаружение подключения и устанавливает наиболее метод подключения, а также устанавливается P2P-соединение для получения аудио- и видеопотока мультимедиа другой стороны. .
pc.ontrack = (e) => {
if (e && e.streams) {
message.log("收到对方音频/视频流数据...");
remoteVideo.srcObject = e.streams[0];
}
};
-
Двунаправленное подключение к каналу данных
RTCDataChannelton может устанавливать двухточечное соединение P2P через API RTCPeerConnection без необходимости использования промежуточного сервера и с меньшей задержкой.
Один конец устанавливает канал данных, а другой конец получает объект канала данных через ondatachannel.
API:pc.createDataChannel
参数: label 通道名
options? 通道参数
返回:RTCDataChannel
function receivemsg(e) {
var msg = e.data;
if (msg) {
message.log("-> " + msg + "\r\n");
} else {
console.error("received msg is null");
}
}
const dc = pc.createDataChannel("chat");
dc.onmessage = receivemsg;
dc.onopen = function () {
console.log("datachannel open");
};
dc.onclose = function () {
console.log("datachannel close");
};
pc.ondatachannel = e => {
if(!dc){
dc = e.channel;
dc.onmessage = receivemsg;
dc.onopen = dataChannelStateChange;
dc.opclose = dataChannelStateChange;
}
}; //当对接创建数据通道时会回调该方法。
Фреймворк NAT и ICE
упомянутый вышеICE
Он объединяет различные технологии обхода NAT, такие как STUN и TURN, которые могут реализоватьNAT
Проникновение, обнаружение механизма пути передачи P2P между хостами. Далее кратко расскажем, что такое NAT, STUN и TURN.
-
Преобразование сетевых адресов (NAT)
NAT
Часто развертывается на выходе из сети организации. Сеть разделена на две части: частная сеть и публичная сеть.Шлюз NAT устанавливается на выходе маршрута из частной сети в публичную сеть.Двунаправленные данные между частной сетью и публичной сетью должны проходить через NAT-шлюз . Большое количество устройств внутри организации может совместно использовать общедоступный IP-адрес через NAT, что решает проблему нехватки IPv4-адресов.
Как показано на рисунке ниже, есть две организации, и NAT каждой организации назначен общедоступный IP-адрес 1.2.3.4 и 1.2.3.5. Частное сетевое устройство каждой организации преобразует внутренний сетевой адрес в общедоступный сетевой адрес через NAT, а затем подключается к Интернету.
Существует четыре реализации NAT для UDP, а именно: полный конус, конус с ограниченным адресом, конус с ограниченным портом и симметричный.
-
Утилиты обхода сеанса для NAT (STUN)
STUN
Позволяет клиентам за NAT (или несколькими NAT) узнать свой общедоступный сетевой адрес, тип NAT, за которым они находятся, и общедоступный сетевой порт, который NAT связывает с локальным портом.
STUN
Это протокол в режиме C/S.Клиент отправляет запрос STUN, а ответ службы STUN информируетNAT
IP-адрес и номер порта, назначенные хосту, также являются протоколом запроса/ответа Номер порта по умолчанию — 3478.
Чтобы хост внутренней сети знал свой внешний сетевой IP-адрес, вам необходимо настроить STUN-сервер в общедоступной сети, отправить запрос на этот сервер, и сервер вернет свой общедоступный сетевой IP-адрес.
Ниже приведена захваченная пара запроса и ответа привязки STUN. Сначала клиент отправляет запрос на привязку (запрос на привязку STUN) на сервер STUN с адресом 216.93.246.18.
Сервер вернул Binding Response и вернул общедоступный IP-адрес:
-
обход с использованием реле NAT (TURN)
TURN
является протоколом передачи данных. Разрешает проникновение NAT или брандмауэра через TCP или UDP. TURN — это клиент-серверный протокол. Метод обхода NAT в TURN похож на STUN тем, что он обеспечивает обход NAT путем получения общедоступного сетевого адреса на прикладном уровне.
-
Коллекция ЛЕД
ICE
Два конца не знают местоположение и тип NAT сети, в которой они находятся.ICE
Оптимальный путь передачи может быть найден динамически. Сторона ICE собирает локальные адреса, передаетSTUN
Сервис собирает внешние сетевые адреса NAT, передаетTURN
Соберите ретрансляционные адреса, чтобы было три адреса-кандидата:
тип хоста, то есть IP и порт локальной сети;
тип srflx, то есть IP и порт внешней сети после локального сопоставления NAT;
тип ретрансляции, который является IP-адресом и портом сервера ретрансляции.
{
IP: xxx.xxx.xxx.xxx,
port: number,
type: host/srflx/relay,
priority: number,
protocol: UDP/TCP,
usernameFragment: string
...
}
На рисунке ниже Алиса и Боб собирают три типа кандидатов через серверы STUN и TURN.
После того, как ICE соберет кандидатов, он выполняет обнаружение подключения, чтобы определить наилучший путь передачи P2P между хостами.