Wprowadzenie do WebRTC

WebRTC to nowy front w długiej wojnie o otwartą i nieograniczoną sieć.

Brendan Eich, twórca JavaScriptu

Komunikacja w czasie rzeczywistym bez wtyczek

Wyobraź sobie świat, w którym telefon, telewizor i komputer mogłyby komunikować się na wspólnej platformie. Wyobraź sobie, że dodanie czatu wideo i udostępnianie danych peer-to-peer w aplikacji internetowej jest bardzo proste. To właśnie zakłada WebRTC.

Chcesz spróbować? WebRTC jest dostępny na komputerach i urządzeniach mobilnych w przeglądarkach Google Chrome, Safari, Firefox i Opera. Na początek możesz skorzystać z prostej aplikacji do wideoczatu na stronie appr.tc:

  1. Otwórz appr.tc w przeglądarce.
  2. Kliknij Dołącz, aby dołączyć do pokoju czatu i zezwolić aplikacji na korzystanie z Twojej kamery internetowej.
  3. Otwórz adres URL wyświetlany na końcu strony w nowej karcie lub, co jeszcze lepsze, na innym komputerze.

Krótkie wprowadzenie

Nie masz czasu na przeczytanie tego artykułu lub interesuje Cię tylko kod?

Możesz też przejść bezpośrednio do ćwiczenia z programowania WebRTC, w którym znajdziesz szczegółowe instrukcje tworzenia kompletnej aplikacji do czatu wideo, w tym prostego serwera sygnalizacyjnego.

Krótka historia WebRTC

Jednym z ostatnich dużych wyzwań dla internetu jest umożliwienie komunikacji głosowej i wideo, czyli komunikacji w czasie rzeczywistym (RTC). RTC powinien być tak samo naturalny w aplikacji internetowej jak wpisywanie tekstu w polu tekstowym. Bez niego ogranicza się Twoją zdolność do wprowadzania innowacji i rozwijania nowych sposobów interakcji z użytkownikami.

W przeszłości RTC było złożonym rozwiązaniem korporacyjnym, które wymagało licencjonowania drogich technologii audio i wideo lub ich wewnętrznego opracowania. Integracja technologii RTC z istniejącymi treściami, danymi i usługami była trudna i czasochłonna, zwłaszcza w przypadku internetu.

Rozmowy wideo w Gmailu stały się popularne w 2008 r., a w 2011 r. Google wprowadził Hangouts, który korzysta z Talk (podobnie jak Gmail). Google kupiło GIPS, firmę, która opracowała wiele komponentów potrzebnych do RTC, takich jak kodeki i techniki redukcji echa. Google udostępniła jako oprogramowanie open source technologie opracowane przez GIPS i współpracowała z odpowiednimi organami standaryzacyjnymi w ramach grupy roboczej IETF (Internet Engineering Task Force) i konsorcjum W3C (World Wide Web Consortium), aby uzyskać konsensus branży. W maju 2011 r. firma Ericsson stworzyła pierwszą implementację WebRTC.

WebRTC wdrożył otwarte standardy do komunikacji w czasie rzeczywistym, bez wtyczek, dotyczących wideo, dźwięku i danych. Potrzebowaliśmy tego:

  • Wiele usług internetowych używało RTC, ale wymagało pobierania, aplikacji natywnych lub wtyczek. Dotyczyło to Skype, Facebooka i Hangouts.
  • Pobieranie, instalowanie i aktualizowanie wtyczek jest skomplikowane, podatne na błędy i niewygodne.
  • Wdrożenie, debugowanie, rozwiązywanie problemów, testowanie i utrzymanie wtyczek jest trudne, a ich stosowanie może wymagać licencjonowania i integracji ze złożoną, kosztowną technologią. Często trudno jest przekonać użytkowników do zainstalowania wtyczek.

Projekt WebRTC opiera się na następujących zasadach: interfejsy API powinny być typu open source, bezpłatne, standardowe, wbudowane w przeglądarki internetowe i wydajniejsze niż istniejące technologie.

Gdzie jesteśmy?

WebRTC jest używany w różnych aplikacjach, takich jak Google Meet. WebRTC został też zintegrowany z aplikacją natywną WebKitGTK+ i Qt.

WebRTC implementuje te 3 interfejsy API: – MediaStream (znany też jako getUserMedia) – RTCPeerConnectionRTCDataChannel

Interfejsy API są zdefiniowane w tych 2 specyfikacjach:

