Wprowadzenie do WebRTC

WebRTC to nowy front w długiej wojnie o otwartą i wolną od ograniczeń sieć.

Brendan Eich, twórca JavaScriptu

Komunikacja w czasie rzeczywistym bez wtyczek

Wyobraź sobie świat, w którym telefon, telewizor i komputer mogą komunikować się na wspólnej platformie. Wyobraź sobie, że możesz łatwo dodać do aplikacji internetowej czat wideo i udostępnianie danych peer-to-peer. To właśnie jest wizja WebRTC.

Chcesz wypróbować tę funkcję? WebRTC jest dostępny na komputerach i urządzeniach mobilnych w przeglądarkach Google Chrome, Safari, Firefox i Opera. Dobrym punktem wyjścia jest prosta aplikacja do czatu wideo 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 używanie kamery internetowej.
  3. Otwórz adres URL wyświetlany na końcu strony w nowej karcie lub, co jeszcze lepsze, na innym komputerze.

Szybki start

Nie masz czasu na przeczytanie tego artykułu lub chcesz tylko uzyskać kod?

Możesz też przejść od razu do ćwiczeń z programowania poświęconych WebRTC, czyli szczegółowego przewodnika, który wyjaśnia, jak utworzyć kompletną aplikację do czatu wideo, w tym prosty serwer sygnalizacyjny.

Krótka historia WebRTC

Jednym z ostatnich poważnych wyzwań dla internetu jest umożliwienie komunikacji głosowej i wideo, czyli komunikacji w czasie rzeczywistym (RTC). RTC powinna być w aplikacji internetowej tak naturalna jak wpisywanie tekstu w polu tekstowym. Bez niej Twoje możliwości wprowadzania innowacji i opracowywania nowych sposobów interakcji z użytkownikami są ograniczone.

Tradycyjnie RTC było złożone i wymagało drogich technologii audio i wideo, które trzeba było licencjonować lub opracowywać we własnym zakresie. Integracja technologii RTC z dotychczasowymi treściami, danymi i usługami była trudna i czasochłonna, zwłaszcza w internecie.

Rozmowy wideo w Gmailu zyskały popularność w 2008 roku, a w 2011 roku Google wprowadził Hangouts, który korzystał z Talk (podobnie jak Gmail). Google kupiło firmę GIPS, która opracowała wiele komponentów wymaganych w przypadku RTC, takich jak kodeki i techniki eliminowania echa. Google udostępniło technologie opracowane przez GIPS jako oprogramowanie open source i nawiązało współpracę z odpowiednimi organizacjami normalizacyjnymi w ramach Internet Engineering Task Force (IETF) i World Wide Web Consortium (W3C), aby zapewnić konsensus w branży. W maju 2011 roku firma Ericsson stworzyła pierwszą implementację WebRTC.

WebRTC wykorzystuje otwarte standardy do komunikacji w czasie rzeczywistym za pomocą wideo, dźwięku i danych bez konieczności instalowania wtyczek. Potrzeba była realna:

  • Wiele usług internetowych korzystało z RTC, ale wymagało pobierania, aplikacji natywnych lub wtyczek. Były to m.in. Skype, Facebook i Hangouts.
  • Pobieranie, instalowanie i aktualizowanie wtyczek jest skomplikowane, podatne na błędy i uciążliwe.
  • Wtyczki są trudne do wdrażania, debugowania, rozwiązywania problemów, testowania i utrzymywania, a także mogą wymagać licencjonowania i integracji ze złożoną, kosztowną technologią. Często trudno jest w ogóle przekonać użytkowników do zainstalowania wtyczek.

Głównymi założeniami projektu WebRTC są: otwarty kod źródłowy, bezpłatność, standaryzacja, wbudowanie w przeglądarki internetowe i większa wydajność niż w przypadku istniejących technologii.

Gdzie teraz jesteśmy?

