Aus Linux-Magazin 08/2013

Web RTC: Videotelefonie ohne Browser-Plugin

© Danil Chepko, 123RF.com

Das Protokoll Web RTC macht den Browser zur Kommunikationszentrale: Ganz ohne Plugins erledigt er Videochats oder Datei-Übertragungen über Peer-to-Peer-Verbindungen.

Wer sich bisher mit Videotelefonie und anderen Formen der Echtzeitkommunikation beschäftigte, landete fast automatisch bei Skype und Konsorten. Im Webbrowser ging Vergleichbares nur mit Flash- oder Java-Plugins. In der jüngsten Browsergeneration hat sich das geändert: Ergänzend zu HTML 5 bringt Web RTC (Web Realtime Communication, [1]) die Echtzeitkommunikation nativ in den Webbrowser.

Mit dem Protokoll lassen sich Videochats durchführen oder Anwendungsdaten austauschen. Die Kommunikation erfolgt dabei direkt von Browser zu Browser. Dieser Artikel zeigt anhand eines Internet-Videochats, wie man Web RTC im Zusammenspiel von HTML, Javascript, CSS und Node.js anwendet.

Web RTC tragen die Browserhersteller Google, Mozilla und Opera gemeinsam. Obwohl die Spezifikation noch nicht abgeschlossen ist, unterstützen die Webbrowser Google Chrome und Mozilla Firefox ab Version 22 Web RTC bereits weitestgehend. Es handelt sich um einen freien Standard, der in einer Reihe von Dokumenten [2] der IETF beschrieben ist. Dem W3C liegt ein Entwurf für eine Programmierschnittstelle [3] für Web RTC im Browser vor.

Neue Möglichkeiten

Die Möglichkeiten von Web RTC in Kombination mit weiteren Technologien demonstriert Mozilla in einem Video [4]. Es zeigt einen Video-Anruf von einem Firefox-Browser auf ein Mobiltelefon über das öffentliche Telefonnetz. Web RTC ist allerdings nicht unumstritten: Microsoft etwa hält es für zu kompliziert und hat mit UC-RTC [5] einen eigenen Entwurf zur Echtzeitkommunikation in Browsern vorgelegt.

Abbildung 1 zeigt den Datenfluss während einer Web-RTC-Sitzung. Anwendungs- und Konfigurationsdaten gehen getrennte Wege: Der Server fungiert als Router, der Konfigurationsdaten wie beispielsweise IP-Adressen oder Angaben zu Video- und Audioformaten von dem einem Browser entgegennimmt und an den anderen weiterleitet. Web RTC nennt diesen Prozess Signaling. Zur Übertragung der Konfigurationsdaten bieten sich die Protokolle Websocket oder HTTP an, zu ihrer Strukturierung die Javascript Object Notation (Json).

Abbildung 1: Konfigurations- und Anwendungsdaten gehen bei Web RTC getrennte Wege: Die Konfigurationsdaten vermittelt ein zwischengeschalteter Server, die Anwendungsdaten überträgt das Protokoll von Peer zu Peer.

Abbildung 1: Konfigurations- und Anwendungsdaten gehen bei Web RTC getrennte Wege: Die Konfigurationsdaten vermittelt ein zwischengeschalteter Server, die Anwendungsdaten überträgt das Protokoll von Peer zu Peer.

Nach dem Austausch der IP-Adressen über den Signaling-Server bauen die Browser untereinander wie in Abbildung 1 eine Peer-to-Peer-Verbindung auf. Diese überträgt die Anwendungsdaten mittels UDP oder TCP direkt zwischen den Browsern, was Zeit und Datenverkehr spart.

Der Weg durchs Netz

Beim Verbindungsaufbau überwindet Web RTC zwischengeschaltete NAT-Router (Network Address Translation) oder Firewalls durch den Einsatz von ICE (Interactive Connectivity Establishment, [6]). ICE beschafft taugliche IP-Adressen und Ports für das Versenden und Empfangen von Daten über Peer-to-Peer-Verbindungen.