Wszystkie 3 interfejsy API są obsługiwane na urządzeniach mobilnych i komputerach w przypadku przeglądarek Chrome, Safari, Firefox, Edge i Opera.

getUserMedia: wersje demonstracyjne i kod znajdziesz w próbkach WebRTC. Możesz też wypróbować niesamowite przykłady Chrisa Wilsona, które wykorzystują getUserMedia jako dane wejściowe do dźwięku w internecie.

RTCPeerConnection: proste demo i w pełni funkcjonalną aplikację do wideokonferencji znajdziesz odpowiednio w próbnych implementacjach WebRTC Peer connectionappr.tc. Aplikacja ta korzysta z pliku adapter.js, który jest dodatkiem JavaScriptu obsługiwanym przez Google przy wsparciu społeczności WebRTC. Pozwala ona pominąć różnice między przeglądarkami i zmiany specyfikacji.

RTCDataChannel: aby zobaczyć to w działaniu, otwórz próbki WebRTC i obejrzyj jeden z demo kanału danych.

Ćwiczenia z programowania dotyczące WebRTC pokazują, jak użyć wszystkich 3 interfejsów API do utworzenia prostej aplikacji do czatu wideo i udostępniania plików.

Pierwsze spotkanie WebRTC

Aplikacje WebRTC muszą:

  • strumieniowe odtwarzanie dźwięku, obrazu lub innych danych;
  • Pobieranie informacji o sieci, takich jak adresy IP i porty, oraz wymiana tych informacji z innymi klientami WebRTC (zwanymi peerami) w celu umożliwienia połączenia nawet przez NAT i zapory sieciowe.
  • koordynować sygnalizację, aby zgłaszać błędy i inicjować lub zamykać sesje;
  • wymieniać informacje o multimediach i możliwościach klienta, takie jak rozdzielczość i kodeki;
  • przesyłać strumieniowo dźwięk, wideo lub dane;

Aby pozyskiwać i przesyłać dane strumieniowe, WebRTC implementuje te interfejsy API:

  • MediaStream uzyskuje dostęp do strumieni danych, np. z aparatu i mikrofonu użytkownika.
  • RTCPeerConnection umożliwia prowadzenie rozmów audio i wideo z szyfrowaniem oraz zarządzaniem przepustowością.
  • RTCDataChannel umożliwia komunikację peer-to-peer danych ogólnych.

(szczegółowe informacje o aspekcie sieci i sygnalizacji WebRTC znajdziesz w następnych częściach artykułu).

Interfejs API MediaStream (znany też jako interfejs API getUserMedia)

Interfejs MediaStream API reprezentuje zsynchronizowane strumienie multimediów. Na przykład strumień z wejścia z aparatu i mikrofonu zawiera zsynchronizowane ścieżki wideo i dźwięku. (Nie myl MediaStreamTrack z elementem <track>, który jest czymś zupełnie innym).

Najłatwiej zrozumieć interfejs API MediaStream, korzystając z niego w praktyce:

  1. W przeglądarce otwórz Przykłady WebRTC getUserMedia.
  2. Otwórz konsolę.
  3. Sprawdź zmienną stream, która ma zakres globalny.

Każdy element MediaStream ma dane wejściowe, które mogą być generowane przez element MediaStream, oraz dane wyjściowe, które mogą być przekazywane do elementu wideo lub elementu RTCPeerConnection.getUserMedia()

Metoda getUserMedia() przyjmuje parametr obiektu MediaStreamConstraints i zwraca obiekt Promise, który jest rozwiązywany do obiektu MediaStream.

Każdy element MediaStream ma element label, np. 'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'. Metody getAudioTracks() i getVideoTracks() zwracają tablicę wartości MediaStreamTrack.

W przypadku przykładu getUserMedia funkcja stream.getAudioTracks() zwraca pusty tablicowy (ponieważ nie ma dźwięku), a zakładając, że podłączona jest działająca kamera internetowa, funkcja stream.getVideoTracks() zwraca tablicowy z jednym elementem MediaStreamTrack reprezentującym strumień z kamery internetowej. Każdy MediaStreamTrack ma rodzaj ('video' lub 'audio'), label (coś w rodzaju 'FaceTime HD Camera (Built-in)') i reprezentuje co najmniej 1 kanał audio lub wideo. W tym przypadku jest tylko 1 ścieżka wideo i brak dźwięku, ale łatwo sobie wyobrazić przypadki użycia, w których jest ich więcej, np. aplikacja do czatowania, która przesyła strumienie z przedniego aparatu, tylnego aparatu i mikrofonu, oraz aplikacja udostępniająca ekran.