WebRTC jest używany w różnych aplikacjach, takich jak Google Meet. WebRTC został też zintegrowany z WebKitGTK+ i natywnymi aplikacjami 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 przez przeglądarki Chrome, Safari, Firefox, Edge i Opera.

getUserMedia: przykłady i kod znajdziesz w próbkach WebRTC lub wypróbuj świetne przykłady Chrisa Wilsona, które wykorzystują getUserMedia jako dane wejściowe dla dźwięku w internecie.

RTCPeerConnection: Proste demo i w pełni funkcjonalną aplikację do czatu wideo znajdziesz odpowiednio w przykładowych połączeniach WebRTC i appr.tc. Ta aplikacja korzysta z adapter.js, czyli JavaScriptowego shimu utrzymywanego przez Google przy pomocy społeczności WebRTC, który ukrywa różnice między przeglądarkami i zmiany specyfikacji.

RTCDataChannel: Aby zobaczyć to w praktyce, zapoznaj się z przykładami WebRTC i sprawdź jedną z demonstracji kanału danych.

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

Pierwsze połączenie WebRTC

Aplikacje WebRTC muszą wykonywać kilka czynności:

  • uzyskiwać strumieniowy dźwięk, obraz lub inne dane;
  • Pobieranie informacji o sieci, takich jak adresy IP i porty, oraz wymienianie ich z innymi klientami WebRTC (zwanymi węzłami), aby umożliwić połączenie nawet przez NAT i zapory sieciowe.
  • Koordynowanie komunikacji sygnalizacyjnej w celu zgłaszania błędów oraz rozpoczynania i zamykania sesji.
  • 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ć strumieniowo dane, WebRTC implementuje te interfejsy API:

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

(Szczegółowe omówienie aspektów sieciowych i sygnalizacyjnych WebRTC znajdziesz w dalszej części tego artykułu).

MediaStream API (znany też jako getUserMedia API)

MediaStream Interfejs API reprezentuje zsynchronizowane strumienie multimediów. Na przykład strumień pochodzący z wejścia kamery i mikrofonu ma zsynchronizowane ścieżki wideo i audio. (Nie myl MediaStreamTrack z elementem <track>, który jest czymś zupełnie innym).

Prawdopodobnie najłatwiejszym sposobem zrozumienia interfejsu MediaStream API jest przyjrzenie się mu w praktyce:

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

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

Metoda getUserMedia() przyjmuje parametr obiektu MediaStreamConstraints i zwraca obiekt Promise, który jest rozpoznawany jako obiekt MediaStream.

Każdy MediaStream ma label, np. 'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'. Tablica obiektów MediaStreamTrack jest zwracana przez metody getAudioTracks()getVideoTracks().

W przypadku przykładu getUserMedia funkcja stream.getAudioTracks() zwraca pustą tablicę (ponieważ nie ma dźwięku), a funkcja stream.getVideoTracks() zwraca tablicę z jednym elementem MediaStreamTrack reprezentującym strumień z kamery internetowej (zakładając, że jest ona podłączona i działa). Każdy MediaStreamTrack ma rodzaj ('video' lub 'audio'), label (np. 'FaceTime HD Camera (Built-in)') i reprezentuje co najmniej 1 kanał audio lub wideo. W tym przypadku jest tylko jedna ś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 pobiera strumienie z przedniego i tylnego aparatu, mikrofonu oraz aplikacja udostępniająca ekran.

Element MediaStream można dołączyć do elementu wideo, ustawiając srcObject atrybut. Wcześniej można to było zrobić, ustawiając atrybut src na adres URL obiektu utworzony za pomocą URL.createObjectURL(), ale ta metoda została wycofana.

getUserMedia może być też używany jako węzeł wejściowy interfejsu 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 mogą też zawierać getUserMedia. Dodanie do pliku manifestu audioCapture lub videoCapture uprawnień umożliwia żądanie i przyznawanie uprawnień tylko raz podczas instalacji. Użytkownik nie jest już proszony o pozwolenie na dostęp do aparatu lub mikrofonu.