Zunächst versucht ICE mittels STUN (Session Traversal Utilities for NAT, [7]) IP-Adresse und Port zu ermitteln. Dazu sendet es eine Nachricht an einen öffentlichen STUN-Server und erhält die Absenderadresse zurück. Verbietet aber beispielsweise der NAT-Router STUN, so kommen IP-Adresse und Port von einem Relayserver im Internet mittels TURN (Traversal Using Relays around NAT, [8]) zur Anwendung. ICE meldet die geeigneten IP-Adressen und Ports an Web RTC, indem es das Ereignis »onicecandidate« auslöst. Die Daten sind zusammen mit dem zu verwendenden Protokoll – UDP oder TCP – in ein Objekt vom Typ ICE-Kandidat verpackt.

Beispielanwendung

Die folgende Beispielanwendung verwendet Web RTC im Browser, um einen Videochat umzusetzen. Dabei nimmt der Webbrowser das Bild der Webcam und den Ton eines Mikrofons und leitet sie per Web RTC an einen zweiten Browser weiter. Die Clientanwendung setzt dieser Artikel in HTML, CSS und Javascript um. Zum Implementieren des Signaling-Servers dient Node.js.

Das Beispiel beschränkt sich zunächst auf den Browser Firefox, lässt sich aber auch auf Chrome übertragen. Abbildung 2 zeigt die Beispielanwendung beim Einsatz in Firefox auf Ubuntu Linux. Die Gegenstelle verwendet den freien Browser auf Windows 7.

Abbildung 2: Saitenblicke: Links das Bild der lokalen Webcam, rechts das der entfernten.

Abbildung 2: Saitenblicke: Links das Bild der lokalen Webcam, rechts das der entfernten.

Mit der Programmierschnittstelle »getUserMedia()« [9] bieten die aktuellen Versionen der Browser Firefox, Chrome und Opera Zugriff auf Webcam und Mikrofon des lokalen Rechners, fragen den Anwender aber in einem Popup zuvor um Erlaubnis. Web RTC vereint die Video- und Audioströme wie in Abbildung 3 zu einem Medienstrom, um sie zu verarbeiten. Ein Medienstrom kann beliebig viele Video- und Audiospuren aufnehmen, eine Audiospur umfasst zudem zwei Stereokanäle.

Abbildung 3: Der Medienstrom bündelt Video- und Audioströme zur Verwendung in HTML-Video- und Audio-Elementen sowie in Peer-to-Peer-Verbindungen.

Abbildung 3: Der Medienstrom bündelt Video- und Audioströme zur Verwendung in HTML-Video- und Audio-Elementen sowie in Peer-to-Peer-Verbindungen.

Video- und Audiosignale

Die Listings 1 bis 3 demonstrieren, wie man das Bild der lokalen Webcam in ein HTML-Dokument einbindet (Abbildung 4). Das HTML-Dokument in Listing 1 referenziert im Kopfbereich (Zeilen 3 bis 4) zunächst zwei Javascript-Dateien: Die Datei »core.js« enthält Funktionen für mehrere Beispiele in diesem Artikel, »mediastream.js« die für das aktuelle. Im Körper des HTML-Dokuments aus Listing 1 wartet in Zeile 8 das Video-Element mit der ID »local« auf die Verbindung mit einem Medienstrom.

Listing 1

HTML mit Video-Element

01 <html>
02 <head>
03   <script src="js/core.js"></script>
04   <script src="js/mediastream.js"></script>
05 </head>
06 <body>
07   <p id="msg"></p>
08   <video id="local" autoplay></video>
09 </body>
10 </html>
Abbildung 4: Erster Erfolg: Firefox spielt das Bild der lokalen Webcam im Browser ab.