Do elementu wideo można dołączyć MediaStream, ustawiając atrybut srcObject. Wcześniej było to możliwe dzięki ustawieniu atrybutu src na adres URL obiektu utworzonego za pomocą atrybutu URL.createObjectURL(), ale został on wycofany.

getUserMedia może też służyć jako węzeł wejściowy dla Web Audio API:

// Cope with browser differences.
let audioContext;
if (typeof AudioContext === 'function') {
  audioContext = new AudioContext();
} else if (typeof webkitAudioContext === 'function') {
  audioContext = new webkitAudioContext(); // eslint-disable-line new-cap
} else {
  console.log('Sorry! Web Audio not supported.');
}

// Create a filter node.
var filterNode = audioContext.createBiquadFilter();
// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section
filterNode.type = 'highpass';
// Cutoff frequency. For highpass, audio is attenuated below this frequency.
filterNode.frequency.value = 10000;

// Create a gain node to change audio volume.
var gainNode = audioContext.createGain();
// Default is 1 (no change). Less than 1 means audio is attenuated
// and vice versa.
gainNode.gain.value = 0.5;

navigator.mediaDevices.getUserMedia({audio: true}, (stream) => {
  // Create an AudioNode from the stream.
  const mediaStreamSource =
    audioContext.createMediaStreamSource(stream);
  mediaStreamSource.connect(filterNode);
  filterNode.connect(gainNode);
  // Connect the gain node to the destination. For example, play the sound.
  gainNode.connect(audioContext.destination);
});

Aplikacje i rozszerzenia oparte na Chromium również mogą zawierać getUserMedia. Dodanie do pliku manifestu uprawnień audioCapture lub videoCapture umożliwia proszenie o uprawnienia i przyznawanie ich tylko raz podczas instalacji. Następnie użytkownik nie jest proszony o pozwolenie na dostęp do aparatu ani mikrofonu.

Uprawnienia należy przyznać tylko raz.getUserMedia() Gdy po raz pierwszy otwierasz stronę, na infobar przeglądarki wyświetla się przycisk Zezwól. Pod koniec 2015 r. Chrome wycofał obsługę dostępu HTTP do usługi getUserMedia(), ponieważ została ona sklasyfikowana jako potężna funkcja.

Chodzi o to, aby umożliwić MediaStream dla dowolnego źródła danych strumieniowych, a nie tylko aparatu czy mikrofonu. Umożliwiłoby to strumieniowanie z przechowywanych danych lub dowolnych źródeł danych, takich jak czujniki czy inne dane wejściowe.

getUserMedia() naprawdę błyszczy w połączeniu z innymi interfejsami API i bibliotekami JavaScriptu:

  • Webcam Toy to aplikacja do robienia zdjęć, która wykorzystuje WebGL do dodawania dziwnych i wspaniałych efektów do zdjęć, które można udostępniać lub zapisywać lokalnie.
  • FaceKat to gra śledząca twarz, stworzona przy użyciu biblioteki headtrackr.js.
  • Aparat ASCII generuje obrazy ASCII za pomocą interfejsu Canvas API.
Obraz ASCII wygenerowany przez idevelop.ro/ascii-camera
gUM ASCII art!

Ograniczenia

Ograniczenia można używać do ustawiania wartości rozdzielczości filmów w getUserMedia(). Umożliwia to też obsługę innych ograniczeń, takich jak format obrazu, kierunek kamery (przednia lub tylna), szybkość, wysokość i szerokość klatki oraz metoda applyConstraints().

Przykład znajdziesz w przykładach kodu WebRTC: getUserMedia wybierz rozdzielczość.

Ustawienie niedozwolonej wartości ograniczeń powoduje wyświetlenie wartości DOMException lub OverconstrainedError, jeśli na przykład żądana rozdzielczość jest niedostępna. Aby zobaczyć to w działaniu, skorzystaj z próbek WebRTC: wybierz rozdzielczość.getUserMedia

Przechwytywanie ekranu i karty

Aplikacje w Chrome umożliwiają też udostępnianie obrazu wideo z pojedynczej karty przeglądarki lub całego pulpitu za pomocą interfejsów API chrome.tabCapture i chrome.desktopCapture. (Demo i więcej informacji znajdziesz w artykule Udostępnianie ekranu za pomocą WebRTC. Artykuł ma już kilka lat, ale nadal jest interesujący.)