Uprawnienia do getUserMedia() wystarczy przyznać tylko raz. Za pierwszym razem w pasku informacji przeglądarki wyświetli się przycisk Zezwól. Dostęp HTTP do getUserMedia() został wycofany przez Chrome pod koniec 2015 r., ponieważ został sklasyfikowany jako funkcja o dużych możliwościach.

Celem jest potencjalne włączenie MediaStream dla dowolnego źródła danych strumieniowych, nie tylko kamery lub mikrofonu. Umożliwi to przesyłanie strumieniowe z przechowywanych danych lub dowolnych źródeł danych, takich jak czujniki lub inne dane wejściowe.

getUserMedia() w pełni rozwija swoje możliwości w połączeniu z innymi interfejsami API i bibliotekami JavaScriptu:

  • Webcam Toy to aplikacja do fotobudki, która wykorzystuje WebGL do dodawania do zdjęć dziwnych i wspaniałych efektów. Zdjęcia można udostępniać lub zapisywać lokalnie.
  • FaceKat to gra śledząca twarz, która została stworzona przy użyciu biblioteki headtrackr.js.
  • ASCII Camera używa interfejsu Canvas API do generowania obrazów ASCII.
Obraz ASCII wygenerowany przez idevelop.ro/ascii-camera
gUM ASCII art!

Ograniczenia

Ograniczenia można stosować do ustawiania wartości rozdzielczości wideo w przypadku getUserMedia(). Umożliwia to też obsługę innych ograniczeń, takich jak format obrazu, tryb kamery (przednia lub tylna), liczba klatek na sekundę, wysokość i szerokość oraz metoda applyConstraints().

Przykład znajdziesz w przykładowych aplikacjach WebRTCgetUserMedia: wybierz rozdzielczość.

Ustawienie niedozwolonej wartości ograniczenia powoduje zwrócenie wartości DOMException lub OverconstrainedError, np. jeśli żądana rozdzielczość jest niedostępna. Aby zobaczyć, jak to działa, zapoznaj się z przykładowymi aplikacjami WebRTCgetUserMedia: wybierz rozdzielczość, aby zobaczyć wersję demonstracyjną.

Przechwytywanie ekranu i karty

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

Możesz też użyć zrzutu ekranu jako MediaStream źródła w Chrome, korzystając z eksperymentalnego ograniczenia chromeMediaSource. Pamiętaj, że przechwytywanie ekranu wymaga protokołu HTTPS i powinno być używane tylko na potrzeby programowania, ponieważ jest włączane za pomocą flagi wiersza poleceń, jak wyjaśniono w tym poście.

Sygnalizacja: kontrola sesji, sieć i informacje o multimediach

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

Zamiast tego deweloperzy aplikacji WebRTC mogą wybrać dowolny protokół przesyłania wiadomości, np. SIP lub XMPP, oraz dowolny odpowiedni dwukierunkowy kanał komunikacji. W przykładzie appr.tc jako mechanizmu sygnalizacyjnego użyto XHR i interfejsu Channel API. codelab korzystają z Socket.io działającego na serwerze Node.

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

  • Komunikaty sterujące sesją: służą do inicjowania lub zamykania komunikacji oraz zgłaszania błędów.
  • Konfiguracja sieci: jaki jest adres IP i port Twojego komputera dla świata zewnętrznego?
  • Możliwości multimedialne: jakie kodeki i rozdzielczości są obsługiwane przez Twoją przeglądarkę i przeglądarkę, z którą chcesz się komunikować?

Zanim rozpocznie się transmisja peer-to-peer, wymiana informacji za pomocą sygnalizacji musi zostać zakończona.

Załóżmy, że Alicja chce się skontaktować z Robertem. Oto przykładowy kod ze specyfikacji W3C WebRTC, który pokazuje proces sygnalizacji w działaniu. Kod zakłada istnienie mechanizmu sygnalizacyjnego utworzonego w metodzie createSignalingChannel(). Pamiętaj też, że w przypadku przeglądarek Chrome i Opera przed symbolem RTCPeerConnection jest obecnie dodawany prefiks.

