Wprowadzenie do WebRTC

WebRTC to nowy front długiej wojny o otwartą i niezakłóconą sieć.

Brendan Eich, wynalazca JavaScriptu

Komunikacja w czasie rzeczywistym bez wtyczek

Wyobraź sobie świat, w którym telefon, telewizor i komputer mogą się ze sobą komunikować na jednej platformie. Wyobraź sobie, że dodanie czatu wideo i funkcji udostępniania danych peer-to-peer do aplikacji internetowej jest proste. To wizja WebRTC.

Chcesz spróbować? WebRTC jest dostępny na komputerach i urządzeniach mobilnych w przeglądarkach Google Chrome, Safari, Firefox i Oper. Najlepiej zacząć od prostej aplikacji do obsługi 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 pozwolić aplikacji na używanie kamery internetowej.
  3. Otwórz adres URL wyświetlany na końcu strony w nowej karcie lub, najlepiej jeszcze, na innym komputerze.

Krótkie wprowadzenie

Nie masz czasu na przeczytanie tego artykułu lub potrzebujesz tylko kodu?

Możesz też od razu przejść do ćwiczenia z programowania WebRTC, czyli szczegółowego przewodnika, który wyjaśnia, jak stworzyć kompletną aplikację do obsługi czatu wideo, która zawiera prosty serwer sygnałów.

Bardzo krótka historia WebRTC

Jednym z ostatnich dużych wyzwań w branży internetowej jest umożliwienie ludzkości komunikacji głosowej i wideo. Jest to w skrócie komunikacji w czasie rzeczywistym, czyli RTC. Funkcja RTC powinna być tak samo naturalna w aplikacji internetowej, jak wpisywanie tekstu w tekście. Bez niego ograniczymy Twoje możliwości wprowadzania innowacji i wypracowywania nowych sposobów interakcji użytkowników.

W przeszłości RTC było podmiotem korporacyjnym i skomplikowanym, które wymagało posiadania kosztownych technologii audio i wideo, a także posiadania licencji i własnych rozwiązań. Integracja technologii RTC z dotychczasowymi treściami, danymi i usługami było trudne i czasochłonne, zwłaszcza w internecie.

Czat wideo w Gmailu stał się popularny w 2008 roku, a w 2011 roku firma Google wprowadziła Hangouts, która używa Talk (tak samo jak Gmail). Firma Google wykupiła GIPS, firmę, która opracowała wiele komponentów wymaganych przez RTC, takich jak kodeki i techniki usuwania echa. Aby osiągnąć konsensus branżowy, firma Google udostępniła technologie opracowane przez GIPS w ramach licencji open source oraz współpracuje z odpowiednimi instytucjami zajmującymi się standardami w Internet Engineering Task Force (IETF) i World Wide Web Consortium (W3C). W maju 2011 roku firma Ericsson zbudowała pierwszą implementację WebRTC.

WebRTC zaimplementuje otwarte standardy komunikacji wideo, audio i danych w czasie rzeczywistym bez wtyczek. Potrzeby były realne:

  • Wiele usług internetowych korzystało z RTC, ale wymagało pobierania, aplikacji natywnych lub wtyczek. Obejmuje to aplikacje takie jak Skype, Facebook i Hangouts.
  • Pobieranie, instalowanie i aktualizowanie wtyczek jest skomplikowane, podatne na błędy i irytujące.
  • Wtyczki są trudne we wdrażaniu, debugowaniu, rozwiązywaniu problemów, testowaniu i utrzymywaniu oraz mogą wymagać licencjonowania i integracji ze złożoną, droginą technologią. Często trudno jest przekonać ludzi do instalowania wtyczek.

Głównym założeniem projektu WebRTC jest to, że jego interfejsy API powinny być typu open source, bezpłatne, ustandaryzowane, wbudowane w przeglądarki internetowe i bardziej wydajne niż istniejące technologie.

Co teraz?

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

WebRTC implementuje te 3 interfejsy API: – MediaStream (dawniej getUserMedia) – RTCPeerConnectionRTCDataChannel