W Chrome możesz też użyć jako źródła MediaStream zrzutu ekranu, korzystając z eksperymentalnej reguły chromeMediaSource. Pamiętaj, że przechwytywanie ekranu wymaga HTTPS i należy go używać tylko do celów programistycznych, ponieważ jest włączane za pomocą flagi w linii poleceń, jak opisano w tym poście.

sygnalizacja: informacje o sterowaniu sesją, sieci i multimediach;

WebRTC używa RTCPeerConnection do przesyłania danych strumieniowych między przeglądarkami (zwanymi też peerami), ale potrzebuje też mechanizmu do koordynowania komunikacji i wysyłania komunikatów sterujących, czyli procesu znanego jako sygnalizacja. Metody i protokoły sygnalizacji nie są określone przez WebRTC. Sygnalizacja nie jest częścią interfejsu API RTCPeerConnection.

Zamiast tego deweloperzy aplikacji WebRTC mogą wybrać dowolny preferowany protokół przesyłania wiadomości, np. SIP lub XMPP, oraz dowolny odpowiedni kanał komunikacji dwukierunkowej. Przykład appr.tc używa XHR i Channel API jako mechanizmu sygnalizacji. Ćwiczenie z programowaniem korzysta z Socket.io działającego na serwerze Node.js.

Sygnalizowanie służy do wymiany 3 rodzajów informacji:

  • komunikaty sterujące sesją: do inicjowania i zamykania komunikacji oraz zgłaszania błędów;
  • Konfiguracja sieci: jakie są adres IP i port komputera w oczach świata zewnętrznego?
  • Możliwości multimedialne: jakie kodeki i rozdzielczości może obsługiwać Twoja przeglądarka i przeglądarka, z którą chce się komunikować?

Zanim rozpocznie się strumieniowe przesyłanie danych peer-to-peer, wymiana informacji za pomocą sygnalizacji musi zostać zakończona.

Załóżmy, że Alicja chce się komunikować z Robertem. Oto przykładowy kod ze specyfikacji W3C WebRTC, który pokazuje proces sygnalizacji w akcji. Kod zakłada istnienie pewnego mechanizmu sygnalizacji utworzonego w ramach metody createSignalingChannel(). Pamiętaj też, że w Chrome i Operze przedrostek RTCPeerConnection jest obecnie dodawany.

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // Send the offer to the other peer.
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// Once remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

Najpierw Alicja i Robert wymieniają się informacjami o sieci. (wyrażenie znajdowanie kandydatów odnosi się do procesu znajdowania interfejsów sieciowych i portów za pomocą ramki ICE).

  1. Alice tworzy obiekt RTCPeerConnection z obsługą onicecandidate, która jest wykonywana, gdy stają się dostępne kandydackie sieci.
  2. Alice wysyła zserializowane dane kandydata do Boba za pomocą dowolnego kanału sygnalizacyjnego, takiego jak WebSocket lub inny mechanizm.
  3. Gdy Bob otrzyma od Alice wiadomość od kandydata, zadzwoni pod numer addIceCandidate, aby dodać kandydata do opisu zdalnego peera.

Klienci WebRTC (zwani też peerami lub Alicją i Bobem w tym przykładzie) muszą też ustalać i wymieniać informacje o lokalnych i zdalnych nośnikach audio i wideo, takie jak rozdzielczość i możliwości kodeków. Sygnalizowanie wymiany informacji o konfiguracji mediów odbywa się poprzez wymianę oferty i odpowiedzi za pomocą protokołu SDP (Session Description Protocol):

  1. Alicja wykonuje metodę RTCPeerConnection createOffer(). Zwracana wartość jest przekazywana do RTCSessionDescription – opisu sesji lokalnej Alice.
  2. W wywołaniu zwrotnym Alice ustawia lokalny opis za pomocą setLocalDescription(), a potem wysyła opis sesji do Boba przez kanał sygnalizacyjny. Pamiętaj, że RTCPeerConnection nie zacznie zbierać kandydatów, dopóki nie zostanie wywołana funkcja setLocalDescription(). Jest to ujęte w projekcie JSEP IETF.
  3. Robert ustawia opis wysłany przez Alicję jako opis zdalny za pomocą atrybutu setRemoteDescription().
  4. Robert wykonuje metodę RTCPeerConnection createAnswer(), przekazując do niej zdalny opis otrzymany od Alicji, aby można było wygenerować lokalną sesję zgodną z jej sesją. Wywołanie zwrotne createAnswer() otrzymuje argument RTCSessionDescription. Robert ustawia go jako opis lokalny i wysyła do Alicji.
  5. Gdy Alicja otrzyma opis sesji Roberta, ustawi go jako opis zdalny za pomocą setRemoteDescription.
  6. Ping!