// 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ą struktury ICE).

  1. Alicja tworzy RTCPeerConnection obiekt z onicecandidate modułem obsługi, który jest uruchamiany, gdy dostępne stają się kandydaci do sieci.
  2. Alicja wysyła do Roberta zserializowane dane kandydata za pomocą dowolnego kanału sygnalizacyjnego, np. WebSocket lub innego mechanizmu.
  3. Gdy Bob otrzyma od Alicji wiadomość z kandydatem, wywoła funkcję addIceCandidate, aby dodać kandydata do opisu zdalnego elementu równorzędnego.

Klienci WebRTC (zwani też peerami, czyli w tym przykładzie Alicją i Bobem) muszą też ustalić i wymienić lokalne i zdalne informacje o multimediach audio i wideo, takie jak rozdzielczość i możliwości kodeka. Sygnalizacja wymiany informacji o konfiguracji multimediów odbywa się przez wymianę oferty i odpowiedzi za pomocą protokołu opisu sesji (SDP):

  1. Alicja stosuje metodę RTCPeerConnection createOffer(). Zwracana wartość to RTCSessionDescription – lokalny opis sesji Alicji.
  2. W wywołaniu zwrotnym Alicja ustawia opis lokalny za pomocą setLocalDescription(), a następnie wysyła ten opis sesji do Boba przez kanał sygnalizacyjny. Pamiętaj, że funkcja RTCPeerConnection nie zacznie zbierać kandydatów, dopóki nie zostanie wywołana funkcja setLocalDescription(). Jest to skodyfikowane w projekcie JSEP IETF.
  3. Robert ustawia opis przesłany przez Alicję jako opis zdalny za pomocą setRemoteDescription().
  4. Bob uruchamia metodę RTCPeerConnection createAnswer(), przekazując jej zdalny opis, który otrzymał od Alicji, aby można było wygenerować lokalną sesję zgodną z jej sesją. Wywołanie zwrotne createAnswer() otrzymuje wartość RTCSessionDescription. Bob ustawia ten opis jako lokalny i wysyła go do Alicji.
  5. Gdy Alicja otrzyma opis sesji Roberta, ustawi go jako opis zdalny za pomocą setRemoteDescription.
  6. Ping!

Obiekty RTCSessionDescription to obiekty binarne zgodne z protokołem opisu sesji (SDP). Zserializowany obiekt SDP 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ć zakończone, zanim będzie można rozpocząć strumieniowe przesyłanie dźwięku i obrazu między urządzeniami.

Opisana wcześniej architektura oferty/odpowiedzi jest nazywana JavaScript Session Establishment Protocol, czyli JSEP. (Świetną animację wyjaśniającą proces sygnalizacji i przesyłania strumieniowego znajdziesz w filmie demonstracyjnym firmy Ericsson dotyczącym pierwszej implementacji WebRTC).

Schemat architektury JSEP
Architektura JSEP

Po pomyślnym zakończeniu procesu sygnalizacji dane mogą być przesyłane strumieniowo bezpośrednio między dzwoniącym a odbiorcą połączenia lub, w razie niepowodzenia, przez pośredniczący serwer przekaźnikowy (więcej informacji na ten temat znajdziesz w dalszej części artykułu). Strumieniowanie to zadanie RTCPeerConnection.

RTCPeerConnection

RTCPeerConnection to komponent WebRTC, który odpowiada za stabilną i wydajną komunikację danych przesyłanych strumieniowo między urządzeniami.

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

Schemat architektury WebRTC
Architektura WebRTC (na podstawie webrtc.org)

Z punktu widzenia JavaScriptu najważniejsze jest to, że RTCPeerConnection chroni programistów stron internetowych przed wieloma złożonymi problemami, które kryją się pod spodem. Kodeki i protokoły używane przez WebRTC wykonują ogromną pracę, aby umożliwić komunikację w czasie rzeczywistym, nawet w przypadku zawodnych sieci:

  • Ukrywanie utraty pakietów
  • Usuwanie echa
  • Dostosowywanie przepustowości
  • Dynamiczne buforowanie zakłóceń
  • Automatyczna kontrola wzmocnienia
  • Redukcja i eliminowanie szumów
  • Czyszczenie obrazu

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