Abbildung 4: Erster Erfolg: Firefox spielt das Bild der lokalen Webcam im Browser ab.

Der Aufruf der Javascript-Methode »mozGetUserMedia()« in Listing 2 (Zeilen 2 bis 8) packt die Bilder der Webcam und den Ton des Mikrofons in einen Medienstrom. Diesen übergibt der Code in den Zeilen 4 bis 6 »stream« als Parameter an die Rückruffunktion. Die Funktion »connectStream()« in Zeile 5 und in Listing 3 spielt den Medienstrom im Video-Element ab. Tritt ein Problem auf, so kommt in Zeile 7 eine Callback-Funktion für den Fehlerfall zum Aufruf. Firefox macht die Angabe einer Rückruffunktion für diesen Fall obligatorisch.

Listing 3

core.js (Auszug)

01 function connectStream(stream, query) {
02   var ele = document.querySelector(query);
03   if (ele) {
04     ele.mozSrcObject = stream;
05     ele.play();
06   }
07 };

Listing 2

mediastream.js

01 document.addEventListener('DOMContentLoaded', function() {
02   navigator.mozGetUserMedia(
03     {video:true, audio:true},
04     function(stream) {
05       connectStream(stream, "#local");
06     },
07     error
08    );
09 });

Listing 3 zeigt die Javascript-Funktion »connectStream()« für den Browser Firefox. Zeile 2 wählt mit der Methode »querySelector()« anhand eines CSS-Selektors ein Element aus dem HTML-Dokument aus – vorzugsweise ein Video-Element. Zeile 4 bindet den Medienstrom über das Firefox-spezifische Attribut »mozSrcObject« an dieses Element.

Aufbau einer Peer-to-Peer-Verbindung

Das nächste Beispiel geht einen Schritt weiter und überträgt den Medienstrom mittels Web RTC. Vor der Übertragung tauschen die beteiligten Browser Sitzungsbeschreibungen im SDP-Format (Session Description Protocol, [10]) aus. Die Beschreibungen enthalten Angaben über die zu übertragenden Medienströme sowie die zu benutzenden IP-Adressen, Ports und Protokolle. Abbildung 5 zeigt die Schritte, die eine Anwendung zum Öffnen einer Peer-to-Peer-Verbindung vollziehen muss. Zunächst erstellt »create()« , links oben im Bild, ein Peer-Objekt für den linken Browser. »stream()« speichert den lokalen Medienstrom in dem lokalen Peer-Objekt. »offer()« erzeugt eine Sitzungsbeschreibung, »keepLoc()« speichert sie als lokale Sitzungsbeschreibung ebenfalls im lokalen Peer-Objekt. »send()« überträgt die Beschreibung an den rechten Browser.

Abbildung 5: Vor dem Öffnen einer Peer-to-Peer-Verbindung erzeugen die beiden Browser Sitzungsbeschreibungen und tauschen sie aus.

Abbildung 5: Vor dem Öffnen einer Peer-to-Peer-Verbindung erzeugen die beiden Browser Sitzungsbeschreibungen und tauschen sie aus.

Beim Eintreffen der Sitzungsbeschreibung im rechten Browser speichert »keepRem()« sie als Information über die Gegenstelle. Anschließend erzeugt »answer()« eine lokale Sitzungsbeschreibung für diesen Browser. »keepLoc()« speichert sie als lokale Sitzungsbeschreibung und »send()« überträgt sie zurück an den linken Browser, wo die dortige »keepRem()« -Funktion sie als entfernte Sitzungsbeschreibung abspeichert. Damit ist der Handshake komplett.

Web RTC im lokalen Browser

Die Listings 4 bis 6 übertragen den Medienstrom über eine Peer-to-Peer-Verbindung nach dem Schema aus Abbildung 5. Damit diese Vorstudie auf einen Signaling-Server verzichten kann, kommunizieren nur zwei Video-Elemente im Firefox-Browser des lokalen Rechners (Abbildung 6). Selbst lokal funktioniert Web RTC nur mit Netzwerkverbindung.