Interfejsy API są zdefiniowane w tych 2 specyfikacjach:

Wszystkie 3 interfejsy API są obsługiwane na urządzeniach mobilnych i komputerach przez Chrome, Safari, Firefox, Edge i Operę.

getUserMedia: prezentacje i kod znajdziesz w przykładach z WebRTC lub na niezwykłych przykładach Chrisa Wilsona, w których użyto getUserMedia jako danych wejściowych dla dźwięku w sieci.

RTCPeerConnection: prostą prezentację i w pełni funkcjonalną aplikację do czatu wideo znajdziesz odpowiednio na stronach przykładowe połączenia równorzędne z WebRTC i appr.tc. Ta aplikacja korzysta z pliku adapter.js – podkładki JavaScriptu hostowanej przez Google przy pomocy społeczności WebRTC, aby wyeliminować różnice w przeglądarkach i zmiany specyfikacji.

RTCDataChannel: aby zobaczyć, jak to wygląda w praktyce, zapoznaj się z przykładami z WebRTC i zobacz jedną z wersji demonstracyjnych kanałów danych.

Z ćwiczenia z programowania WebRTC dowiesz się, jak za pomocą wszystkich 3 interfejsów API stworzyć prostą aplikację do czatu wideo i udostępniania plików.

Twój pierwszy WebRTC

Aplikacje WebRTC muszą robić kilka rzeczy:

  • Odtwarzaj strumieniowo dźwięk, obraz lub inne dane.
  • Uzyskuj informacje sieciowe, takie jak adresy IP i porty, oraz wymieniaj je z innymi klientami WebRTC (peerami), aby umożliwić nawiązywanie połączeń nawet przez serwery NAT i zapory sieciowe.
  • Koordynowanie komunikacji w postaci sygnałów, by zgłaszać błędy i rozpoczynać lub zamykać sesje.
  • Wymiana informacji o multimediach i możliwościach klienta, takich jak rozdzielczość i kodeki.
  • Prześlij strumieniowy przekaz audio, wideo lub danych.

Aby pozyskiwać i przekazywać dane strumieniowe, WebRTC implementuje następujące interfejsy API:

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

(W dalszej części artykułu zostanie szczegółowo omówione aspekty sieci i sygnalizacji w WebRTC).

Interfejs API MediaStream (nazywany też getUserMedia API)

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

Prawdopodobnie najprostszym sposobem na zrozumienie interfejsu API MediaStream jest spojrzenie na niego w środowisku naturalnym:

  1. W przeglądarce otwórz przykłady WebRTC getUserMedia.
  2. Otwórz konsolę.
  3. Sprawdź zmienną stream, która jest w zakresie globalnym.

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

Metoda getUserMedia() przyjmuje parametr obiektu MediaStreamConstraints i zwraca Promise, który zwraca wartość obiektu MediaStream.

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

W przypadku przykładu z elementem getUserMedia stream.getAudioTracks() zwraca pustą tablicę (ponieważ nie ma dźwięku) i gdy jest podłączona działająca kamera internetowa, stream.getVideoTracks() zwraca tablicę z jednym elementem MediaStreamTrack reprezentującym strumień z kamery internetowej. Każdy element MediaStreamTrack ma swój rodzaj ('video' lub 'audio'), label (np. 'FaceTime HD Camera (Built-in)') i reprezentuje co najmniej 1 kanał audio lub wideo. W tym przypadku mamy tylko jedną ścieżkę wideo, bez dźwięku, ale łatwo sobie wyobrazić, że mamy do czynienia z innymi, np. aplikacją do obsługi czatu pobierającą strumienie z przedniego aparatu, tylnego aparatu i mikrofonu, a także aplikację udostępniającą ekran.

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

getUserMedia może też służyć jako węzeł wejściowy dla 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 również mogą korzystać z getUserMedia. Dodanie do pliku manifestu uprawnień audioCapture lub videoCapture umożliwia jednorazowe żądanie i przyznawanie uprawnień podczas instalacji. Od tego momentu użytkownik nie zostanie poproszony o zgodę na dostęp do aparatu lub mikrofonu.

