Créer les services de backend nécessaires à une application WebRTC

Qu'est-ce que la signalisation ?

La signalisation est le processus de coordination de la communication. Pour qu'une application WebRTC configure un appel, ses clients doivent échanger les informations suivantes:

  • Messages de contrôle de session utilisés pour ouvrir ou fermer la communication
  • Messages d'erreur
  • Métadonnées multimédias, telles que les codecs, les paramètres de codec, la bande passante et les types de contenu multimédia
  • Données de clé utilisées pour établir des connexions sécurisées
  • Données réseau, telles que l'adresse IP et le port d'un hôte tels qu'ils apparaissent à l'extérieur

Ce processus de signalisation nécessite un moyen pour les clients de transmettre des messages. Ce mécanisme n'est pas implémenté par les API WebRTC. Vous devez le créer vous-même. Plus loin dans cet article, vous découvrirez comment créer un service de signalisation. Tout d'abord, vous avez besoin d'un peu de contexte.

Pourquoi la signalisation n'est-elle pas définie par WebRTC ?

Pour éviter la redondance et maximiser la compatibilité avec les technologies établies, les méthodes et protocoles de signalisation ne sont pas spécifiés par les normes WebRTC. Cette approche est décrite par le protocole JSEP (JavaScript Session Establishment Protocol):

L'architecture de JSEP évite également au navigateur d'avoir à enregistrer l'état, c'est-à-dire de fonctionner comme une machine d'état de signalisation. Cela serait problématique si, par exemple, les données de signalisation étaient perdues à chaque fois qu'une page était actualisée. L'état de la signalisation peut être enregistré sur un serveur.

Schéma de l'architecture JSEP
Architecture JSEP

JSEP nécessite l'échange entre les pairs d'offres et de réponses, les métadonnées multimédias mentionnées ci-dessus. Les offres et les réponses sont communiquées au format SDP (Session Description Protocol), qui se présente comme suit:

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2

Vous voulez savoir ce que tout ce charabia SDP signifie en réalité ? Consultez les exemples de l'IETF (Internet Engineering Task Force).

N'oubliez pas que WebRTC est conçu pour que l'offre ou la réponse puisse être modifiée avant d'être définie comme description locale ou distante en modifiant les valeurs du texte SDP. Par exemple, la fonction preferAudioCodec() dans appr.tc peut être utilisée pour définir le codec et le débit par défaut. SDP est un peu difficile à manipuler avec JavaScript. Il est discuté de savoir si les futures versions de WebRTC devraient utiliser JSON à la place, mais SDP présente certains avantages.

API RTCPeerConnection et signalisation: offre, réponse et candidat

RTCPeerConnection est l'API utilisée par les applications WebRTC pour créer une connexion entre pairs et communiquer des données audio et vidéo.

Pour initialiser ce processus, RTCPeerConnection a deux tâches:

  • Déterminez les conditions des médias locaux, telles que la résolution et les fonctionnalités du codec. Il s'agit des métadonnées utilisées pour le mécanisme d'offre et de réponse.
  • Obtenez les adresses réseau potentielles de l'hôte de l'application, appelées candidats.

Une fois ces données locales déterminées, elles doivent être échangées via un mécanisme de signalisation avec l'homologue distant.