Abbildung 6: Vorstudie zum Videochat: Zwei Video-Elemente im selben Browserfenster kommunizieren.

Abbildung 6: Vorstudie zum Videochat: Zwei Video-Elemente im selben Browserfenster kommunizieren.

Listing 4 zeigt das HTML-Dokument des Beispiels. Ebenso wie Listing 2 bindet es zwei Javascript-Dateien im Kopfbereich ein. Der Körper des Dokuments enthält in Zeile 9 ein Video-Element zur Ausgabe des lokalen Medienstroms und in der darauf folgenden Zeile 10 eines zur Ausgabe des mit Web RTC übertragenen Medienstroms.

Listing 4

Lokaler und übertragener Medienstrom

01 <html>
02 <head>
03   <link rel="stylesheet" href="css/styles.css"/>
04   <script src="js/core.js"></script>
05   <script src="js/localwebrtc.js"></script>
06 </head>
07 <body>
08   <p id="msg"></p>
09   <video id="local" autoplay></video>
10   <video id="remote" autoplay></video>
11 </body>
12 </html>

Listing 5 zeigt den Javascript-Code der Beispielanwendung aus der Datei »localwebrtc.js« . Die Abfrage und Konfiguration des Medienstroms erfolgt wie in Listing 2, jedoch mit einer geänderten Rückruffunktion in den Zeilen 5 bis 19: Zeile 6 verbindet zunächst den Medienstrom mit dem Video-Element. Zeile 7 erzeugt das lokale Peer-Objekt und speichert es in der Variablen »pcLocal« , Zeile 8 das entfernte Peer-Objekt in Pcremote. Zeile 9 speichert den lokalen Medienstrom im Peer-Objekt »pcRemote« .

Listing 5

localwebrtc.js: Web RTC im lokalen Browser

01 document.addEventListener('DOMContentLoaded', function() {
02   var serv = null;
03   navigator.mozGetUserMedia(
04     {video:true, audio:true},
05     function(stream) {
06       connectStream(stream, "#local");
07       var pcLocal = new mozRTCPeerConnection(serv);
08       var pcRemote = new mozRTCPeerConnection(serv);
09       pcLocal.addStream(stream);
10       pcLocal.createOffer(function(desc) {
11         pcLocal.setLocalDescription(desc);
12         pcRemote.setRemoteDescription(desc);
13         pcRemote.createAnswer(function(desc) {
14           pcRemote.setLocalDescription(desc);
15           pcLocal.setRemoteDescription(desc);
16         })
17       });
18       pcRemote.onaddstream = onadd()
19     },
20     error
21   );
22 });

Die asynchrone Methode »createOffer()« erzeugt in Zeile 10 die Sitzungsbeschreibung für den lokalen Kontext und übergibt sie der Rückruffunktion in den Zeilen 10 bis 17 im Parameter »desc« . Die Methode »setLocalDescription()« speichert den Inhalt von »desc« als lokale Sitzungsbeschreibung im Peer-Objekt »pcLocal« . Da die Anwendung lokal läuft, sichert die Methode »setRemoteDescription()« den Inhalt aus »desc« in Zeile 12 als entfernte Sitzungsbeschreibung für das entfernte Peer-Objekt »pcRemote« .

Der Aufruf der asynchronen Methode »createAnswer()« in Zeile 13 erstellt die lokale Sitzungsbeschreibung für das entfernte Peer-Objekt »pcRemote« und übergibt sie der Rückruffunktion aus den Zeilen 13 bis 16. Zeile 14 speichert die Sitzungsbeschreibung für das entfernte Peer-Objekt »pcRemote« als lokale Sitzungsbeschreibung, Zeile 15 als entfernte Sitzungsbeschreibung für das lokale Peer-Objekt »pcLocal« .