Uprawnienie można przyznać użytkownikowi getUserMedia() tylko raz. Przy pierwszym uruchomieniu na pasku informacyjnym przeglądarki pojawia się przycisk Zezwalaj. Dostęp HTTP do getUserMedia() został wycofany przez Chrome pod koniec 2015 roku, ponieważ sklasyfikowano go jako zaawansowaną funkcję.

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

getUserMedia() naprawdę działa w połączeniu z innymi interfejsami API i bibliotekami JavaScript:

  • Webcam Toy to aplikacja do obsługi zdjęć z budki fotograficznej wykorzystująca WebGL, aby dodawać do zdjęć dziwne i wspaniałe efekty, które można udostępniać lub zapisywać lokalnie.
  • FaceKat to gra śledząca twarz stworzoną za pomocą headtrackr.js.
  • Kamera ASCII generuje obrazy ASCII za pomocą interfejsu Canvas API.
Obraz ASCII wygenerowany przez idevelop.ro/ascii-camera
grafika ASCII!

Ograniczenia

Ograniczenia pozwalają ustawić wartości rozdzielczości wideo w getUserMedia(). Umożliwia to też obsługę innych ograniczeń, takich jak format obrazu, tryb nagrywania przedniego lub tylnego aparatu, liczba klatek, wysokość i szerokość oraz metoda applyConstraints().

Przykład: przykłady z WebRTC getUserMedia: wybór rozdzielczości.

Ustawienie niedozwolonej wartości ograniczenia daje DOMException lub OverconstrainedError, jeśli np. żądana rozdzielczość jest niedostępna. Aby zobaczyć, jak to działa, zapoznaj się z przykładami z WebRTC getUserMedia: wybieranie rozdzielczości wersji demonstracyjnej.

Przechwytywanie ekranu i kart

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

Możesz też użyć zrzutu ekranu jako źródła MediaStream w Chrome, używając eksperymentalnego ograniczenia chromeMediaSource. Pamiętaj, że zrzuty ekranu wymagają protokołu HTTPS i powinny być używane wyłącznie do programowania, ponieważ można go włączyć za pomocą flagi wiersza poleceń, jak opisano w tym poście.

Sygnalizacja: kontrola sesji, informacje o sieci i multimediach

WebRTC korzysta z RTCPeerConnection do przesyłania strumieniowego danych między przeglądarkami (nazywanymi też elementami równorzędnymi), ale potrzebuje również mechanizmu koordynowania komunikacji i wysyłania komunikatów sterujących (proces ten jest nazywany sygnalizacją). WebRTC nie określa metod ani protokołów sygnałów. Sygnalizowanie nie jest częścią interfejsu API RTCPeerConnection.

Zamiast tego deweloperzy aplikacji WebRTC mogą wybrać preferowany protokół przesyłania wiadomości, np. SIP lub XMPP, oraz dowolny odpowiedni kanał komunikacji dwupoziomowej (dwukierunkowej). W przykładzie appr.tc używany jest XHR i interfejs Channel API jako mechanizm sygnału. Ćwiczenia z programowania wykorzystują Socket.io uruchomione na serwerze węzłów.

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

  • Komunikaty kontrolne sesji: do inicjowania lub zamykania komunikacji i zgłaszania błędów.
  • Konfiguracja sieci: dla osób z zewnątrz, jaki jest adres IP i port Twojego komputera?
  • Funkcje multimedialne: jakie kodeki i rozdzielczości obsługuje Twoja przeglądarka oraz przeglądarka, z którą chce się komunikować?

Wymiana informacji za pomocą sygnalizacji musi się zakończyć, zanim będzie można rozpocząć transmisję peer-to-peer.

Załóżmy na przykład, że Alicja chce się komunikować z Robertem. Oto przykładowy kod ze specyfikacji WebRTC W3C, który pokazuje, jak działa proces sygnalizacji. Kod zakłada, że istnieje mechanizm sygnalizacji utworzony w metodzie createSignalingChannel(). Pamiętaj również, że w Chrome i Operze przedrostek RTCPeerConnection jest obecnie przedrostek.