Imaginons qu'Alice essaie d'appeler Eve. Voici le mécanisme d'offre/réponse complet dans tous ses détails:

  1. Alice crée un objet RTCPeerConnection.
  2. Alice crée une offre (une description de session SDP) à l'aide de la méthode createOffer() RTCPeerConnection.
  3. Alice appelle setLocalDescription() pour lui présenter son offre.
  4. Alice concatène l'offre et utilise un mécanisme de signalisation pour l'envoyer à Eve.
  5. Eve appelle setRemoteDescription() pour lui communiquer l'offre d'Alice, afin que son RTCPeerConnection soit informé de la configuration d'Alice.
  6. Eve appelle createAnswer(), et le rappel de réussite lui transmet une description de session locale (réponse d'Eve).
  7. Eve définit sa réponse comme description locale en appelant setLocalDescription().
  8. Eve utilise ensuite le mécanisme de signalisation pour envoyer sa réponse sous forme de chaîne à Alice.
  9. Alice définit la réponse d'Eve comme description de la session distante à l'aide de setRemoteDescription().

Alice et Eve doivent également échanger des informations réseau. L'expression "trouver des candidats" fait référence au processus de recherche d'interfaces réseau et de ports à l'aide du framework ICE.

  1. Alice crée un objet RTCPeerConnection avec un gestionnaire onicecandidate.
  2. Le gestionnaire est appelé lorsque les candidats de réseau deviennent disponibles.
  3. Dans le gestionnaire, Alice envoie des données candidates concaténées à Eve via son canal de signalisation.
  4. Lorsqu'Eve reçoit un message candidat d'Alice, elle appelle addIceCandidate() pour ajouter le candidat à la description du pair distant.

JSEP est compatible avec le flux de candidats ICE, qui permet à l'appelant de fournir progressivement des candidats à l'appelant après l'offre initiale, et à l'appelant de commencer à traiter l'appel et à configurer une connexion sans attendre que tous les candidats arrivent.

Coder WebRTC pour la signalisation

L'extrait de code suivant est un exemple de code W3C qui résume le processus de signalisation complet. Le code suppose l'existence d'un mécanisme de signalisation, SignalingChannel. Nous reviendrons plus en détail sur la signalisation ultérieurement.

// 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);
  }
};

// After 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);
  }
};

Pour voir les processus d'offre/réponse et d'échange de candidats en action, consultez simpl.info RTCPeerConnection et examinez le journal de la console pour obtenir un exemple de chat vidéo sur une seule page. Pour en savoir plus, téléchargez un dump complet de la signalisation et des statistiques WebRTC à partir de la page about://webrtc-internals dans Google Chrome ou de la page opera://webrtc-internals dans Opera.

Découverte de pairs

Il s'agit d'une façon élégante de demander "Comment trouver quelqu'un à qui parler ?".

Pour les appels téléphoniques, vous disposez de numéros de téléphone et de répertoires. Pour le chat vidéo et la messagerie en ligne, vous avez besoin de systèmes de gestion de l'identité et de la présence, ainsi que d'un moyen pour les utilisateurs de lancer des sessions. Les applications WebRTC doivent permettre aux clients de s'indiquer mutuellement qu'ils souhaitent démarrer ou rejoindre un appel.

Les mécanismes de découverte de pairs ne sont pas définis par WebRTC, et vous n'avez pas accès aux options ici. Le processus peut être aussi simple que d'envoyer une URL par e-mail ou par message. Pour les applications de chat vidéo telles que Talky, tawk.to et Browser Meeting, vous invitez des personnes à participer à un appel en partageant un lien personnalisé. Le développeur Chris Ball a créé une expérience serverless-webrtc intéressante qui permet aux participants à un appel WebRTC d'échanger des métadonnées via n'importe quel service de messagerie de leur choix, comme la messagerie instantanée, le courrier électronique ou le pigeon voyageur.

Comment créer un service de signalisation ?

Pour rappel, les protocoles et mécanismes de signalisation ne sont pas définis par les normes WebRTC. Quelle que soit votre option, vous avez besoin d'un serveur intermédiaire pour échanger des messages de signalisation et des données d'application entre les clients. Malheureusement, une application Web ne peut pas simplement crier sur Internet "Connectez-moi à mon ami !".

Heureusement, les messages de signalisation sont de petite taille et sont principalement échangés au début d'un appel. Lors des tests avec appr.tc pour une session de chat vidéo, le service de signalisation a géré un total d'environ 30 à 45 messages, avec une taille totale de tous les messages d'environ 10 ko.