Das Ereignis »onaddstream« für das entfernte Peer-Objekt »pcRemote« wird in Zeile 18 durch Aufruf der Funktion »onadd()« aus Listing 6 belegt. Diese gibt eine Rückruffunktion zurück. Sie verbindet alle eingehenden Medienströme mit dem Video-Element mit der ID »remote« (Listing 4, Zeile 10). Die lokale Kommunikation ist hergestellt.

Listing 6

onadd()

01 function onadd() {
02   return function(evt) {
03     connectStream(evt.stream, "#remote");
04   }
05 }

Web RTC über das Internet

Das typische Anwendungsszenario von Web RTC über das Internet macht einen Signaling-Server erforderlich. Der leitet gemäß Abbildung 1 die Konfigurationsdaten weiter. Das folgende Beispiel verwendet für diese Daten das Json-Format. Jede Nachricht entspricht einem Javascript-Objekt mit zwei Komponenten: »command« beschreibt den Nachrichtentyp, »data« nimmt die Nutzdaten auf.

Die Anwendung verwendet drei unterschiedliche Nachrichtentypen: Zur Eröffnung einer Peer-to-Peer-Verbindung dient eine Nachricht des Typs »offer« (Listing 7). Zur Bestätigung der Verbindungseröffnung schickt die Gegenstelle eine Nachricht vom Typ »answer« zurück. Beide Signale enthalten im Datenfeld die jeweils lokale Sitzungsbeschreibung. Übermittelt ICE einen neuen Kandidaten, teilt es dies durch Senden einer Nachricht vom Typ ICE mit. In diesem Fall enthält »data« den Kandidaten.

Listing 7

Offer im Json-Format

01 {
02   "command":"offer",
03   "data": {
04     "sdp":"v=0\r\no=- 3077950714 2 IN IP4 127.0.0.1 [...] \r\n",
05     "type":"offer"
06   }
07 }

Signaling-Server

Für den Signaling-Server kommt ebenfalls Javascript zum Einsatz, jedoch Server-seitig auf Node.js. Eine ausführliche Einführung in den Node.js-Server bietet ein Artikel aus einem früheren Linux-Magazin [11]. Unter Ubuntu Linux installiert man Node.js, aktuell ist Version 0.8, und seinen Paketmanager Npm mit dem Befehl »sudo apt-get install nodejs npm« . Der Signaling-Server benötigt zudem die Module Connect ab Version 2.7.2 und Websocket ab Version 1.0.8.

Da Websocket seinerseits von »node-gyp« abhängt, installiert der Anwender zunächst dieses mit »npm install -g node-gyp« und anschließend Websocket und Connect mit dem Kommando »sudo npm install connect websocket« .

Listing 8 zeigt den Code für den Signaling-Server in Node.js. Die Zeilen 2 bis 4 binden die benötigten Module ein. Die Variable »channels« (Zeile 5) speichert die verbundenen Clients in einem Feld. In der darauf folgenden Zeile nimmt die Variable »app« eine um das Modul »static« erweiterte Connect-Anwendung auf. Static bewirkt, dass Node.js auf HTTP-Anfragen wie »http://localhost:6655/webrtc.html« die Datei »webrtc.html« aus dem Verzeichnis »../« zurückgibt.

Listing 8

Signaling-Server für Node.js

01 #!/usr/bin/env node
02 var http = require('http'),
03     connect = require('connect'),
04     WebSocketServer = require('websocket').server,
05     channels = [],
06     app = connect().use(connect.static('../'));
07
08 var wsServer = new WebSocketServer({
09   httpServer:new http.createServer(app).listen(6655)
10 });
11
12 wsServer.on('request', function(req) {
13   var thisChannel = req.accept('webrtc', req.origin);
14   channels.push(thisChannel);
15   thisChannel.on('message', function(msg) {
16     channels.forEach(function(channel) {
17       if (channel !== thisChannel) {
18         console.log(msg.utf8Data)
19         channel.sendUTF(msg.utf8Data);
20       }
21     });
22   });
23   thisChannel.on('close', function() {
24     channels = channels.filter(function(channel) {
25    return (channel !== thisChannel)?true:false;
26     });
27   });
28 });