RTCPeerConnection bez serwerów

Poniższy kod pochodzi z przykładowego połączenia równorzędnego WebRTC, które zawiera lokalne i zdalne RTCPeerConnection (oraz lokalne i zdalne wideo) na jednej stronie internetowej. Nie jest to zbyt przydatne – osoba dzwoniąca i odbiorca połączenia znajdują się na tej samej stronie – ale wyjaśnia działanie interfejsu RTCPeerConnection API, ponieważ obiekty RTCPeerConnection na stronie mogą wymieniać dane i wiadomości bezpośrednio, bez konieczności korzystania z mechanizmów sygnalizacyjnych.

W tym przykładzie pc1 oznacza lokalnego użytkownika (dzwoniącego), a pc2 – zdalnego użytkownika (odbiorcę połączenia).

Rozmówca

  1. Utwórz nowy RTCPeerConnection i dodaj strumień z getUserMedia():```js// Servers is an optional configuration file. (Więcej informacji o TURN i STUN znajdziesz w dalszej części artykułu). pc1 = new RTCPeerConnection(servers); // ... localStream.getTracks().forEach((track) => { pc1.addTrack(track, localStream); });
  1. Utwórz ofertę i ustaw ją jako opis lokalny dla pc1 oraz jako opis zdalny dla pc2. Można to zrobić bezpośrednio w kodzie bez używania sygnalizacji, ponieważ osoba dzwoniąca i odbierająca znajdują się na tej samej stronie:js pc1.setLocalDescription(desc).then(() => { onSetLocalSuccess(pc1); }, onSetSessionDescriptionError ); trace('pc2 setRemoteDescription start'); pc2.setRemoteDescription(desc).then(() => { onSetRemoteSuccess(pc2); }, onSetSessionDescriptionError );

Callee

  1. Utwórz pc2 i po dodaniu strumienia z pc1 wyświetl go w elemencie wideo:js pc2 = new RTCPeerConnection(servers); pc2.ontrack = gotRemoteStream; //... function gotRemoteStream(e){ vid2.srcObject = e.stream; }

RTCPeerConnection Interfejs API i serwery

W rzeczywistości WebRTC potrzebuje serwerów, nawet prostych, więc może się zdarzyć, że:

  • Użytkownicy odkrywają się nawzajem i wymieniają informacje o sobie, np. imiona i nazwiska.
  • Aplikacje klienckie WebRTC (węzły) wymieniają informacje o sieci.
  • Urządzenia wymieniają dane o mediach, takie jak format i rozdzielczość wideo.
  • Aplikacje klienckie WebRTC przechodzą przez bramy NAT i zapory sieciowe.

Innymi słowy, WebRTC potrzebuje 4 rodzajów funkcji po stronie serwera:

  • Wyszukiwanie użytkowników i komunikacja z nimi
  • Wysyłanie sygnałów
  • Omijanie NAT/zapory sieciowej
  • serwery przekaźnikowe w przypadku niepowodzenia komunikacji peer-to-peer,

Zagadnienia związane z przechodzeniem przez NAT, sieciami peer-to-peer i wymaganiami dotyczącymi tworzenia aplikacji serwera do wykrywania użytkowników i sygnalizacji wykraczają poza zakres tego artykułu. Protokół STUN i jego rozszerzenie TURN są używane przez platformę ICE, aby umożliwić RTCPeerConnection radzenie sobie z przechodzeniem przez NAT i innymi nieprzewidywalnymi zachowaniami sieci.

ICE to platforma do łączenia się z innymi użytkownikami, np. z dwoma klientami czatu wideo. Początkowo ICE próbuje połączyć urządzenia bezpośrednio z jak najmniejszym opóźnieniem za pomocą protokołu UDP. Serwery STUN mają w tym procesie jedno zadanie: umożliwić urządzeniu za NAT-em ustalenie jego publicznego adresu i portu. (Więcej informacji o STUN i TURN znajdziesz w artykule Tworzenie usług backendu potrzebnych do działania aplikacji WebRTC).

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

Jeśli UDP nie działa, ICE próbuje TCP. Jeśli połączenie bezpośrednie się nie powiedzie – w szczególności z powodu przechodzenia przez NAT i zapory sieciowe w firmach – ICE używa pośredniego (przekazującego) serwera TURN. Inaczej mówiąc, ICE najpierw używa STUN z UDP do bezpośredniego łączenia urządzeń, a jeśli to się nie uda, przełącza się na serwer przekaźnikowy TURN. Wyrażenie znajdowanie kandydatów odnosi się do procesu znajdowania interfejsów sieciowych i portów.

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

Więcej informacji o ICE, STUN i TURN znajdziesz w prezentacji WebRTC z Google I/O 2013, którą przygotował inżynier WebRTC Justin Uberti. (Przykłady implementacji serwerów TURN i STUN znajdziesz na slajdach prezentacji).

Prosty klient do czatu wideo

Dobrym miejscem do wypróbowania WebRTC, wraz z sygnalizacją i przechodzeniem przez NAT/firewall za pomocą serwera STUN, jest demonstracyjna aplikacja do czatu wideo na stronie appr.tc. Ta aplikacja używa pliku adapter.js, czyli shimu, który chroni aplikacje przed zmianami specyfikacji i różnicami w prefiksie.

Kod jest celowo rozbudowany w zakresie rejestrowania. Sprawdź konsolę, aby poznać kolejność zdarzeń. Poniżej znajdziesz szczegółowe omówienie kodu.

Topologie sieci

Obecnie WebRTC obsługuje tylko komunikację 1:1, ale można go używać w bardziej złożonych scenariuszach sieciowych, np. z wieloma uczestnikami komunikującymi się ze sobą bezpośrednio lub za pomocą jednostki sterującej wieloma punktami (MCU), czyli serwera, który może obsługiwać dużą liczbę uczestników i wykonywać selektywne przekazywanie strumieni oraz miksowanie lub nagrywanie dźwięku i obrazu.

Diagram topologii MCU
Przykład topologii jednostki sterującej połączeniem wielopunktowym

Wiele istniejących aplikacji WebRTC umożliwia komunikację tylko między przeglądarkami internetowymi, 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 na licencji open source klienta SIP sipml5, który został stworzony przy użyciu WebRTC i WebSocket. Umożliwia on (między innymi) prowadzenie rozmów wideo między przeglądarkami a aplikacjami działającymi na iOS i Androidzie. Podczas konferencji Google I/O firmy Tethr i Tropo zaprezentowały platformę do komunikacji w sytuacjach kryzysowych w walizce, która wykorzystuje komórkę OpenBTS do umożliwienia komunikacji między telefonami komórkowymi a komputerami za pomocą WebRTC. Rozmowy telefoniczne bez operatora!

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

RTCDataChannel API<

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

Interfejs RTCDataChannel API umożliwia wymianę dowolnych danych w trybie peer-to-peer z małymi opóźnieniami i dużą przepustowością. Przykłady aplikacji jednostronicowych i informacje o tym, jak utworzyć prostą aplikację do przesyłania plików, znajdziesz w przykładowych aplikacjach WebRTCćwiczeniach z programowania poświęconych WebRTC.

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

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

Interfejs API ma kilka funkcji, które pozwalają w pełni wykorzystać możliwości RTCPeerConnection i umożliwiają elastyczną komunikację peer-to-peer:

  • Wykorzystanie konfiguracji sesji RTCPeerConnection
  • Wiele równoległych kanałów z określaniem priorytetów
  • Semantyka niezawodnego i niezaufanego dostarczania
  • Wbudowane zabezpieczenia (DTLS) i kontrola przeciążenia
  • Możliwość korzystania z dźwiękiem lub obrazem albo bez nich

Składnia jest celowo podobna do składni 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 szybsze niż WebSocket, nawet jeśli wymagany jest serwer przekazujący (TURN), gdy nie uda się przekroczyć zapory sieciowej i NAT.

RTCDataChannel jest dostępny w przeglądarkach Chrome, Safari, Firefox, Opera i Samsung Internet. Gra Cube Slam używa interfejsu API do przekazywania informacji o stanie gry. Zagraj ze znajomym lub z niedźwiedziem. Innowacyjna platforma Sharefest umożliwiała udostępnianie plików za pomocą RTCDataChannel, a peerCDN pokazywała, jak WebRTC może umożliwiać dystrybucję treści peer-to-peer.

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

Bezpieczeństwo

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

  • Nieszyfrowane multimedia lub dane mogą zostać przechwycone między przeglądarkami albo 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ą być instalowane wraz z pozornie nieszkodliwą wtyczką lub aplikacją.

WebRTC ma kilka funkcji, które pozwalają uniknąć tych problemów:

  • Implementacje WebRTC korzystają z bezpiecznych protokołów, takich jak DTLSSRTP.
  • Szyfrowanie jest obowiązkowe w przypadku wszystkich komponentów WebRTC, w tym mechanizmów sygnalizacyjnych.
  • 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 za każdym razem, gdy aktualizowana jest przeglądarka.
  • Dostęp do aparatu i mikrofonu musi być przyznany wyraźnie, a gdy są one włączone, interfejs użytkownika wyraźnie to pokazuje.

Pełne omówienie bezpieczeństwa strumieniowych treści multimedialnych wykracza poza zakres tego artykułu. Więcej informacji znajdziesz w proponowanej architekturze zabezpieczeń WebRTC, którą opracowała grupa IETF.

Podsumowanie

Interfejsy API i standardy WebRTC mogą zdemokratyzować i zdecentralizować narzędzia do tworzenia treści i komunikacji, w tym telefonię, gry, produkcję wideo, tworzenie muzyki i zbieranie wiadomości.

Technologia nie może być bardziej przełomowa.

Jak napisał bloger Phil Edholm: „WebRTC i HTML5 mogą potencjalnie umożliwić taką samą transformację w komunikacji w czasie rzeczywistym, jaką pierwotna przeglądarka umożliwiła w przypadku informacji”.

Narzędzia dla programistów

Więcej informacji

Standardy i protokoły

Podsumowanie obsługi WebRTC

Interfejsy API MediaStreamgetUserMedia

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

RTCPeerConnection interfejs API

  • Chrome na komputerze w wersji 20 lub nowszej; Chrome na Androida w wersji 29 lub nowszej (bez flag)
  • Opera 18 i nowsze (domyślnie włączone); Opera na Androida 20 i nowsze (domyślnie włączone)
  • Firefox 22 i nowsze (włączone domyślnie)
  • Microsoft Edge 16 i nowsze wersje
  • Safari w wersji 11.2 lub nowszej na iOS oraz w wersji 11.1 lub nowszej na macOS
  • Samsung Internet 4 lub nowsza,

RTCDataChannel interfejs API

  • Wersja eksperymentalna w Chrome 25, ale bardziej stabilna (i współpracująca z Firefoxem) w Chrome 26 i nowszych wersjach; Chrome na Androida 29 i nowsze wersje
  • Stabilna wersja (z możliwością współpracy z Firefoxem) w Operze 18 i nowszych oraz Operze na Androida 20 i nowszych.
  • Firefox 22 i nowsze (włączone domyślnie)

Szczegółowe informacje o obsłudze interfejsów API na różnych platformach, np. getUserMediaRTCPeerConnection, znajdziesz na stronach caniuse.comChrome Platform Status.

Natywne interfejsy API dla RTCPeerConnection są też dostępne w dokumentacji na webrtc.org.