En plus d'être relativement peu exigeants en termes de bande passante, les services de signalisation WebRTC ne consomment pas beaucoup de traitement ni de mémoire, car ils ne doivent que relayer des messages et conserver une petite quantité de données d'état de session, telles que les clients connectés.

Envoyer des messages du serveur au client

Un service de messagerie pour la signalisation doit être bidirectionnel: du client au serveur et du serveur au client. La communication bidirectionnelle va à l'encontre du modèle HTTP client/serveur requête/réponse, mais divers hacks tels que le polling long ont été développés au fil des ans pour transférer des données d'un service exécuté sur un serveur Web vers une application Web exécutée dans un navigateur.

Plus récemment, l'API EventSource a été largement implémentée. Cela permet d'utiliser des événements envoyés par le serveur, c'est-à-dire des données envoyées d'un serveur Web à un client de navigateur via HTTP. EventSource est conçu pour la messagerie à sens unique, mais il peut être utilisé avec XHR pour créer un service d'échange de messages de signalisation. Un service de signalisation transmet un message d'un appelant, envoyé par requête XHR, en le transmettant via EventSource à l'appelant.

WebSocket est une solution plus naturelle, conçue pour la communication client-serveur en duplex intégral (les messages peuvent circuler dans les deux sens en même temps). L'un des avantages d'un service de signalisation créé avec des événements WebSocket ou envoyés par le serveur (EventSource) est que le backend de ces API peut être implémenté sur différents frameworks Web communs à la plupart des packages d'hébergement Web pour des langages tels que PHP, Python et Ruby.

Tous les navigateurs modernes, à l'exception d'Opera Mini, sont compatibles avec WebSocket. Plus important encore, tous les navigateurs compatibles avec WebRTC sont également compatibles avec WebSocket, à la fois sur ordinateur et sur mobile. Le TLS doit être utilisé pour toutes les connexions afin de s'assurer que les messages ne peuvent pas être interceptés sans chiffrement et de réduire les problèmes liés au proxy traversal. (Pour en savoir plus sur WebSocket et la traversée de proxy, consultez le chapitre WebRTC de l'ouvrage High Performance Browser Networking (Haute performance de la mise en réseau du navigateur) d'Ilya Grigorik.)

Il est également possible de gérer la signalisation en demandant aux clients WebRTC d'interroger un serveur de messagerie à plusieurs reprises via Ajax, mais cela entraîne de nombreuses requêtes réseau redondantes, ce qui est particulièrement problématique pour les appareils mobiles. Même après l'établissement d'une session, les pairs doivent interroger les messages de signalisation en cas de modification ou de fin de session par d'autres pairs. L'exemple d'application WebRTC Book utilise cette option avec quelques optimisations pour la fréquence d'interrogation.

Signalement de l'échelle