Der Websocket-Server bindet in Zeile 9 ein HTTP-Server-Objekt ein. Die Variable »app« als Argument im Aufruf der Methode »createServer()« übergibt die Anwendungslogik der Connect-Anwendung an das HTTP-Server-Objekt. Die Methode »listen()« lässt den HTTP-Server auf Port 6655 lauschen.

Verbindungsereignisse

Die Rückruffunktion in den Zeilen 12 bis 28 beschreibt die Antwort des Signaling-Servers beim Aufbau einer Verbindung über das Websocket-Protokoll. Dazu bindet in Zeile 12 die Methode »on()« die Rückruffunktion aus dem zweiten Argument an das Auftreten des Ereignisses »request« . Die Rückruffunktion speichert in Zeile 13 eine gültige Verbindung in der Variablen »thisChannel« ab und Zeile 14 fügt sie der Liste aller bestehenden Verbindungen hinzu.

Die Zeilen 15 bis 22 definieren analog zu den Zeilen 12 bis 28 eine Rückruffunktion zum Verarbeiten einer eingehenden Nachricht. In der Schleife über alle Verbindungen (Zeilen 16 bis 21) leitet die »sendUTF()« -Methode die Nachricht an alle anderen Verbindungen weiter. Die Zeilen 23 bis 27 entfernen beim Beenden einer Verbindung den Client aus der Liste.

Wie Abbildung 7 zeigt, kann man dem Signaling-Server bei der Arbeit zusehen: Dank »console.log(msg.utf8Data)« in Zeile 18 schreibt er seine Ausgaben nämlich auch auf die Konsole.

Abbildung 7: Der Signaling-Server leitet nach seinem Start wie ein Router Nachrichten von einem Client an den anderen weiter.

Abbildung 7: Der Signaling-Server leitet nach seinem Start wie ein Router Nachrichten von einem Client an den anderen weiter.

Soll Web RTC auch über das Internet arbeiten, versendet man die Sitzungsbeschreibungen aus Listing 5 per Websocket-Protokoll und Signaling-Server wie in den Abbildungen 1 und 5 an die zu verbindenden Browser. Listing 9 zeigt das HTML-Dokument des Beispiels. Im Unterschied zu Listing 4 bindet es »webrtc.js« statt »localwebrtc.js« ein.

Listing 9

HTML für Web RTC per Internet

01 <html>
02 <head>
03   <link rel="stylesheet" href="css/styles.css"/>
04   <script src="js/core.js"></script>
05   <script src="js/webrtc.js"></script>
06 </head>
07 <body>
08   <p id="msg"></p>
09   <video id="local" autoplay></video>
10   <video id="remote" autoplay></video>
11 </body>
12 </html>

Listing 10 zeigt den Javascript-Code aus »webrtc.js« : Zeile 8 öffnet eine Verbindung zum Websocket-Server und speichert diese in der Variablen »channel« . Nach dem erfolgreichen Öffnen führt der Code die Rückruffunktion aus den Zeilen 9 bis 15 aus. Darin erzeugt Zeile 10 ein neues Peer-Objekt und speichert es in der globalen Variablen »pc« . Zeile 11 fügt den Medienstrom »stream« aus der Abfrage in Zeile 4 mit der Methode »addStream()« dem Peer-Objekt hinzu.

Listing 10

webrtc.js: Web RTC per Internet