Obiekty RTCSessionDescription to bloby zgodne z protokołem Session Description Protocol (SDP). Obiekt SDP w postaci ciągu wygląda tak:

v=0
o=- 3883943731 1 IN IP4 127.0.0.1
s=
t=0 0
a=group:BUNDLE audio video
m=audio 1 RTP/SAVPF 103 104 0 8 106 105 13 126

// ...

a=ssrc:2223794119 label:H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810

Pozyskiwanie i wymiana informacji o sieci i mediach mogą odbywać się jednocześnie, ale oba procesy muszą zostać ukończone, zanim rozpocznie się strumieniowe przesyłanie dźwięku i obrazu między peerami.

Opisywana wcześniej architektura oferty/odpowiedzi nosi nazwę JavaScript Session Establishment Protocol (JSEP). W filmie demonstracyjnym firmy Ericsson dotyczącym pierwszej implementacji WebRTC znajdziesz świetną animację, która wyjaśnia proces sygnalizacji i strumieniowania.

Schemat architektury JSEP
Architektura JSEP

Po zakończeniu procesu sygnalizacji dane mogą być przesyłane strumieniowo bezpośrednio między stronami, między osobą dzwoniącą a osobą odbierającą połączenie, lub w razie niepowodzenia przez pośredniczący serwer przekaźnikowy (więcej informacji na ten temat znajdziesz poniżej). Streaming to zadanie RTCPeerConnection.

RTCPeerConnection

RTCPeerConnection to komponent WebRTC, który zapewnia stabilną i wydajną komunikację danych strumieniowych między urządzeniami.

Poniżej znajduje się diagram architektury WebRTC pokazujący rolę RTCPeerConnection. Jak widać, zielone części są skomplikowane.

Schemat architektury WebRTC
Architektura WebRTC (z webrtc.org)

Z tego diagramu wynika, że RTCPeerConnection chroni programistów stron internetowych przed mnóstwem złożonych elementów, które kryją się pod spodem. Kodeki i protokoły używane przez WebRTC wykonują ogromną ilość pracy, aby umożliwić komunikację w czasie rzeczywistym nawet w niepewnych sieciach:

  • Ukrywanie utraty pakietów
  • usuwanie echa;
  • dostosowywanie się do przepustowości,
  • Dynamiczne buforowanie jittera
  • Automatyczna kontrola wzmocnienia
  • Redukcja i eliminacja szumów
  • Czyszczenie obrazu

Poprzedni kod W3C pokazuje uproszczony przykład WebRTC z perspektywy sygnalizacji. Poniżej znajdziesz instrukcje dotyczące dwóch działających aplikacji WebRTC. Pierwszy to prosty przykład pokazujący RTCPeerConnection, a drugi to w pełni działający klient czatu wideo.

RTCPeerConnection bez serwerów

Poniższy kod pochodzi z próbek kodu WebRTC dotyczących połączenia peer-to-peer, które zawiera lokalny i zdalny RTCPeerConnection (oraz lokalny i zdalny film) na jednej stronie internetowej. Nie jest to bardzo przydatne – wywołujący i wywoływany znajdują się na tej samej stronie – ale pozwala nieco lepiej zrozumieć działanie interfejsu API RTCPeerConnection, ponieważ obiekty RTCPeerConnection na stronie mogą wymieniać dane i wiadomości bezpośrednio bez konieczności korzystania z pośrednich mechanizmów sygnalizacji.

W tym przykładzie pc1 reprezentuje lokalny punkt końcowy (wywołujący), a pc2 reprezentuje zdalny punkt końcowy (wywoływany).