Bien qu'un service de signalisation consomme relativement peu de bande passante et de CPU par client, les serveurs de signalisation d'une application populaire peuvent devoir gérer de nombreux messages provenant de différents emplacements avec des niveaux de simultanéité élevés. Les applications WebRTC qui génèrent beaucoup de trafic ont besoin de serveurs de signalisation capables de gérer une charge considérable. Vous n'entrez pas dans les détails ici, mais il existe plusieurs options pour la messagerie à fort volume et hautes performances, y compris les suivantes:

  • XMPP (Extensible Messaging and Presence Protocol), initialement connu sous le nom de Jabber, est un protocole développé pour la messagerie instantanée et pouvant être utilisé pour la signalisation (les implémentations de serveur incluent ejabberd et Openfire). Les clients JavaScript, tels que Strophe.js, utilisent BOSH pour émuler le streaming bidirectionnel. Toutefois, pour diverses raisons, BOSH peut ne pas être aussi efficace que WebSocket et, pour les mêmes raisons, ne pas être bien évolutif.) (En passant, Jingle est une extension XMPP qui permet d'activer les appels vocaux et vidéo. Le projet WebRTC utilise les composants réseau et de transport de la bibliothèque libjingle, une implémentation C++ de Jingle.)

  • Bibliothèques Open Source, telles que ZeroMQ (utilisée par TokBox pour son service Rumour) et OpenMQ (NullMQ applique les concepts ZeroMQ aux plates-formes Web utilisant le protocole STOMP sur WebSocket.)

  • Plates-formes de messagerie cloud commerciales qui utilisent WebSocket (bien qu'elles puissent revenir à la méthode de requête longue), telles que Pusher, Kaazing et PubNub (PubNub dispose également d'une API pour WebRTC).

  • Plates-formes WebRTC commerciales, telles que vLine

(Le guide des technologies Web en temps réel du développeur Phil Leggetter fournit une liste complète des services et bibliothèques de messagerie.)

Créer un service de signalisation avec Socket.io sur Node

Vous trouverez ci-dessous le code d'une application Web simple qui utilise un service de signalisation créé avec Socket.io sur Node. La conception de Socket.io permet de créer facilement un service d'échange de messages. Socket.io est particulièrement adapté à la signalisation WebRTC en raison de son concept de salles intégré. Cet exemple n'est pas conçu pour être évolutif en tant que service de signalisation de qualité de production, mais il est facile à comprendre pour un nombre relativement faible d'utilisateurs.

Socket.io utilise WebSocket avec des solutions de remplacement: polling long AJAX, streaming multiparties AJAX, iframe Forever et polling JSONP. Il a été porté sur différents backends, mais il est peut-être surtout connu pour sa version Node utilisée dans cet exemple.

Cet exemple ne comporte pas de WebRTC. Il n'a pour but que de montrer comment intégrer la signalisation dans une application Web. Consultez le journal de la console pour voir ce qui se passe lorsque les clients rejoignent une salle et échangent des messages. Cet atelier de programmation WebRTC explique étape par étape comment l'intégrer à une application de chat vidéo WebRTC complète.

Voici le client index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

Voici le fichier JavaScript main.js référencé dans le client:

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') {
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => {
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => {
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => {
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => {
  console.log.apply(console, array);
});

Voici l'application serveur complète:

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {

  // Convenience function to log server messages to the client
  function log(){
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => {
    log('Got message:', message);
    // For a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => {
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) {
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

(Vous n'avez pas besoin de connaître node-static pour cela. Il se trouve qu'il est utilisé dans cet exemple.)

Pour exécuter cette application sur localhost, vous devez installer Node, Socket.IO et node-static. Vous pouvez télécharger Node depuis Node.js (l'installation est simple et rapide). Pour installer Socket.IO et node-static, exécutez le gestionnaire de paquets Node depuis un terminal dans le répertoire de votre application:

npm install socket.io
npm install node-static

Pour démarrer le serveur, exécutez la commande suivante dans un terminal du répertoire de votre application:

node server.js

Dans votre navigateur, ouvrez localhost:2013. Ouvrez un nouvel onglet ou une nouvelle fenêtre dans n'importe quel navigateur, puis ouvrez à nouveau localhost:2013. Pour voir ce qui se passe, consultez la console. Dans Chrome et Opera, vous pouvez accéder à la console via les outils pour les développeurs Google Chrome avec Ctrl+Shift+J (ou Command+Option+J sur Mac).

Quelle que soit l'approche que vous choisissez pour la signalisation, votre backend et votre application cliente doivent au moins fournir des services similaires à cet exemple.

Pièges de signalisation

  • RTCPeerConnection ne commencera à collecter des candidats que lorsque setLocalDescription() sera appelé. Cette exigence est stipulée dans le brouillon de l'IETF sur le JSEP.
  • Profitez de Trickle ICE. Appelez addIceCandidate() dès que les candidats arrivent.

Serveurs de signalisation prêts à l'emploi

Si vous ne souhaitez pas créer votre propre serveur de signalisation WebRTC, plusieurs serveurs sont disponibles. Ils utilisent Socket.IO comme dans l'exemple précédent et sont intégrés aux bibliothèques JavaScript clientes WebRTC:

  • webRTC.io est l'une des premières bibliothèques d'abstraction pour WebRTC.
  • Signalmaster est un serveur de signalisation conçu pour être utilisé avec la bibliothèque cliente JavaScript SimpleWebRTC.

Si vous ne souhaitez pas écrire de code du tout, des plates-formes WebRTC commerciales complètes sont disponibles auprès d'entreprises telles que vLine, OpenTok et Asterisk.

Pour information, Ericsson a créé un serveur de signalisation utilisant PHP sur Apache au début de WebRTC. Cette approche est désormais quelque peu obsolète, mais il est intéressant d'examiner le code si vous envisagez quelque chose de similaire.

Sécurité de la signalisation

"La sécurité est l'art de ne rien laisser se produire."

Salman Rushdie

Le chiffrement est obligatoire pour tous les composants WebRTC.

Toutefois, les mécanismes de signalisation ne sont pas définis par les normes WebRTC. Il vous appartient donc de sécuriser la signalisation. Si un pirate informatique parvient à pirater la signalisation, il peut arrêter les sessions, rediriger les connexions, et enregistrer, modifier ou injecter du contenu.

Le facteur le plus important pour sécuriser la signalisation consiste à utiliser des protocoles sécurisés (HTTPS et WSS, par exemple, TLS), qui garantissent que les messages ne peuvent pas être interceptés sans chiffrement. Veillez également à ne pas diffuser les messages de signalisation de manière à ce qu'ils soient accessibles par d'autres appelants utilisant le même serveur de signalisation.

Après la signalisation: utiliser ICE pour gérer les NAT et les pare-feu

Pour la signalisation des métadonnées, les applications WebRTC utilisent un serveur intermédiaire, mais pour la diffusion de données et de médias réelle une fois une session établie, RTCPeerConnection tente de connecter les clients directement ou en peer-to-peer.

Dans un monde plus simple, chaque point de terminaison WebRTC aurait une adresse unique qu'il pourrait échanger avec d'autres pairs afin de communiquer directement.

Connexion simple de pair à pair
Un monde sans NAT ni pare-feu

En réalité, la plupart des appareils se trouvent derrière une ou plusieurs couches de NAT, certains disposent d'un logiciel antivirus qui bloque certains ports et protocoles, et beaucoup se trouvent derrière des proxys et des pare-feu d'entreprise. Un pare-feu et un NAT peuvent en fait être implémentés par le même appareil, tel qu'un routeur Wi-Fi domestique.

Équivalents derrière des NAT et des pare-feu
Le monde réel

Les applications WebRTC peuvent utiliser le framework ICE pour surmonter les complexités de la mise en réseau dans le monde réel. Pour ce faire, votre application doit transmettre les URL des serveurs ICE à RTCPeerConnection, comme décrit dans cet article.

ICE tente de trouver le meilleur chemin pour connecter les pairs. Il essaie toutes les possibilités en parallèle et choisit l'option la plus efficace qui fonctionne. ICE tente d'abord d'établir une connexion à l'aide de l'adresse hôte obtenue à partir du système d'exploitation et de la carte réseau d'un appareil. Si cela échoue (ce qui est le cas pour les appareils derrière des NAT), ICE obtient une adresse externe à l'aide d'un serveur STUN. Si cela échoue, le trafic est acheminé via un serveur de relais TURN.

En d'autres termes, un serveur STUN permet d'obtenir une adresse réseau externe, tandis que les serveurs TURN servent à relayer le trafic en cas d'échec de la connexion directe (point à point).

Chaque serveur TURN est compatible avec STUN. Un serveur TURN est un serveur STUN avec une fonctionnalité de relais intégrée supplémentaire. ICE gère également la complexité des configurations NAT. En réalité, le perçage de trous NAT peut nécessiter plus qu'une adresse IP publique:port.

Les URL des serveurs STUN et/ou TURN sont (facultatif) spécifiées par une application WebRTC dans l'objet de configuration iceServers, qui est le premier argument du constructeur RTCPeerConnection. Pour appr.tc, cette valeur se présente comme suit:

{
  'iceServers': [
    {
      'urls': 'stun:stun.l.google.com:19302'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

Une fois que RTCPeerConnection dispose de ces informations, la magie ICE opère automatiquement. RTCPeerConnection utilise le framework ICE pour déterminer le meilleur chemin entre les pairs, en travaillant avec les serveurs STUN et TURN si nécessaire.

STUN

Les NAT fournissent à un appareil une adresse IP à utiliser dans un réseau local privé, mais cette adresse ne peut pas être utilisée en externe. Sans adresse publique, les pairs WebRTC ne peuvent pas communiquer. Pour contourner ce problème, WebRTC utilise STUN.

Les serveurs STUN se trouvent sur Internet public et ont une tâche simple : vérifier l'adresse IP:port d'une requête entrante (d'une application exécutée derrière un NAT) et renvoyer cette adresse en réponse. En d'autres termes, l'application utilise un serveur STUN pour découvrir son adresse IP et son port d'un point de vue public. Ce processus permet à un pair WebRTC d'obtenir une adresse accessible au public pour lui-même, puis de la transmettre à un autre pair via un mécanisme de signalisation afin de configurer un lien direct. (En pratique, les différents NAT fonctionnent différemment et il peut y avoir plusieurs couches NAT, mais le principe reste le même.)

Les serveurs STUN n'ont pas besoin de faire grand-chose ni de se souvenir de beaucoup de choses. Par conséquent, les serveurs STUN aux spécifications relativement faibles peuvent gérer un grand nombre de requêtes.

La plupart des appels WebRTC établissent une connexion à l'aide de STUN (86% selon Webrtcstats.com), mais ce chiffre peut être inférieur pour les appels entre pairs derrière des pare-feu et des configurations NAT complexes.

Connexion point à point à l&#39;aide d&#39;un serveur STUN
Utiliser des serveurs STUN pour obtenir des adresses IP:port publiques

TURN

RTCPeerConnection tente de configurer une communication directe entre les pairs via UDP. En cas d'échec, RTCPeerConnection utilise TCP. Si cela échoue, les serveurs TURN peuvent être utilisés comme solution de secours, en transmettant des données entre les points de terminaison.

Pour rappel, TURN permet de relayer le streaming audio, vidéo et de données entre pairs, et non les données de signalisation.

Les serveurs TURN disposent d'adresses publiques et peuvent donc être contactés par les pairs, même s'ils se trouvent derrière des pare-feu ou des proxys. Les serveurs TURN ont une tâche conceptuellement simple : relayer un flux. Toutefois, contrairement aux serveurs STUN, ils consomment intrinsèquement beaucoup de bande passante. En d'autres termes, les serveurs TURN doivent être plus puissants.

Connexion point à point à l&#39;aide d&#39;un serveur STUN
The Full Monty: STUN, TURN et signalisation

Ce diagramme montre TURN en action. STUN pur n'a pas réussi, de sorte que chaque paire a recours à un serveur TURN.

Déployer des serveurs STUN et TURN

Pour les tests, Google exécute un serveur STUN public, stun.l.google.com:19302, utilisé par appr.tc. Pour un service STUN/TURN de production, utilisez rfc5766-turn-server. Le code source des serveurs STUN et TURN est disponible sur GitHub, où vous trouverez également des liens vers plusieurs sources d'informations sur l'installation de serveurs. Une image de VM pour Amazon Web Services est également disponible.

Un autre serveur TURN est restund, disponible en tant que code source et également pour AWS. Voici comment configurer le rappel sur Compute Engine.

  1. Ouvrez le pare-feu si nécessaire pour tcp=443, udp/tcp=3478.
  2. Créez quatre instances, une pour chaque adresse IP publique, avec l'image Ubuntu 12.06 standard.
  3. Configurez la configuration du pare-feu local (autorisez TOUS à partir de TOUS).
  4. Installez les outils : shell sudo apt-get install make sudo apt-get install gcc
  5. Installez libre sur creytiv.com/re.html.
  6. Récupérez restund sur creytiv.com/restund.html et décompressez-le./
  7. wget hancke.name/restund-auth.patch et appliquer avec patch -p1 < restund-auth.patch.
  8. Exécutez make, sudo make install pour libre et restund.
  9. Adaptez restund.conf à vos besoins (remplacez les adresses IP et assurez-vous qu'il contient le même secret partagé) et copiez-le dans /etc.
  10. Copiez restund/etc/restund dans le répertoire /etc/init.d/.
  11. Configurez le remboursement :
    1. Définissez LD_LIBRARY_PATH.
    2. Copiez restund.conf dans le répertoire /etc/restund.conf.
    3. Définissez restund.conf pour utiliser le bon 10. une nouvelle adresse IP.
  12. Exécuter restund
  13. Test avec le client de test à partir d'une machine distante: ./client IP:port

Au-delà du dialogue en tête-à-tête: WebRTC multiparti

Vous pouvez également consulter la proposition de norme IETF de Justin Uberti pour une API REST permettant d'accéder aux services TURN.

Il est facile d'imaginer des cas d'utilisation du streaming multimédia qui vont au-delà d'un simple appel en tête-à-tête. Par exemple, une visioconférence entre un groupe de collègues ou un événement public avec un orateur et des centaines ou des millions de spectateurs.

Une application WebRTC peut utiliser plusieurs RTCPeerConnections afin que chaque point de terminaison se connecte à tous les autres points de terminaison dans une configuration en réseau maillé. C'est l'approche adoptée par des applications telles que talky.io, et elle fonctionne remarquablement bien pour un petit nombre d'utilisateurs. Au-delà, le traitement et la consommation de bande passante deviennent excessifs, en particulier pour les clients mobiles.

Réseau maillé: petit appel à plusieurs participants
Topologie de réseau maillé complet: tous les nœuds sont connectés à tous les autres

Une application WebRTC peut également choisir un point de terminaison pour distribuer des flux à tous les autres dans une configuration en étoile. Vous pouvez également exécuter un point de terminaison WebRTC sur un serveur et créer votre propre mécanisme de redistribution (un exemple d'application cliente est fourni par webrtc.org).

Depuis Chrome 31 et Opera 18, un MediaStream d'un RTCPeerConnection peut être utilisé comme entrée pour un autre. Cela peut permettre des architectures plus flexibles, car une application Web peut gérer l'acheminement des appels en choisissant à quel autre pair se connecter. Pour voir comment cela fonctionne, consultez les exemples WebRTC Peer connection relay (Relais de connexion entre pairs WebRTC) et WebRTC samples Multiple peer connections (Exemples WebRTC de plusieurs connexions entre pairs).

Multipoint Control Unit

Pour un grand nombre de points de terminaison, il est préférable d'utiliser une unité de contrôle multipoint (MCU). Il s'agit d'un serveur qui sert de pont pour distribuer des contenus multimédias entre un grand nombre de participants. Les MCU peuvent gérer différentes résolutions, codecs et fréquences d'images lors d'une visioconférence, gérer le transcodage, transférer des flux de manière sélective, et mixer ou enregistrer des contenus audio et vidéo. Pour les appels multiparties, un certain nombre de problèmes doivent être pris en compte, en particulier l'affichage de plusieurs entrées vidéo et le mixage audio de plusieurs sources. Les plates-formes cloud, telles que vLine, tentent également d'optimiser le routage du trafic.

Vous pouvez acheter un package matériel MCU complet ou en créer un vous-même.

Vue arrière du Cisco MCU5300
Arrière d'une MCU Cisco

Plusieurs options logicielles Open Source pour MCU sont disponibles. Par exemple, Licode (anciennement Lynckia) produit un MCU Open Source pour WebRTC. OpenTok propose Mantis.

Au-delà des navigateurs: VoIP, téléphones et messagerie

La nature standardisée de WebRTC permet d'établir une communication entre une application WebRTC exécutée dans un navigateur et un appareil ou une plate-forme exécutés sur une autre plate-forme de communication, comme un téléphone ou un système de visioconférence.

Le protocole SIP est un protocole de signalisation utilisé par les systèmes VoIP et de visioconférence. Pour permettre la communication entre une application Web WebRTC et un client SIP, tel qu'un système de visioconférence, WebRTC a besoin d'un serveur proxy pour assurer la médiation du signalement. La signalisation doit transiter par la passerelle, mais une fois la communication établie, le trafic SRTP (vidéo et audio) peut transiter directement en pair à pair.

Le réseau téléphonique commuté public (RTCP) est le réseau commuté par circuits de tous les téléphones analogiques classiques. Pour les appels entre les applications Web WebRTC et les téléphones, le trafic doit passer par une passerelle PSTN. De même, les applications Web WebRTC ont besoin d'un serveur XMPP intermédiaire pour communiquer avec les points de terminaison Jingle tels que les clients de messagerie instantanée. Jingle a été développé par Google en tant qu'extension de XMPP pour permettre les appels vocaux et vidéo dans les services de messagerie. Les implémentations WebRTC actuelles sont basées sur la bibliothèque C++ libjingle, une implémentation de Jingle initialement développée pour Talk.

De nombreuses applications, bibliothèques et plates-formes utilisent la capacité de WebRTC à communiquer avec le monde extérieur:

  • sipML5: client SIP JavaScript Open Source
  • jsSIP: bibliothèque JavaScript SIP
  • Phono: API JavaScript pour téléphones open source créée en tant que plug-in
  • Zingaya: widget pour téléphone intégré
  • Twilio: voix et messagerie
  • Uberconference: visioconférence

Les développeurs de sipML5 ont également créé la passerelle webrtc2sip. Tethr et Tropo ont présenté un framework de communications en cas de catastrophe "dans une mallette" à l'aide d'une cellule OpenBTS pour permettre les communications entre les téléphones standards et les ordinateurs via WebRTC. Il s'agit d'une communication téléphonique sans opérateur.

En savoir plus

L'atelier de programmation WebRTC fournit des instructions détaillées pour créer une application de chat vidéo et textuel à l'aide d'un service de signalisation Socket.io exécuté sur Node.

Présentation WebRTC de Google I/O en 2013 avec Justin Uberti, responsable technique WebRTC

Présentation SFHTML5 de Chris Wilson : Présentation des applications WebRTC

Le livre de 350 pages WebRTC: APIs et protocoles RTCWEB du Web en temps réel HTML5 fournit de nombreux détails sur les voies de données et de signalisation, et inclut un certain nombre de diagrammes de topologie réseau détaillés.

WebRTC et signalisation : ce que nous avons appris en deux ans : article de blog TokBox expliquant pourquoi il était judicieux de ne pas inclure la signalisation dans la spécification

A Practical Guide to Building WebRTC Apps (Un guide pratique pour créer des applications WebRTC) de Ben Strong fournit de nombreuses informations sur les architectures et les infrastructures WebRTC.

Le chapitre sur WebRTC de l'ouvrage High Performance Browser Networking (Hautes performances de la mise en réseau du navigateur) d'Ilya Grigorik explore en détail l'architecture, les cas d'utilisation et les performances de WebRTC.