01 document.addEventListener('DOMContentLoaded', function() {
02   var serv = pc = null,
03       url = 'ws://'+location.host;
04   navigator.mozGetUserMedia(
05     {video:true, audio:true},
06     function(stream) {
07       connectStream(stream, "#local");
08       var channel = new WebSocket(url, 'webrtc');
09       channel.onopen = function() {
10         pc = new mozRTCPeerConnection(serv);
11         pc.addStream(stream);
12         pc.onicecandidate = onice(pc);
13         pc.onaddstream = onadd();
14         pc.createOffer(desc(pc, channel, "offer"));
15       };
16       channel.onmessage = function(msg) {
17         var sig = JSON.parse(msg.data);
18         switch(sig.command) {
19           case "offer":
20             pc = new mozRTCPeerConnection(serv);
21             pc.addStream(stream);
22             pc.setRemoteDescription(new mozRTCSessionDescription(sig.data));
23             pc.onicecandidate = onice(pc);
24             pc.onaddstream = onadd();
25             pc.createAnswer(desc(pc, channel, "answer"));
26           break;
27           case "answer":
28             pc.setRemoteDescription(new mozRTCSessionDescription(sig.data));
29           break;
30           case "ice":
31             pc.addIceCandidate(new mozRTCIceCandidate(sig.data));
32           break;
33         }
34       };
35     },
36     error
37   );
38 });

ICE-Kandidat kommt

Die beiden darauf folgenden Zeilen erzeugen die Callback-Funktionen für die Ereignisse »onicecandidate« und »onaddstream« des Verbindungsaufbaus. Die Ereignisbehandlung für »onicecandidate« ist in Zeile 12 durch den Aufruf der Funktion »onice()« aus Listing 11 belegt. Sie gibt wie die Funktion »onadd()« aus Listing 6 eine Rückruffunktion zurück. Bei deren Aufruf verschickt die Funktion »send()« (Listing 13) einen ICE-Kandidaten. Abschließend erzeugt Zeile 14 von Listing 10 mittels »createOffer()« eine Sitzungsbeschreibung und übergibt sie der Rückruffunktion aus dem Aufruf von »desc()« (Listing 12). Dabei hinterlegt Zeile 3 die Sitzungsbeschreibung im Peer-Objekt »pc« , die folgende Zeile versendet sie über den Signaling-Server.

Listing 13

Objekt serialisieren

01 function send(channel, msg) {
02   channel.send(JSON.stringify(msg));
03 }

Listing 12

Sitzungsbeschreibung

01 function desc(pc, channel, comm) {
02   return function(desc) {
03     pc.setLocalDescription(desc);
04     send(channel, {command:comm, data:desc});
05   };
06 }

Listing 11

Ereignisbehandlung

01 function onice2(channel) {
02   return function(evt) {
03    if (evt.candidate) {
04      send(channel, {command:"ice", data:evt.candidate});
05     }
06   }
07 }

Empfangen und senden

Empfängt der Browser über die Websocket-Verbindung hingegen eine Nachricht von außen, so tritt die Rückruffunktion aus den Zeilen 16 bis 34 von Listing 10 in Aktion. Sie verwandelt zunächst die eingehende Json-Nachricht in ein Javascript-Objekt, das die anschließende Switch-Anweisung je nach dem Nachrichtentyp verarbeitet.

Listing 13 zeigt die Funktion »send()« . Diese nutzt in Zeile 2 die gleichnamige Methode des Websocket-Objekts, um eine Nachricht zu verschicken. Zuvor wandelt »stringify()« sie in eine Zeichenkette im Json-Format um.

Datenkanal

Mittels Web RTC lassen sich nicht nur Medienströme übertragen. Die Funktion »data()« in Listing 14 erzeugt einen Data-Channel zur Übertragung von Textnachrichten, der wie ein Websocket funktioniert. Zeile 3 öffnet mit der Methode »createDataChannel()« einen Kanal für die bestehende Peer-to-Peer-Verbindung im Peer-Objekt »pc« . Das Ereignis »ondatachannel« in Zeile 5 tritt beim Öffnen eines Datenkanals ein. Die Funktion »config()« ab Zeile 11 definiert die Rückruffunktion, die beim Eintreffen einer Textnachricht aktiv wird. Die Methode »send()« in Zeile 14 versendet Nachrichten über die bestehende Verbindung.