Rozmówca

  1. Utwórz nowy plik RTCPeerConnection i dodaj do niego strumień z pliku getUserMedia():```js // Servers to opcjonalny plik konfiguracji. (patrz dalej sekcja dotycząca protokołów TURN i STUN) pc1 = new RTCPeerConnection(servers); // ... localStream.getTracks().forEach((track) => { pc1.addTrack(track, localStream); });
  1. Utwórz ofertę i ustaw ją jako lokalny opis dla pc1 oraz jako opis zdalny dla pc2. Można to zrobić bezpośrednio w kodzie bez używania sygnalizacji, ponieważ zarówno dzwoniący, jak i odbiorca są na tej samej stronie: js pc1.setLocalDescription(desc).then(() => { onSetLocalSuccess(pc1); }, onSetSessionDescriptionError ); trace('pc2 setRemoteDescription start'); pc2.setRemoteDescription(desc).then(() => { onSetRemoteSuccess(pc2); }, onSetSessionDescriptionError );

Wywoływany

  1. Utwórz pc2, a gdy dodasz strumień z pc1, wyświetl go w elemencie wideo:js pc2 = new RTCPeerConnection(servers); pc2.ontrack = gotRemoteStream; //... function gotRemoteStream(e){ vid2.srcObject = e.stream; }

RTCPeerConnection API plus serwery

W rzeczywistych zastosowaniach WebRTC wymaga serwerów, nawet prostych, więc mogą wystąpić takie sytuacje:

  • Użytkownicy mogą się nawzajem znajdować i wymieniać informacje na temat siebie, np. imiona.
  • Aplikacje klienckie WebRTC (peers) wymieniają informacje o sieci.
  • Urządzenia wymieniają się danymi o multimediach, takimi jak format i rozdzielczość wideo.
  • Aplikacje klienckie WebRTC przechodzą przez bramy NAT i zapory sieciowe.

Innymi słowy, WebRTC wymaga 4 typów funkcji po stronie serwera:

  • Wyszukiwanie użytkowników i komunikacja
  • Wysyłanie sygnałów
  • Omijanie zapory sieciowej lub bramy NAT
  • serwery pośredniczące na wypadek, gdyby komunikacja peer-to-peer nie powiodła się;

Ten artykuł nie dotyczy pokonywania NAT, sieci typu peer-to-peer ani wymagań dotyczących tworzenia aplikacji serwera na potrzeby wykrywania użytkowników i sygnalizacji. Wystarczy powiedzieć, że protokół STUN i jego rozszerzenie TURN są używane przez mechanizm ICE, aby umożliwić RTCPeerConnection radzenie sobie z przekierowywaniem NAT i innymi kaprysami sieci.

ICE to framework do łączenia urządzeń, np. dwóch klientów rozmów wideo. Na początku ICE próbuje połączyć peerów bezpośrednio z najmniejszym możliwym opóźnieniem za pomocą protokołu UDP. W ramach tego procesu serwery STUN mają tylko jedno zadanie: umożliwić peerowi za NAT-em znalezienie jego publicznego adresu i portu. (Więcej informacji o protokołach STUN i TURN znajdziesz w artykule Tworzenie usług backendowych potrzebnych do aplikacji WebRTC).

Wyszukiwanie kandydatów do połączenia
Znajdowanie kandydatów do połączenia

Jeśli UDP się nie powiedzie, ICE spróbuje użyć TCP. Jeśli połączenie bezpośrednie się nie powiedzie, zwłaszcza z powodu korporacyjnych zapory sieciowej i przekierowania NAT, ICE używa pośredniego serwera TURN. Inaczej mówiąc, ICE najpierw używa STUN z UDP do bezpośredniego łączenia peerów, a jeśli to się nie uda, przechodzi na serwer pośredniczący TURN. Wyrażenie znajdowanie kandydatów odnosi się do procesu znajdowania interfejsów i portów sieciowych.

Ścieżki danych WebRTC
Ścieżki danych WebRTC

Inżynier zajmujący się WebRTC, Justin Uberti, podaje więcej informacji o protokołach ICE, STUN i TURN w prezentacji WebRTC z Google I/O 2013. (slajdy z prezentacją zawierają przykłady implementacji serwera TURN i STUN).

Prosty klient do obsługi czatów wideo

Aby wypróbować WebRTC z użyciem sygnalizacji i przechodzenia przez NAT/firewall za pomocą serwera STUN, możesz skorzystać z demo czatu wideo na stronie appr.tc. Ta aplikacja używa pliku adapter.js, który chroni aplikacje przed zmianami specyfikacji i różnicami w prefiksach.

Kod celowo jest wyczerpujący w logowaniu. Aby poznać kolejność zdarzeń, sprawdź konsolę. Poniżej znajdziesz szczegółowy opis kodu.

Topologie sieci

W obecnej implementacji WebRTC obsługuje tylko komunikację jeden-do-jednego, ale można go używać w bardziej złożonych scenariuszach sieciowych, np. w przypadku wielu punktów końcowych, z których każdy komunikuje się bezpośrednio lub za pomocą jednostki sterującej wielopunktowej (MCU), czyli serwera, który może obsługiwać dużą liczbę uczestników i wykonywć selektywną transmisję strumieniową oraz miksowanie lub nagrywanie dźwięku i obrazu.

Diagram topologii wielopunktowego modułu sterującego
Przykład topologii jednostki sterującej wielopunktowej

Wiele istniejących aplikacji WebRTC demonstruje tylko komunikację między przeglądarkami, ale serwery bramy mogą umożliwić aplikacji WebRTC działającej w przeglądarce interakcję z urządzeniami, takimi jak telefony (znane też jako PSTN) i systemy VOIP. W maju 2012 r. firma Doubango Telecom udostępniła w wersji open source klienta SIP sipml5 zbudowanego z użyciem WebRTC i WebSocket, który (między innymi) umożliwia prowadzenie rozmów wideo między przeglądarkami i aplikacjami działającymi na iOS i Androidzie. Podczas konferencji Google I/O firmy Tethr i Tropo zaprezentowały ramy do komunikacji w sytuacji katastrofy w walizce, korzystając z komórki OpenBTS, aby umożliwić komunikację między telefonami komórkowymi a komputerami za pomocą WebRTC. Komunikacja telefoniczna bez operatora

Prezentacja Tethr/Tropo na konferencji Google I/O 2012
Tethr/Tropo: komunikacja w przypadku katastrofy w walizce

RTCDataChannel API<

Oprócz dźwięku i obrazu WebRTC obsługuje komunikację w czasie rzeczywistym dla innych typów danych.

Interfejs API RTCDataChannel umożliwia wymianę dowolnych danych z małym opóźnieniem i dużą przepustowością. Demonstracje jednostronicowe i informacje o tworzeniu prostej aplikacji do przesyłania plików znajdziesz odpowiednio w przykładach WebRTCćwiczeniach z programowania poświęconych WebRTC.

Interfejs API ma wiele zastosowań, m.in.:

  • Gry
  • Aplikacje pulpitu zdalnego
  • Czat tekstowy w czasie rzeczywistym
  • Przesyłanie plików
  • Sieci zdecentralizowane

Interfejs API zawiera kilka funkcji, które umożliwiają optymalne wykorzystanie RTCPeerConnection i zapewniają zaawansowaną i elastyczna komunikację peer-to-peer:

  • Korzystanie z konfiguracji sesji RTCPeerConnection
  • Wiele kanałów jednocześnie z uwzględnieniem priorytetów
  • niezawodna i nierzetelna semantyka dostawy,
  • Wbudowane zabezpieczenia (DTLS) i kontrola natężenia
  • Możliwość korzystania z nich z dźwiękiem lub bez niego

Składnia jest celowo podobna do WebSocket z metodą send() i zdarzeniem message:

const localConnection = new RTCPeerConnection(servers);
const remoteConnection = new RTCPeerConnection(servers);
const sendChannel =
  localConnection.createDataChannel('sendDataChannel');

// ...

remoteConnection.ondatachannel = (event) => {
  receiveChannel = event.channel;
  receiveChannel.onmessage = onReceiveMessage;
  receiveChannel.onopen = onReceiveChannelStateChange;
  receiveChannel.onclose = onReceiveChannelStateChange;
};

function onReceiveMessage(event) {
  document.querySelector("textarea#send").value = event.data;
}

document.querySelector("button#send").onclick = () => {
  var data = document.querySelector("textarea#send").value;
  sendChannel.send(data);
};

Komunikacja odbywa się bezpośrednio między przeglądarkami, więc RTCDataChannel może być znacznie szybsza niż WebSocket, nawet jeśli serwer przekazujący (TURN) jest wymagany, gdy nie udaje się wykonać dziurkowania, aby poradzić sobie z zaporami sieciowymi i NAT.

RTCDataChannel jest dostępna w Chrome, Safari, Firefox, Opera i Samsung Internet. Gra Cube Slam używa interfejsu API do komunikowania stanu gry. Możesz zagrać rolę przyjaciela lub niedźwiedzia. Innowacyjna platforma Sharefest umożliwiała udostępnianie plików za pomocą RTCDataChannel, a peerCDN dawała możliwość zapoznania się z możliwościami rozpowszechniania treści peer-to-peer za pomocą WebRTC.

Więcej informacji o RTCDataChannel znajdziesz w projektie specyfikacji protokołu IETF.

Bezpieczeństwo

Aplikacja do komunikacji w czasie rzeczywistym lub wtyczka mogą naruszać bezpieczeństwo na kilka sposobów. Na przykład:

  • Niezaszyfrowane multimedia lub dane mogą zostać przechwycone między przeglądarkami lub między przeglądarką a serwerem.
  • Aplikacja może nagrywać i rozpowszechniać filmy lub dźwięk bez wiedzy użytkownika.
  • Złośliwe oprogramowanie lub wirusy mogą zostać zainstalowane razem z pozornie nieszkodliwym wtyczką lub aplikacją.

WebRTC zawiera kilka funkcji, które zapobiegają tym problemom:

  • Implementacje WebRTC korzystają z bezpiecznych protokołów, takich jak DTLSSRTP.
  • Szyfrowanie jest wymagane w przypadku wszystkich komponentów WebRTC, w tym mechanizmów sygnalizacji.
  • WebRTC nie jest wtyczką. Jego komponenty działają w piaskownicy przeglądarki, a nie w osobnym procesie. Komponenty nie wymagają osobnej instalacji i są aktualizowane przy każdej aktualizacji przeglądarki.
  • Dostęp do aparatu i mikrofonu musi być wyraźnie przyznany, a w przypadku włączonego aparatu lub mikrofonu musi być wyraźnie widoczny w interfejsie.

Ten artykuł nie zawiera pełnej dyskusji na temat zabezpieczeń mediów strumieniowych. Więcej informacji znajdziesz w proponowanej architekturze zabezpieczeń WebRTC opracowanej przez IETF.

Podsumowanie

Interfejsy API i standardy WebRTC mogą demokratyzować i decentralizować narzędzia do tworzenia treści i komunikacji, w tym telefonii, gier, produkcji filmów, tworzenia muzyki i gromadzenia wiadomości.

Trudno o bardziej rewolucyjną technologię.

Jak powiedział bloger Phil Edholm, „WebRTC i HTML5 mogą umożliwić tę samą przemianę komunikacji w czasie rzeczywistym, jaką pierwotna przeglądarka wprowadziła w przypadku informacji”.

Narzędzia dla programistów

Więcej informacji

Standardy i protokoły

Podsumowanie obsługi WebRTC

Interfejsy MediaStream i getUserMedia API

  • Chrome na komputery w wersji 18.0.1008 lub nowszej; Chrome na Androida w wersji 29 lub nowszej
  • Opera 18 lub nowsza; Opera na Androida 20 lub nowsza
  • Opera 12, Opera Mobile 12 (oparte na silniku Presto)
  • Firefox 17 lub nowsza
  • Microsoft Edge w wersji 16 lub nowszej
  • Safari w wersji 11.2 lub nowszej w systemie iOS oraz w wersji 11.1 lub nowszej w systemie macOS
  • UC 11.8 lub nowsza na Androida
  • Samsung Internet 4 lub nowsza

RTCPeerConnection interfejs API

  • Chrome na komputery w wersji 20 lub nowszej; Chrome na Androida w wersji 29 lub nowszej (bez flagi)
  • Opera 18 lub nowsza (włączona domyślnie); Opera na Androida 20 lub nowsza (włączona domyślnie)
  • Firefox 22 lub nowsza (włączona domyślnie)
  • Microsoft Edge w wersji 16 lub nowszej
  • Safari w wersji 11.2 lub nowszej w systemie iOS oraz w wersji 11.1 lub nowszej w systemie macOS
  • Samsung Internet 4 lub nowsza

RTCDataChannel interfejs API

  • Wersja eksperymentalna w Chrome 25, ale bardziej stabilna (i z współdziałaniem z Firefoxem) w Chrome 26 i nowszych; Chrome na Androida 29 i nowszych.
  • stabilna wersja (z współdziałaniem z Firefoxem) w Opera 18 lub nowszej; Opera na Androida 20 lub nowszej
  • Firefox 22 lub nowsza (włączona domyślnie)

Więcej informacji o obsługiwaniu interfejsów API na różnych platformach, takich jak getUserMediaRTCPeerConnection, znajdziesz na stronie caniuse.com i w dokumentacji stanu platformy Chrome.

Natywna wersja interfejsu API dla RTCPeerConnection jest też dostępna w dokumentacji na stronie webrtc.org.