// 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 znajdujące kandydatów odnosi się do procesu znajdowania interfejsów sieci i portów za pomocą platformy ICE.

  1. Alicja tworzy obiekt RTCPeerConnection z modułem obsługi onicecandidate, który jest uruchamiany, gdy dostępne są kandydujące sieci.
  2. Alicja wysyła do Roberta zserializowane dane kandydata za pomocą dowolnego kanału sygnałów, którego używają, na przykład WebSocket lub innego mechanizmu.
  3. Gdy Robert dostaje wiadomość od Alicji, którą kandyduje, dzwoniąc do addIceCandidate, aby dodać kandydata do opisu zdalnego podobnego klienta.

Klienty WebRTC (nazywane też peerami lub w tym przykładzie Alicja i Robert) muszą też określać i wymieniać lokalne i zdalne informacje o multimediach audio i wideo, takie jak rozdzielczość czy możliwości kodeka. Wymiana informacji o konfiguracji mediów odbywa się przez wymianę oferty i odpowiedzi za pomocą protokołu SIP (Session description Protocol):

  1. Alicja uruchamia metodę createOffer() RTCPeerConnection. Uzyskany wynik Alicji jest przekazywany do opisu sesji lokalnej Alicji: RTCSessionDescription.
  2. W wywołaniu zwrotnym Alicja ustawia lokalny opis za pomocą funkcji setLocalDescription(), a następnie wysyła opis tej sesji do Roberta za pomocą swojego kanału sygnałów. Pamiętaj, że RTCPeerConnection zacznie zbierać kandydatów dopiero po wywołaniu funkcji setLocalDescription(). Ta opcja jest sformalizowane w wersji roboczej JSEP IETF.
  3. Robert ustawia opis przesłany przez Alicję jako opis zdalny za pomocą setRemoteDescription().
  4. Robert uruchamia metodę RTCPeerConnection createAnswer(), przekazując do niej zdalny opis otrzymany od Alicji, aby można było wygenerować sesję lokalną zgodną z jej. Wywołanie zwrotne createAnswer() jest przekazywane (RTCSessionDescription). Robert ustawia go jako opis lokalny i wysyła go do Alicji.
  5. Kiedy Alicja otrzymuje opis sesji Roberta, ustawia go jako opis zdalny w funkcji setRemoteDescription.
  6. Ping!

Obiekty RTCSessionDescription to obiekty blob zgodne z protokołem Session Opis Protocol, 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

Pozyskanie i wymiana informacji o sieci i multimediach może być realizowane jednocześnie, ale oba procesy muszą się zakończyć, zanim będzie można rozpocząć strumieniowe przesyłanie dźwięku i obrazu między elementami równorzędnymi.

Opisana wcześniej architektura ofert/odpowiedzi nosi nazwę JavaScript Session Embedment Protocol (JSEP). (W prezentacji Ericssona z jej pierwszym wdrożeniem WebRTC znajduje się świetna animacja wyjaśniająca proces sygnalizacji i strumieniowej transmisji).

Schemat architektury JSEP
Architektura JSEP

Po pomyślnym zakończeniu procesu sygnalizacji dane mogą być przesyłane strumieniowo bezpośrednio do połączenia peer-to-peer, między rozmówcą i osobą wywołującą oraz – w przypadku niepowodzenia – przez pośredni serwer przekazujący (więcej informacji na ten temat znajdziesz później). Zadaniem strumieniowania jest RTCPeerConnection.

RTCPeerConnection

RTCPeerConnection to komponent WebRTC, który obsługuje stabilną i wydajną komunikację strumieniowych danych między aplikacjami równorzędnymi.

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

Diagram architektury WebRTC
Architektura WebRTC (z webrtc.org)

Z perspektywy JavaScriptu najważniejsze jest, aby zrozumieć, z tego diagramu, że RTCPeerConnection chroni programistów przed niezliczonymi złożonościami, które czai się poniżej. Kodeki i protokoły używane przez WebRTC wykonują mnóstwo pracy, aby umożliwić komunikację w czasie rzeczywistym nawet w niestabilnych sieciach:

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