Listing 14

Data-Channel

01 function data(pc, opener) {
02   if (opener) {
03     config(pc.createDataChannel("work"));
04   } else {
05     pc.ondatachannel = function(evt) {
06       config(evt.channel);
07     }
08   }
09 }
10
11 function config(channel) {
12   channel.onmessage = function(evt) {
13     console.log(evt.data);
14     channel.send("recived message");
15   }
16 }

Chrome kann’s auch

Die Beispielanwendung für Firefox lässt sich mit wenig Aufwand auf den Google-Browser Chrome übertragen. Dazu ändert der Programmierer in den Beispielen das Hersteller-Präfix »moz« in »webkit« . Die betroffen Ausdrücke lassen sich aber auch in Funktionen auslagern, die für jeden Browser die passende Funktion inklusive Hersteller-Präfix aufrufen. Zu beachten ist: Die Beispiele funktionieren in Chrome nur beim Aufruf über das HTTP-Protokoll, etwa »http://localhost:6655/mediastream.html« .

Mit Web RTC lassen sich auch mehrere Browser verbinden. Der Topologie der Peer-to-Peer-Verbindungen sind keine Grenzen gesetzt, jedoch kommt ein Rechner beim Abspielen mehrerer Medienströme rasch an seine Grenzen. Web RTC bringt die Echtzeitkommunikation nativ in den Browser. Zwei Browser bauen dazu eine Peer-to-Peer-Verbindung über das Internet auf, und das auch durch eine NAT oder eine Firewall hindurch. Web RTC bietet mit dem Data-Channel nicht nur den bidirektionalen Austausch von Anwendungsdaten, sondern überträgt auch Medienströme.

Die direkte Verbindung der Browser spart Zeit und Datenverkehr. Damit eröffnet sie dem Webentwickler Möglichkeiten für viele neue HTML-5-Anwendungen, beispielsweise Videochats oder verteilte Anwendungen wie etwa ein vernetztes Memoryspiel. (mhu)

Infos

  1. Web RTC: http://www.webrtc.org
  2. Rtcweb Status Pages: http://tools.ietf.org/wg/rtcweb/draft-ietf-rtcweb-overview/
  3. Web RTC 1.0 W3C Editor’s Draft: http://dev.w3.org/2011/webrtc/editor/webrtc.html
  4. Web RTC MWC Phone Demo: http://www.youtube.com/watch?v=rWPZZeXK6g4
  5. UC-RTC: http://html5labs.interoperabilitybridges.com/cu-rtc-web/cu-rtc-web.htm
  6. ICE (Interactive Connectivity Establishment): http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment
  7. STUN (Session Traversal Utilities for NAT): http://en.wikipedia.org/wiki/STUN
  8. TURN (Traversal Using Relays around NAT): http://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT
  9. »getUserMedia()« : http://dev.w3.org/2011/webrtc/editor/getusermedia.html
  10. SDP-Format (Session Description Protocol: http://tools.ietf.org/html/rfc2327
  11. Andreas Möller, “Schneller knoten: Eigene Anwendungen mit Node.js und Redis programmieren”: Linux-Magazin 05/13, S. 84
  12. Listings zum Artikel: https://www.linux-magazin.de/static/listings/magazin/2013/08/web-rtc

Der Autor

Dipl.-Phys. Andreas Möller http://pamoller.com beschäftigt sich seit 2001 mit der Entwicklung Internet-basierter Software. Dazu zählen Datenbank- und Webanwendungen sowie Arbeiten auf dem Gebiet des Single Source Publishing. Zurzeit ist er als Berater und freier Autor tätig.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 6 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben