Някога в от старинните и вече забравени статии писах как лесно и неусетно може да се предава видео от canvas чрез websockets. В тази статия повърхностно разказах как да се захваща видео от камера и звук от микрофон чрез , как полученият поток да се кодира и изпраща чрез websockets на сървър. Въпреки това, в действителност не се прави така, за предаване се използва или специален софтуер, който трябва да се инсталира и настрои: на око това може да бъде , или се използва WebRTC, който работи направо от кутията, т.е. не изисква инсталиране на никакви плъгини, като flash player, който през декември ще бъде премахнат от браузера Chromium.
Днес ще говорим за WebRTC.
Web Real-Time Communication (WebRTC) не е един протокол, а цяла колекция от стандарти, протоколи и JavaScript API, които всички заедно осигуряват peer-to-peer видео-аудио комуникации в реално време и могат да бъдат използвани за предаване на всякакви бинарни данни. Обикновено пирите са браузъри, но това може да бъде и мобилно приложение, например. За да организирате p2p комуникация между клиентите, браузърът трябва да поддържа различни видове кодиран видео и аудио, поддръжка на множество мрежови протоколи, осигуряване на взаимодействие между хардуера и браузъра (чрез слоеве на ОС): уебкамери, звукови карти. Цялата тази смесица от технологии е скрита зад абстракция на JavaScript API за удобство на разработчиците.
Всичко се свежда до три API:
— разгледан в миналия път, днес ще напиша малко повече за него. Служи за получаване на видео/аудио потоци от "железото"
— осигурява комуникации между двама клиенти (p2p)
— служи за предаване на произволни данни между двама клиенти
Подготовка на аудио и видео потоци за предаване
Всичко започва с "захващането" на медийните потоци от уебкамера и микрофон. Суровите потоци, разбира се, не са подходящи за организиране на телеконференция, всеки поток трябва да бъде обработен: да се подобри качеството, да се синхронизира аудиото с видеото, да се поставят маркери за синхронизация във видеопотока, да се осигури съответстващ на постоянно променящата се ширина на канала битрейт. Браузърът поема всичко това, разработчикът дори не трябва да се грижи за осигуряване на кодиране на медийните потоци. Вътре в съвременния браузър вече има софтуерни слоеве за захващане, подобряване на качеството (премахване на ехо и шум от звука, подобряване на изображението), кодиране на видео и аудио. Схемата на слоевете е показана на рис. 1:
Рис. 1. Слоеве на аудио и видео обработка в браузера
Цялата обработка се извършва директно в самия браузър, не са необходими допълнителни приставки. Въпреки това положението не е толкова розово към 2020 година. Има браузъри, които все още не поддържат напълно , можете да преминете по линка и в най-долната част да видите таблицата с съвместимост. В частност IE отново разочарова.
С получените потоци можете да правите много интересни неща: можете да копирате, да променяте разрешението на видеото, да манипулирате качеството на звука, можете да вземете и "прикачите" Media Stream потока към
— момчетата се занимават с realtime CV на Javascript. Те имат цял от различни js-библиотеки за работа с видеопоток на canvas: детектиране на лица, обекти, прилагане на филтри (маски, както в инстаграм) и пр. Отличен пример за това как без допълнителни приставки можете директно в браузера да обработвате видео в реално време.
— документацията на API за захващане на видеопоток от canvas. Вече се поддържа в Chrome, Opera и Firefox
RTCPeerConnection
А ето и стигнахме до въпроса, как собствено да предадем видеото на друг потребител? На преден план излиза . Ако трябва да сме кратки, на тази стъпка е необходимо да създадете обект RTCPeerConnection:
const peerConnection = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
});Една от опциите, които посочваме, е iceServers — това е сървър, който помага за осигуряване на връзката между два браузъра, находящи се зад NAT. Това решава проблема: как да разберем IP-то на събеседника, ако той е зад NAT на своя доставчик? На помощ идва ICE протоколът, всъщност, ICE не е свързан с WebRTC, но за това по-късно.
По-рано получихме Usermedia потоци:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
\/\/ Usermedia-потоци, обикновено видео и аудио
const tracks = stream.getTracks();
for (const track of tracks) {
\/\/ всеки трек прикачваме към peerConnection
peerConnection.addTrack(track);
}
}).catch(console.error);След това в peerConnection се задейства събитието onnegotiationneeded, в обработчика на което трябва да създадем оферта (в термини SDP — Session Description Protocol) и да зададем в peerConnection чрез метода setLocalDescription. За SDP — какво е това и за форматирането на оферта и отговор — ще говорим по-късно.
След задаването на LocalDescription в peerConnection, браузърът „събира“ ice-кандидати, тоест намира различни пътища за комуникация през NAT. Събитието onicegatheringstatechange се задейства. В обработчика onicegatheringstatechange разрешаваме свързването с webrtc-signaling-сървъра stream за обмен на Session Description между пиратите:
peerConnection.oniceconnectionstatechange = (event) => {
console.log('Connection state: ', peerConnection.iceConnectionState);
if (peerConnection.iceConnectionState === 'connected') {
// Можем активировать кнопку Start broadcast
setBroadcasting(true);
setBroadcastingBtnActive(true);
}
};
// Событие срабатывает сразу, как только добавился медаиапоток в peerConnection
peerConnection.onnegotiationneeded = (event) => {
// Создаем и назначаем SDP offer
peerConnection.createOffer().
then((offer) => peerConnection.setLocalDescription(offer)).
catch(console.error);
};
// Событие срабатывает каждый раз, как появляется ICE кандидат
peerConnection.onicegatheringstatechange = (ev) => {
let connection = ev.target;
// Now we can activate broadcast button
if (connection.iceGatheringState === 'complete') {
let delay = 50;
let tries = 0;
let maxTries = 3;
let timerId = setTimeout(function allowStreaming() {
if (isOnline) {
setBroadcastingBtnActive(true);
return;
}
if (tries < maxTries) {
tries += 1;
delay *= 2;
timerId = setTimeout(allowStreaming, delay);
} else {
// TODO: show user notification
console.error("Can't connect to server");
alert("Can't connect to server");
}
}, delay);
}
};webrtc-signaling-сървърът — това е сървър, необходим за осигуряване на обмен на session description между два пира, това може да бъде прост websockets или xhr-сървър на който и да е език. Неговата задача е проста: да приеме session description от един пир и да я предаде на друг.
След обмена на Session descriptions, и двете страни са готови да предават и приемат видеопотоци, от страната, която приема видеопотока, се задейства събитието ontrack на peerConnection, в обработчика на което можем да зададем получените трекове на <video> и да гледаме любимия събеседник. Следват теория и подробности.
Линкове и литература:
— документация
— реализация на WebRTC протоколите на go
— книжка от създателите на pion
— книга High Perfomance Browser Networking. Подробно разглежда въпросите за осигуряване на висока производителност на уеб приложения. В края се описва WebRTC. Книгата е разбира се стара (2013), но не е загубила своята актуалност.
В следващата част искам да дам още малко теоретична информация и на практика да разгледам приема и обработката на видео поток на сървър с помощта на Pion, транскодиране в HLS чрез FFmpeg за последваща транслация на зрителите в браузъра.
За нетърпеливите: (това е просто експеримент).
Източник: habr.com