Poprzedni kod W3C pokazuje uproszczony przykład WebRTC z perspektywy sygnałów. Poniżej znajdziesz przewodniki po dwóch działających aplikacjach WebRTC. Pierwszy to prosty przykład ilustrujący, że RTCPeerConnection, a drugi to w pełni funkcjonalny klient czatu wideo.

RTCPeerConnection bez serwerów

Poniższy kod pochodzi z przykładowego połączenia równorzędnego z WebRTC, które ma na jednej stronie internetowej lokalny i zdalny RTCPeerConnection (oraz lokalny i zdalny film). Nie stanowi to niczego przydatnego – element wywołujący i wywołanie znajdują się na tej samej stronie, ale sposób działania interfejsu RTCPeerConnection API jest trochę bardziej zrozumiały, ponieważ obiekty RTCPeerConnection na stronie mogą wymieniać dane i wiadomości bezpośrednio bez konieczności korzystania z pośrednich mechanizmów sygnalizujących.

W tym przykładzie pc1 reprezentuje lokalnego peera (rozmówcę), a pc2 – zdalnego klienta.

Rozmówca

  1. Utwórz nowy RTCPeerConnection i dodaj strumień z getUserMedia(): ```js // Serwery to opcjonalny plik konfiguracji. (Zobacz dyskusję na temat TURN i STUN później). 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 tego produktu (pc2). Można to zrobić bezpośrednio w kodzie bez użycia sygnalizacji, ponieważ rozmówca i dzwoniący 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 );

Wywołujący

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

Interfejs API RTCPeerConnection plus serwery

W rzeczywistości WebRTC potrzebuje serwerów, nawet takich, które są proste. Dlatego może nastąpić sytuacja:

  • Użytkownicy odkrywają się i wymieniają się informacjami ze świata rzeczywistego, takimi jak imiona i nazwiska.
  • Aplikacje klienckie WebRTC wymieniają informacje o sieci.
  • Grupy porównawcze wymieniają dane o multimediach, takie jak format i rozdzielczość wideo.
  • Aplikacje klienta WebRTC przemierzają bramy NAT i zapory sieciowe.

Inaczej mówiąc, WebRTC potrzebuje 4 typów funkcji po stronie serwera:

  • Odkrywanie i komunikacja z użytkownikami
  • Sygnalizacja
  • Omijanie NAT/zapory sieciowej
  • Serwery przekazujące w przypadku niepowodzenia komunikacji peer-to-peer

Ten artykuł wykracza poza zakres tego artykułu, dotyczący przemierzania NAT, sieci peer-to-peer oraz wymagań dotyczących tworzenia aplikacji serwerowej na potrzeby wykrywania i sygnalizowania użytkowników. Wystarczy powiedzieć, że protokół STUN i jego rozszerzenie, TURN, są używane przez platformę ICE, aby umożliwić RTCPeerConnection radzenie sobie z poruszaniem NAT i innymi zagrożeniami sieci.

ICE to platforma do komunikacji z innymi wydawcami, na przykład 2 klienty czatu wideo. Początkowo ICE próbuje łączyć się równorzędnie bezpośrednio przy najniższym możliwym czasie oczekiwania przez UDP. W tym procesie serwery STUN mają jedno zadanie: umożliwić serwerowi równorzędnemu za NAT-em znalezienie adresu publicznego i portu. Więcej informacji o STUN i TURN znajdziesz w artykule Tworzenie usług backendu niezbędnych dla aplikacji WebRTC.

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

Jeśli UDP nie powiedzie się, ICE spróbuje użyć TCP. Jeśli połączenie bezpośrednie nie powiedzie się – w szczególności z powodu omijania NAT i zapór sieciowych – ICE używa pośredniego (przekaźnika) serwera TURN. Oznacza to, że ICE najpierw używa protokołu STUN z UDP, aby bezpośrednio łączyć się z połączeniami równorzędnymi, a jeśli to się nie uda, przechodzi z powrotem na serwer przekazujący TURN. Wyrażenie znajdujące kandydatów odnosi się do procesu znajdowania interfejsów sieci i portów.

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

Justin Uberti, inżynier WebRTC, przedstawia więcej informacji na temat ICE, STUN i TURN w prezentacji Google I/O WebRTC z 2013 roku. (Slajdy prezentacji zawierają przykłady implementacji serwera TURN i STUN).

Prosty klient czatu wideo

Dobrym miejscem na wypróbowanie WebRTC wraz z sygnalizowaniem i poruszaniem się zaporą sieciową za pomocą serwera STUN jest wersja demonstracyjna czatu wideo dostępna na stronie appr.tc. Ta aplikacja używa pliku adapter.js – podkładki do zabezpieczania aplikacji przed zmianami specyfikacji i różnicami prefiksów.

Kod będzie celowo szczegółowy w logowaniu. Sprawdź kolejność zdarzeń w konsoli. Poniżej znajduje się szczegółowy przewodnik po kodzie.

Topologie sieci

WebRTC, jak obecnie zaimplementowane, obsługuje tylko komunikację 1:1, ale może być używany w bardziej złożonych scenariuszach sieciowych, np. gdy wiele sieci równorzędnych komunikuje się ze sobą bezpośrednio lub przez jednostkę sterowania wielopunktową (MCU), czyli serwer, który może obsłużyć dużą liczbę uczestników i przekazywać selektywne przekazywanie strumienia oraz mieszać lub nagrywać dźwięk i obraz.

Diagram topologii wielopunktowej jednostki sterującej
Przykład topologii jednostki sterującej wielopunktowej

Wiele istniejących aplikacji WebRTC prezentuje tylko komunikację między przeglądarkami internetowymi, ale serwery bram mogą umożliwić aplikacji WebRTC działającą w przeglądarce interakcję z urządzeniami, takimi jak telefony (PSTN) i systemami VOIP. W maju 2012 roku firma Doubango Telecom open source udostępniła klienta SIPml5 SIP oparty na WebRTC i WebSocket, który (między innymi jako potencjalne zastosowania) umożliwia prowadzenie rozmów wideo między przeglądarkami oraz aplikacjami działającymi na iOS i Androida. Na konferencji Google I/O Tethr i Tropo zaprezentowały platformę komunikacji o katastrofie w aktce przy użyciu komórki OpenBTS, która umożliwia komunikację między telefonami z internetem a komputerami przez WebRTC. Komunikacja telefoniczna bez operatora.

Prezentacja Tethr/Tropo na Google I/O 2012
Tethr/Tropo: informacje o katastrofach w aktce

RTCDataChannel API

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

Interfejs RTCDataChannel API umożliwia wymianę dowolnych danych typu peer-to-peer z krótkim czasem oczekiwania i dużą przepustowością. Jednostronicowe wersje demonstracyjne i informacje o tym, jak stworzyć prostą aplikację do przesyłania plików, znajdziesz w odpowiednich przykładach z WebRTC i ćwiczeniach z programowania WebRTC.

Jest wiele potencjalnych przypadków użycia tego interfejsu API, w tym:

  • Gry
  • Aplikacje 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 zapewnić zaawansowaną i elastyczną komunikację peer-to-peer:

  • Wykorzystanie konfiguracji sesji RTCPeerConnection
  • Wiele równoczesnych kanałów z priorytetami
  • Niezawodna i niezawodna semantyka wyświetlania
  • Wbudowane zabezpieczenia (DTLS) i kontrola zatory
  • Możliwość korzystania z dźwięku lub obrazu i bez nich

Składnia jest celowo podobna do składni WebSocket, używając metody send() i zdarzenia 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 zachodzi bezpośrednio między przeglądarkami, więc RTCDataChannel może działać znacznie szybciej niż WebSocket nawet wtedy, gdy wymagany jest serwer przekazujący (TURN) w celu radzenia sobie z zaporami sieciowymi i awarią NAT.

Aplikacja RTCDataChannel jest dostępna w przeglądarkach Chrome, Safari, Firefox, Opera i Samsung Internet. Gra Cube Slam komunikuje stan gry za pomocą interfejsu API. Zagraj ze znajomym lub z niedźwiedziem! Innowacyjna platforma Sharefest umożliwia udostępnianie plików za pomocą RTCDataChannel i peerCDN, dając Ci wgląd w to, jak WebRTC może umożliwić dystrybucję treści w sieci peer-to-peer.

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

Bezpieczeństwo

Aplikacja lub wtyczka do komunikacji w czasie rzeczywistym może zagrażać bezpieczeństwu 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ć dźwięk i obraz bez wiedzy użytkownika.
  • Złośliwe oprogramowanie lub wirusy mogą być instalowane razem z pozornie nieszkodliwą wtyczką lub aplikacją.

WebRTC ma kilka funkcji pozwalających uniknąć tych problemów:

  • Implementacje WebRTC korzystają z bezpiecznych protokołów, takich jak DTLS i SRTP.
  • Szyfrowanie jest obowiązkowe w przypadku wszystkich komponentów WebRTC, w tym mechanizmów sygnałów.
  • WebRTC nie jest wtyczką. Jego komponenty działają w piaskownicy przeglądarki, a nie w osobnym procesie. Komponenty nie wymagają oddzielnej instalacji i są aktualizowane przy każdej aktualizacji przeglądarki.
  • Dostęp do kamery i mikrofonu należy jednoznacznie przyznać, a gdy kamera lub mikrofon są uruchomione, jest to wyraźnie widoczne w interfejsie użytkownika.

Pełny opis zabezpieczeń strumieniowania multimediów nie został uwzględniony w tym artykule. Więcej informacji znajdziesz w proponowanej architekturze zabezpieczeń WebRTC przez IETF.

Podsumowanie

Interfejsy API i standardy WebRTC mogą zdemokratyzować i zdecentralizować narzędzia do tworzenia treści i komunikacji, w tym do telefonowania, grania, produkcji filmów, tworzenia muzyki i zbierania wiadomości.

Technologia nie jest raczej uciążliwa niż to.

Jak mówi bloger Phil Edholm, „potencjalnie korzystanie z WebRTC i HTML5 mogłoby umożliwić komunikację w czasie rzeczywistym w taki sam sposób, jak oryginalna przeglądarka w celu wyszukiwania informacji”.

Narzędzia dla programistów

Więcej informacji

Standardy i protokoły

Podsumowanie obsługi WebRTC

Interfejsy API MediaStream i getUserMedia

  • Chrome na komputery w wersji 18.0.1008 lub nowszej; Chrome na Androida w wersji 29 lub nowszej
  • Opera w wersji 18 lub nowszej; Opera na Androida w wersji 20 lub nowszej
  • Opera 12, Opera Mobile 12 (na podstawie silnika Presto)
  • Firefox w wersji 17 lub nowszej,
  • Microsoft Edge 16 lub nowsza
  • Safari w wersji 11.2 lub nowszej w systemie iOS oraz 11.1 lub nowszej w systemie macOS.
  • UC 11.8 lub nowsze na urządzeniach z Androidem
  • Samsung Internet 4 i nowsze wersje,

RTCPeerConnection interfejs API

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

RTCDataChannel interfejs API

  • Wersja eksperymentalna w Chrome 25, ale stabilniejsza (i dzięki interoperacyjności Firefoksa) w Chrome 26 i nowszych wersjach; Chrome na Androida w wersji 29 lub nowszej
  • wersja stabilna (z funkcją interoperacyjności przeglądarki Firefox) z przeglądarką Opera 18 lub nowszą; Opera w wersji 20 lub nowszej;
  • Firefox 22 i nowsze (domyślnie włączone)

Szczegółowe informacje o obsłudze interfejsów API na wielu platformach, takich jak getUserMedia i RTCPeerConnection, znajdziesz na stronach caniuse.com i Chrome Platform Status.

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