1. Introduction
WebRTC est un projet Open Source qui permet la communication en temps réel de données audio, vidéo et autres dans les applications Web et natives.
WebRTC comporte plusieurs API JavaScript. Cliquez sur les liens pour voir des démonstrations.
getUserMedia(): capturez l'audio et la vidéo.MediaRecorder: enregistrez l'audio et la vidéo.RTCPeerConnection: diffusez de l'audio et de la vidéo entre les utilisateurs.RTCDataChannel: diffuser des données entre les utilisateurs.
Où puis-je utiliser WebRTC ?
Dans Firefox, Opera et dans Chrome sur ordinateur et Android. WebRTC est également disponible pour les applications natives sur iOS et Android.
Qu'est-ce que la signalisation ?
WebRTC utilise RTCPeerConnection pour communiquer des données de streaming entre les navigateurs, mais a également besoin d'un mécanisme pour coordonner la communication et envoyer des messages de contrôle, un processus appelé signalisation. Les méthodes et protocoles de signalisation ne sont pas spécifiés par WebRTC. Dans cet atelier de programmation, vous utiliserez Socket.IO pour la messagerie, mais il existe de nombreuses alternatives.
Que sont STUN et TURN ?
WebRTC est conçu pour fonctionner en peer-to-peer, ce qui permet aux utilisateurs de se connecter par le chemin le plus direct possible. Toutefois, WebRTC est conçu pour faire face aux réseaux réels : les applications clientes doivent traverser les passerelles NAT et les pare-feu, et les réseaux peer-to-peer ont besoin de solutions de secours en cas d'échec de la connexion directe. Dans le cadre de ce processus, les API WebRTC utilisent des serveurs STUN pour obtenir l'adresse IP de votre ordinateur et des serveurs TURN pour servir de serveurs relais en cas d'échec de la communication peer-to-peer. (Pour en savoir plus, consultez WebRTC dans le monde réel.)
WebRTC est-il sécurisé ?
Le chiffrement est obligatoire pour tous les composants WebRTC, et ses API JavaScript ne peuvent être utilisées qu'à partir d'origines sécurisées (HTTPS ou localhost). Les mécanismes de signalisation ne sont pas définis par les normes WebRTC. Il vous appartient donc de veiller à utiliser des protocoles sécurisés.
2. Présentation
Créez une application pour obtenir des vidéos et prendre des instantanés avec votre webcam, puis partagez-les en peer-to-peer via WebRTC. Vous apprendrez à utiliser les API WebRTC de base et à configurer un serveur de messagerie à l'aide de Node.js.
Points abordés
- Obtenir la vidéo de votre webcam
- Diffuser des vidéos en streaming avec RTCPeerConnection
- Diffuser des données avec RTCDataChannel
- Configurer un service de signalisation pour échanger des messages
- Combiner la connexion et la signalisation entre pairs
- Prendre une photo et la partager via un canal de données
Prérequis
- Chrome 47 ou version ultérieure
- Web Server for Chrome ou le serveur Web de votre choix.
- Exemple de code
- Un éditeur de texte
- Connaissances de base en HTML, CSS et JavaScript
3. Obtenir l'exemple de code
Télécharger le code
Si vous êtes familiarisé avec git, vous pouvez télécharger le code de cet atelier de programmation depuis GitHub en le clonant :
git clone https://github.com/googlecodelabs/webrtc-web
Vous pouvez également cliquer sur le bouton suivant pour télécharger un fichier ZIP du code :
Ouvrez le fichier ZIP téléchargé. Cette action décompresse un dossier de projet (adaptive-web-media) qui contient un dossier pour chaque étape de cet atelier de programmation, ainsi que toutes les ressources dont vous aurez besoin.
Tout le travail de codage sera effectué dans le répertoire work.
Les dossiers step-nn contiennent une version finalisée pour chaque étape de cet atelier de programmation. Ils sont fournis à titre de référence.
Installer et valider le serveur Web
Bien que vous soyez libre d'utiliser votre propre serveur Web, cet atelier de programmation est conçu pour fonctionner correctement avec le serveur Web Chrome. Si l'application n'est pas encore installée, vous pouvez le faire depuis le Chrome Web Store.

Après avoir installé l'application Web Server for Chrome, cliquez sur le raccourci "Applications Chrome" dans la barre de favoris, sur la page "Nouvel onglet" ou dans le lanceur d'applications :

Cliquez sur l'icône Serveur Web :

La boîte de dialogue suivante s'affiche. Elle vous permet de configurer votre serveur Web local :

Cliquez sur le bouton CHOISIR UN DOSSIER, puis sélectionnez le dossier work que vous venez de créer. Cela vous permettra de visualiser votre travail en cours dans Chrome via l'URL indiquée dans la boîte de dialogue du serveur Web, dans la section Web Server URL(s).
Sous Options, cochez la case Automatically show index.html (Afficher automatiquement l'index.html), comme indiqué ci-dessous :

Arrêtez ensuite le serveur, puis redémarrez-le en faisant glisser le bouton Web Server: STARTED (Serveur Web : DÉMARRÉ) vers la gauche, puis de nouveau vers la droite.

Accédez maintenant à votre site de travail dans votre navigateur Web en cliquant sur l'URL du serveur Web mise en surbrillance. Une page semblable à celle-ci, qui correspond à work/index.html, devrait s'afficher :

Évidemment, cette application ne fait rien d'intéressant pour le moment. Il s'agit simplement d'un squelette minimal que nous utilisons pour nous assurer que votre serveur Web fonctionne correctement. Vous ajouterez des fonctionnalités et des éléments de mise en page lors des étapes suivantes.
4. Diffuser une vidéo depuis votre webcam
Points abordés
Dans cette étape, vous allez découvrir comment :
- Obtenez un flux vidéo depuis votre webcam.
- Manipuler la lecture du flux.
- Utilisez CSS et SVG pour manipuler la vidéo.
Une version complète de cette étape se trouve dans le dossier step-01.
Un peu de code HTML…
Ajoutez un élément video et un élément script à index.html dans votre répertoire work :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<video autoplay playsinline></video>
<script src="js/main.js"></script>
</body>
</html>
...et une pincée de JavaScript
Ajoutez les éléments suivants à main.js dans votre dossier js :
'use strict';
// On this codelab, you will be streaming only video (video: true).
const mediaStreamConstraints = {
video: true,
};
// Video element where stream will be placed.
const localVideo = document.querySelector('video');
// Local stream that will be reproduced on the video.
let localStream;
// Handles success by adding the MediaStream to the video element.
function gotLocalMediaStream(mediaStream) {
localStream = mediaStream;
localVideo.srcObject = mediaStream;
}
// Handles error by logging a message to the console with the error message.
function handleLocalMediaStreamError(error) {
console.log('navigator.getUserMedia error: ', error);
}
// Initializes media stream.
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
Essayer
Ouvrez index.html dans votre navigateur. Vous devriez voir quelque chose comme ceci (avec la vue de votre webcam, bien sûr) :

Fonctionnement
Après l'appel getUserMedia(), le navigateur demande à l'utilisateur l'autorisation d'accéder à sa caméra (si c'est la première fois que l'accès à la caméra est demandé pour l'origine actuelle). Si l'opération réussit, un MediaStream est renvoyé. Il peut être utilisé par un élément multimédia via l'attribut srcObject :
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
}
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
L'argument constraints vous permet de spécifier les médias à obtenir. Dans cet exemple, seule la vidéo est activée, car l'audio est désactivé par défaut :
const mediaStreamConstraints = {
video: true,
};
Vous pouvez utiliser des contraintes pour des exigences supplémentaires, telles que la résolution vidéo :
const hdConstraints = {
video: {
width: {
min: 1280
},
height: {
min: 720
}
}
}
La spécification MediaTrackConstraints liste tous les types de contraintes potentiels, mais toutes les options ne sont pas compatibles avec tous les navigateurs. Si la résolution demandée n'est pas compatible avec la caméra actuellement sélectionnée, getUserMedia() sera refusé avec un OverconstrainedError et l'utilisateur ne sera pas invité à autoriser l'accès à sa caméra.
Si getUserMedia() réussit, le flux vidéo de la webcam est défini comme source de l'élément vidéo :
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
Points bonus
- L'objet
localStreamtransmis àgetUserMedia()se trouve dans le champ d'application global. Vous pouvez donc l'inspecter à partir de la console du navigateur : ouvrez la console, saisissez stream et appuyez sur Entrée. (Pour afficher la console dans Chrome, appuyez sur Ctrl+Maj+J ou Cmd+Option+J si vous utilisez un Mac.) - Que renvoie
localStream.getVideoTracks()? - Essayez d'appeler le
localStream.getVideoTracks()[0].stop(). - Examinez l'objet de contraintes : que se passe-t-il lorsque vous le remplacez par
{audio: true, video: true}? - Quelle est la taille de l'élément vidéo ? Comment obtenir la taille naturelle d'une vidéo à partir de JavaScript, par opposition à la taille d'affichage ? Pour le vérifier, utilisez les outils pour les développeurs Chrome.
- Essayez d'ajouter des filtres CSS à l'élément vidéo. Exemple :
video {
filter: blur(4px) invert(1) opacity(0.5);
}
- Essayez d'ajouter des filtres SVG. Exemple :
video {
filter: hue-rotate(180deg) saturate(200%);
}
Ce que vous avez appris
Dans cette étape, vous avez appris à :
- Obtenez la vidéo de votre webcam.
- Définissez des contraintes média.
- Modifiez l'élément vidéo.
Une version complète de cette étape se trouve dans le dossier step-01.
Conseils
- N'oubliez pas l'attribut
autoplaydans l'élémentvideo. Sinon, vous ne verrez qu'une seule image ! - Il existe de nombreuses autres options pour les contraintes
getUserMedia(). Consultez la démonstration sur webrtc.github.io/samples/src/content/peerconnection/constraints. Comme vous le verrez, ce site propose de nombreux exemples WebRTC intéressants.
Bonne pratique
- Assurez-vous que l'élément vidéo ne dépasse pas de son conteneur. Nous avons ajouté
widthetmax-widthpour définir une taille idéale et une taille maximale pour la vidéo. Le navigateur calcule automatiquement la hauteur :
video {
max-width: 100%;
width: 320px;
}
Étape suivante
Vous avez une vidéo, mais comment la diffuser ? Vous le découvrirez à l'étape suivante.
5. Diffuser des vidéos en streaming avec RTCPeerConnection
Points abordés
Dans cette étape, vous allez découvrir comment :
- Masquez les différences entre les navigateurs avec le shim WebRTC, adapter.js.
- Utilisez l'API RTCPeerConnection pour diffuser des vidéos.
- Contrôler la capture et la diffusion de contenus multimédias.
Une version complète de cette étape se trouve dans le dossier step-2.
Qu'est-ce que RTCPeerConnection ?
RTCPeerConnection est une API permettant d'effectuer des appels WebRTC pour diffuser des vidéos et de l'audio, et échanger des données.
Cet exemple configure une connexion entre deux objets RTCPeerConnection (appelés pairs) sur la même page.
Peu d'utilité pratique, mais utile pour comprendre le fonctionnement de RTCPeerConnection.
Ajouter des éléments vidéo et des boutons de commande
Dans index.html, remplacez l'élément vidéo unique par deux éléments vidéo et trois boutons :
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
Un élément vidéo affichera le flux de getUserMedia(), tandis que l'autre affichera la même vidéo diffusée via RTCPeerconnection. (Dans une application concrète, un élément vidéo afficherait le flux local et l'autre le flux distant.)
Ajouter le shim adapter.js
Ajoutez un lien vers la version actuelle de adapter.js au-dessus du lien vers main.js :
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
Index.html devrait désormais se présenter comme ceci :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Installer le code RTCPeerConnection
Remplacez main.js par la version du dossier step-02.
Appelez la méthode.
Ouvrez index.html, cliquez sur le bouton Start pour obtenir la vidéo de votre webcam, puis cliquez sur Call pour établir la connexion peer-to-peer. La même vidéo (provenant de votre webcam) devrait s'afficher dans les deux éléments vidéo. Affichez la console du navigateur pour consulter les journaux WebRTC.
Fonctionnement
Cette étape est très importante…
WebRTC utilise l'API RTCPeerConnection pour configurer une connexion permettant de diffuser des vidéos entre les clients WebRTC, appelés pairs.
Dans cet exemple, les deux objets RTCPeerConnection se trouvent sur la même page : pc1 et pc2. Peu d'utilité pratique, mais utile pour montrer comment fonctionnent les API.
La configuration d'un appel entre pairs WebRTC implique trois tâches :
- Créez un RTCPeerConnection pour chaque extrémité de l'appel et, à chaque extrémité, ajoutez le flux local à partir de
getUserMedia(). - Obtenir et partager des informations sur le réseau : les points de terminaison de connexion potentiels sont appelés candidats ICE.
- Obtenir et partager des descriptions locales et distantes : métadonnées sur les contenus multimédias locaux au format SDP.
Imaginons qu'Alice et Bob souhaitent utiliser RTCPeerConnection pour configurer un chat vidéo.
Tout d'abord, Alice et Bob échangent des informations sur le réseau. L'expression "recherche de candidats" fait référence au processus de recherche d'interfaces et de ports réseau à l'aide du framework ICE.
- Alice crée un objet RTCPeerConnection avec un gestionnaire
onicecandidate (addEventListener('icecandidate')). Cela correspond au code suivant de main.js :
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
'iceconnectionstatechange', handleConnectionChange);
- Alice appelle
getUserMedia()et ajoute le flux transmis à celui-ci :
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
then(gotLocalMediaStream).
catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
localStream = mediaStream;
trace('Received local stream.');
callButton.disabled = false; // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
- Le gestionnaire
onicecandidatede l'étape 1 est appelé lorsque des candidats réseau deviennent disponibles. - Alice envoie à Bob des données de candidats sérialisées. Dans une application réelle, ce processus (appelé signalisation) a lieu via un service de messagerie. Vous apprendrez à le faire à une étape ultérieure. Bien sûr, à cette étape, les deux objets RTCPeerConnection se trouvent sur la même page et peuvent communiquer directement sans avoir besoin de messages externes.
- Lorsque Bob reçoit un message candidat d'Alice, il appelle
addIceCandidate()pour ajouter le candidat à la description du pair distant :
function handleConnection(event) {
const peerConnection = event.target;
const iceCandidate = event.candidate;
if (iceCandidate) {
const newIceCandidate = new RTCIceCandidate(iceCandidate);
const otherPeer = getOtherPeer(peerConnection);
otherPeer.addIceCandidate(newIceCandidate)
.then(() => {
handleConnectionSuccess(peerConnection);
}).catch((error) => {
handleConnectionFailure(peerConnection, error);
});
trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
`${event.candidate.candidate}.`);
}
}
Les pairs WebRTC doivent également trouver et échanger des informations sur les contenus audio et vidéo locaux et distants, telles que la résolution et les capacités de codec. La signalisation pour échanger des informations de configuration multimédia se fait en échangeant des blobs de métadonnées, appelés offre et réponse, au format SDP (Session Description Protocol), appelé SDP :
- Alice exécute la méthode
createOffer()RTCPeerConnection. La promesse renvoyée fournit une RTCSessionDescription : description de la session locale d'Alice :
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
.then(createdOffer).catch(setSessionDescriptionError);
- Si l'opération réussit, Alice définit la description locale à l'aide de
setLocalDescription(), puis envoie cette description de session à Bob via son canal de signalisation. - Bob définit la description qu'Alice lui a envoyée comme description distante à l'aide de
setRemoteDescription(). - Bob exécute la méthode RTCPeerConnection
createAnswer(), en lui transmettant la description à distance qu'il a reçue d'Alice. Une session locale compatible avec la sienne peut ainsi être générée. La promessecreateAnswer()transmet une RTCSessionDescription : Bob la définit comme description locale et l'envoie à Alice. - Lorsqu'Alice reçoit la description de session de Bob, elle la définit comme description distante avec
setRemoteDescription().
// Logs offer creation and sets peer connection session descriptions.
function createdOffer(description) {
trace(`Offer from localPeerConnection:\n${description.sdp}`);
trace('localPeerConnection setLocalDescription start.');
localPeerConnection.setLocalDescription(description)
.then(() => {
setLocalDescriptionSuccess(localPeerConnection);
}).catch(setSessionDescriptionError);
trace('remotePeerConnection setRemoteDescription start.');
remotePeerConnection.setRemoteDescription(description)
.then(() => {
setRemoteDescriptionSuccess(remotePeerConnection);
}).catch(setSessionDescriptionError);
trace('remotePeerConnection createAnswer start.');
remotePeerConnection.createAnswer()
.then(createdAnswer)
.catch(setSessionDescriptionError);
}
// Logs answer to offer creation and sets peer connection session descriptions.
function createdAnswer(description) {
trace(`Answer from remotePeerConnection:\n${description.sdp}.`);
trace('remotePeerConnection setLocalDescription start.');
remotePeerConnection.setLocalDescription(description)
.then(() => {
setLocalDescriptionSuccess(remotePeerConnection);
}).catch(setSessionDescriptionError);
trace('localPeerConnection setRemoteDescription start.');
localPeerConnection.setRemoteDescription(description)
.then(() => {
setRemoteDescriptionSuccess(localPeerConnection);
}).catch(setSessionDescriptionError);
}
- Ping !
Points bonus
- Consultez chrome://webrtc-internals. Cela fournit des statistiques WebRTC et des données de débogage. (La liste complète des URL Chrome est disponible sur chrome://about.)
- Définissez le style de la page avec du code CSS :
- Placez les vidéos côte à côte.
- Faites en sorte que les boutons aient la même largeur et que le texte soit plus grand.
- Assurez-vous que la mise en page fonctionne sur mobile.
- Dans la console des outils pour les développeurs Chrome, examinez
localStream,localPeerConnectionetremotePeerConnection. - Dans la console, examinez
localPeerConnectionpc1.localDescription. À quoi ressemble le format SDP ?
Ce que vous avez appris
Dans cette étape, vous avez appris à :
- Masquez les différences entre les navigateurs avec le shim WebRTC, adapter.js.
- Utilisez l'API RTCPeerConnection pour diffuser des vidéos.
- Contrôler la capture et la diffusion de contenus multimédias.
- Partagez des informations sur le réseau et les contenus multimédias entre pairs pour activer un appel WebRTC.
Une version complète de cette étape se trouve dans le dossier step-2.
Conseils
- Il y a beaucoup à apprendre dans cette étape ! Pour trouver d'autres ressources expliquant RTCPeerConnection plus en détail, consultez webrtc.org. Cette page inclut des suggestions de frameworks JavaScript si vous souhaitez utiliser WebRTC, mais que vous ne voulez pas gérer les API.
- Pour en savoir plus sur le shim adapter.js, consultez le dépôt GitHub adapter.js.
- Vous voulez voir à quoi ressemble la meilleure application de chat vidéo au monde ? Consultez AppRTC, l'application canonique du projet WebRTC pour les appels WebRTC : app, code. Le temps d'établissement de l'appel est inférieur à 500 ms.
Bonne pratique
- Pour pérenniser votre code, utilisez les nouvelles API basées sur les promesses et activez la compatibilité avec les navigateurs qui ne les prennent pas en charge en utilisant adapter.js.
Étape suivante
Cette étape montre comment utiliser WebRTC pour diffuser des vidéos entre pairs. Toutefois, cet atelier de programmation porte également sur les données.
Dans l'étape suivante, découvrez comment diffuser des données arbitraires à l'aide de RTCDataChannel.
6. Utiliser RTCDataChannel pour échanger des données
Points abordés
- Échanger des données entre des points de terminaison WebRTC (pairs)
Une version complète de cette étape se trouve dans le dossier step-03.
Mettre à jour votre code HTML
Pour cette étape, vous allez utiliser les canaux de données WebRTC pour envoyer du texte entre deux éléments textarea sur la même page. Ce n'est pas très utile, mais cela montre comment WebRTC peut être utilisé pour partager des données ainsi que des flux vidéo.
Supprimez les éléments vidéo et bouton de index.html et remplacez-les par le code HTML suivant :
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>
<div id="buttons">
<button id="startButton">Start</button>
<button id="sendButton">Send</button>
<button id="closeButton">Stop</button>
</div>
Une zone de texte servira à saisir le texte, tandis que l'autre affichera le texte tel qu'il est transmis entre les pairs.
Le fichier index.html devrait désormais se présenter comme ceci :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>
<div id="buttons">
<button id="startButton">Start</button>
<button id="sendButton">Send</button>
<button id="closeButton">Stop</button>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Mettre à jour votre code JavaScript
Remplacez main.js par le contenu de step-03/js/main.js.
Essayez de diffuser des données entre pairs : ouvrez index.html, appuyez sur Start (Démarrer) pour configurer la connexion entre pairs, saisissez du texte dans textarea à gauche, puis cliquez sur Send (Envoyer) pour transférer le texte à l'aide des canaux de données WebRTC.
Fonctionnement
Ce code utilise RTCPeerConnection et RTCDataChannel pour permettre l'échange de messages texte.
La majeure partie du code de cette étape est identique à celle de l'exemple RTCPeerConnection.
Les fonctions sendData() et createConnection() contiennent la majeure partie du nouveau code :
function createConnection() {
dataChannelSend.placeholder = '';
var servers = null;
pcConstraint = null;
dataConstraint = null;
trace('Using SCTP based data channels');
// For SCTP, reliable and ordered delivery is true by default.
// Add localConnection to global scope to make it visible
// from the browser console.
window.localConnection = localConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object localConnection');
sendChannel = localConnection.createDataChannel('sendDataChannel',
dataConstraint);
trace('Created send data channel');
localConnection.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
// Add remoteConnection to global scope to make it visible
// from the browser console.
window.remoteConnection = remoteConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object remoteConnection');
remoteConnection.onicecandidate = iceCallback2;
remoteConnection.ondatachannel = receiveChannelCallback;
localConnection.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
La syntaxe de RTCDataChannel est délibérément semblable à celle de WebSocket, avec une méthode send() et un événement message.
Notez l'utilisation de dataConstraint. Les canaux de données peuvent être configurés pour permettre différents types de partage de données (par exemple, en privilégiant la fiabilité de la diffusion par rapport aux performances). Pour en savoir plus sur les options, consultez le Mozilla Developer Network.
Points bonus
- Avec SCTP, le protocole utilisé par les canaux de données WebRTC, la transmission fiable et ordonnée des données est activée par défaut. Dans quels cas RTCDataChannel doit-il assurer une transmission fiable des données, et dans quels cas les performances sont-elles plus importantes, même si cela signifie perdre des données ?
- Utilisez CSS pour améliorer la mise en page et ajoutez un attribut d'espace réservé à la zone de texte "dataChannelReceive".
- Testez la page sur un appareil mobile.
Ce que vous avez appris
Dans cette étape, vous avez appris à :
- Établissez une connexion entre deux pairs WebRTC.
- Échangez des données textuelles entre les pairs.
Une version complète de cette étape se trouve dans le dossier step-03.
En savoir plus
- Canaux de données WebRTC (cet article date de quelques années, mais il reste intéressant)
- Pourquoi le protocole SCTP a-t-il été sélectionné pour le canal de données WebRTC ?
Étape suivante
Vous avez appris à échanger des données entre pairs sur la même page, mais comment faire entre différentes machines ? Vous devez d'abord configurer un canal de signalisation pour échanger des messages de métadonnées. Découvrez comment procéder à l'étape suivante.
7. Configurer un service de signalisation pour échanger des messages
Points abordés
Dans cette étape, vous allez découvrir comment :
- Utilisez
npmpour installer les dépendances du projet, comme indiqué dans package.json. - Exécutez un serveur Node.js et utilisez node-static pour diffuser des fichiers statiques.
- Configurez un service de messagerie sur Node.js à l'aide de Socket.IO.
- Utilisez-le pour créer des "salons" et échanger des messages.
Une version complète de cette étape se trouve dans le dossier step-04.
Concepts
Pour configurer et maintenir un appel WebRTC, les clients WebRTC (pairs) doivent échanger des métadonnées :
- Informations sur le candidat (réseau).
- Les messages offer et answer fournissent des informations sur les contenus multimédias, comme la résolution et les codecs.
En d'autres termes, un échange de métadonnées est nécessaire avant que la diffusion en pair à pair de données audio, vidéo ou autres puisse avoir lieu. Ce processus est appelé signalisation.
Dans les étapes précédentes, les objets RTCPeerConnection de l'expéditeur et du destinataire se trouvent sur la même page. La "signalisation" consiste donc simplement à transmettre des métadonnées entre les objets.
Dans une application réelle, les RTCPeerConnections de l'expéditeur et du destinataire s'exécutent sur des pages Web sur différents appareils. Vous avez donc besoin d'un moyen pour qu'ils communiquent des métadonnées.
Pour ce faire, vous utilisez un serveur de signalisation, c'est-à-dire un serveur qui peut transmettre des messages entre les clients WebRTC (pairs). Les messages eux-mêmes sont en texte brut : des objets JavaScript sérialisés.
Prérequis : installer Node.js
Pour exécuter les prochaines étapes de cet atelier de programmation (dossiers step-04 à step-06), vous devez exécuter un serveur sur localhost à l'aide de Node.js.
Vous pouvez télécharger et installer Node.js à partir de ce lien ou via le gestionnaire de paquets de votre choix.
Une fois installé, vous pourrez importer les dépendances requises pour les étapes suivantes (exécution de npm install), ainsi qu'exécuter un petit serveur localhost pour exécuter l'atelier de programmation (exécution de node index.js). Ces commandes seront indiquées ultérieurement, lorsqu'elles seront nécessaires.
À propos de l'application
WebRTC utilise une API JavaScript côté client, mais nécessite également un serveur de signalisation (messagerie), ainsi que des serveurs STUN et TURN pour une utilisation réelle. Pour en savoir plus, cliquez ici.
Dans cette étape, vous allez créer un serveur de signalisation Node.js simple à l'aide du module Node.js Socket.IO et de la bibliothèque JavaScript pour la messagerie. Une expérience avec Node.js et Socket.IO sera utile, mais pas indispensable, car les composants de messagerie sont très simples.
Dans cet exemple, le serveur (l'application Node.js) est implémenté dans index.js, et le client qui s'exécute dessus (l'application Web) est implémenté dans index.html.
L'application Node.js de cette étape a deux tâches.
Tout d'abord, il sert de relais de messages :
socket.on('message', function (message) {
log('Got message: ', message);
socket.broadcast.emit('message', message);
});
Ensuite, il gère les "salons" de chat vidéo WebRTC :
if (numClients === 0) {
socket.join(room);
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
socket.emit('full', room);
}
Notre application WebRTC simple permettra à un maximum de deux pairs de partager une salle.
HTML et JavaScript
Modifiez index.html pour qu'il se présente comme suit :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<script src="/socket.io/socket.io.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Vous ne verrez rien sur la page à cette étape : tous les journaux sont enregistrés dans la console du navigateur. (Pour afficher la console dans Chrome, appuyez sur Ctrl+Maj+J ou Cmd+Option+J si vous utilisez un Mac.)
Remplacez js/main.js par ce qui suit :
'use strict';
var isInitiator;
window.room = prompt("Enter room name:");
var socket = io.connect();
if (room !== "") {
console.log('Message from client: Asking to join room ' + room);
socket.emit('create or join', room);
}
socket.on('created', function(room, clientId) {
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Message from client: Room ' + room + ' is full :^(');
});
socket.on('ipaddr', function(ipaddr) {
console.log('Message from client: Server IP address is ' + ipaddr);
});
socket.on('joined', function(room, clientId) {
isInitiator = false;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
Configurer Socket.IO pour qu'il s'exécute sur Node.js
Dans le fichier HTML, vous avez peut-être remarqué que vous utilisez un fichier Socket.IO :
<script src="/socket.io/socket.io.js"></script>
Au niveau supérieur de votre répertoire work, créez un fichier nommé package.json avec le contenu suivant :
{
"name": "webrtc-codelab",
"version": "0.0.1",
"description": "WebRTC codelab",
"dependencies": {
"node-static": "^0.7.10",
"socket.io": "^1.2.0"
}
}
Il s'agit d'un fichier manifeste d'application qui indique au gestionnaire de paquets Node (npm) les dépendances de projet à installer.
Pour installer les dépendances (telles que /socket.io/socket.io.js), exécutez la commande suivante à partir du terminal de ligne de commande, dans votre répertoire work :
npm install
Vous devriez voir un journal d'installation qui se termine par quelque chose comme ceci :

Comme vous pouvez le voir, npm a installé les dépendances définies dans package.json.
Créez un fichier index.js au niveau supérieur de votre répertoire work (pas dans le répertoire js) et ajoutez le code suivant :
'use strict';
var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');
var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
fileServer.serve(req, res);
}).listen(8080);
var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {
// convenience function to log server messages on the client
function log() {
var array = ['Message from server:'];
array.push.apply(array, arguments);
socket.emit('log', array);
}
socket.on('message', function(message) {
log('Client said: ', message);
// for a real app, would be room-only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function(room) {
log('Received request to create or join room ' + room);
var clientsInRoom = io.sockets.adapter.rooms[room];
var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 0) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
socket.emit('full', room);
}
});
socket.on('ipaddr', function() {
var ifaces = os.networkInterfaces();
for (var dev in ifaces) {
ifaces[dev].forEach(function(details) {
if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
socket.emit('ipaddr', details.address);
}
});
}
});
});
À partir du terminal de ligne de commande, exécutez la commande suivante dans le répertoire work :
node index.js
Dans votre navigateur, ouvrez localhost:8080.
Chaque fois que vous ouvrez cette URL, vous êtes invité à saisir un nom de pièce. Pour rejoindre la même pièce, choisissez le même nom à chaque fois, par exemple "foo".
Ouvrez une page "Nouvel onglet", puis localhost:8080. Choisissez le même nom de pièce.
Ouvrez localhost:8080 dans un troisième onglet ou une troisième fenêtre. Sélectionnez à nouveau le même nom de pièce.
Consultez la console dans chacun des onglets : vous devriez voir la journalisation du code JavaScript ci-dessus.
Points bonus
- Quels autres mécanismes de messagerie sont possibles ? Quels problèmes pouvez-vous rencontrer en utilisant un WebSocket "pur" ?
- Quels problèmes peuvent survenir lors de la mise à l'échelle de cette application ? Pouvez-vous développer une méthode pour tester des milliers ou des millions de demandes de salles simultanées ?
- Cette application utilise une invite JavaScript pour obtenir le nom d'une pièce. Trouvez un moyen d'obtenir le nom de la pièce à partir de l'URL. Par exemple, localhost:8080/foo donnerait le nom de pièce
foo.
Ce que vous avez appris
Dans cette étape, vous avez appris à :
- Utilisez npm pour installer les dépendances du projet spécifiées dans package.json.
- Exécutez un serveur Node.js pour diffuser des fichiers statiques.
- Configurez un service de messagerie sur Node.js à l'aide de socket.io.
- Utilisez-le pour créer des "salons" et échanger des messages.
Une version complète de cette étape se trouve dans le dossier step-04.
En savoir plus
- Dépôt d'exemple de chat Socket.io
- WebRTC dans le monde réel : STUN, TURN et signalisation
- Le terme "signalisation" dans WebRTC
Étape suivante
Découvrez comment utiliser la signalisation pour permettre à deux utilisateurs d'établir une connexion peer-to-peer.
8. Combiner la connexion et la signalisation entre pairs
Points abordés
Dans cette étape, vous allez découvrir comment :
- Exécuter un service de signalisation WebRTC à l'aide de Socket.IO sur Node.js
- Utilisez ce service pour échanger des métadonnées WebRTC entre pairs.
Une version complète de cette étape se trouve dans le dossier step-05.
Remplacer le code HTML et JavaScript
Remplacez le contenu de index.html par le code suivant :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<div id="videos">
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Remplacez le contenu de js/main.js par celui de step-05/js/main.js.
Exécuter le serveur Node.js
Si vous ne suivez pas cet atelier de programmation à partir de votre répertoire work, vous devrez peut-être installer les dépendances pour le dossier step-05 ou votre dossier de travail actuel. Exécutez la commande suivante à partir de votre répertoire de travail :
npm install
Une fois installé, si votre serveur Node.js n'est pas en cours d'exécution, démarrez-le en appelant la commande suivante dans le répertoire work :
node index.js
Assurez-vous d'utiliser la version de index.js de l'étape précédente qui implémente Socket.IO. Pour en savoir plus sur Node et Socket IO, consultez la section "Configurer un service de signalisation pour échanger des messages".
Dans votre navigateur, ouvrez localhost:8080.
Ouvrez à nouveau localhost:8080 dans un nouvel onglet ou une nouvelle fenêtre. Un élément vidéo affichera le flux local de getUserMedia(), tandis que l'autre affichera la vidéo "à distance" diffusée via RTCPeerconnection.
Affichez la journalisation dans la console du navigateur.
Points bonus
- Cette application ne prend en charge que le chat vidéo en tête-à-tête. Comment modifieriez-vous la conception pour permettre à plusieurs personnes de partager le même salon de chat vidéo ?
- Dans l'exemple, le nom de la pièce foo est codé en dur. Quelle serait la meilleure façon d'activer d'autres noms de pièces ?
- Comment les utilisateurs partageraient-ils le nom de la salle ? Essayez de trouver une autre solution que le partage des noms des espaces.
- Comment pourrais-tu modifier l'application ?
Ce que vous avez appris
Dans cette étape, vous avez appris à :
- Exécutez un service de signalisation WebRTC à l'aide de Socket.IO exécuté sur Node.js.
- Utilisez ce service pour échanger des métadonnées WebRTC entre pairs.
Une version complète de cette étape se trouve dans le dossier step-05.
Conseils
- Les statistiques et les données de débogage WebRTC sont disponibles sur chrome://webrtc-internals.
- Vous pouvez utiliser test.webrtc.org pour vérifier votre environnement local et tester votre caméra et votre micro.
- Si vous rencontrez des problèmes de mise en cache, essayez les solutions suivantes :
- Effectuez une actualisation forcée en maintenant la touche Ctrl enfoncée et en cliquant sur le bouton Actualiser.
- Redémarrez le navigateur.
- Exécutez
npm cache cleanà partir de la ligne de commande.
Étape suivante
Découvrez comment prendre une photo, obtenir les données de l'image et les partager entre pairs distants.
9. Prendre une photo et la partager via un canal de données
Points abordés
Dans cette étape, vous allez apprendre à :
- Prenez une photo et récupérez les données à partir de l'élément canvas.
- Échangez des données d'image avec un utilisateur distant.
Une version complète de cette étape se trouve dans le dossier step-06.
Fonctionnement
Vous avez appris précédemment à échanger des messages texte à l'aide de RTCDataChannel.
Cette étape permet de partager des fichiers entiers (dans cet exemple, des photos prises avec getUserMedia()).
Les principales étapes sont les suivantes :
- Établissez un canal de données. Notez que vous n'ajoutez aucun flux multimédia à la connexion pair-à-pair lors de cette étape.
- Capturez le flux vidéo de la webcam de l'utilisateur avec
getUserMedia():
var video = document.getElementById('video');
function grabWebCamVideo() {
console.log('Getting user media (video) ...');
navigator.mediaDevices.getUserMedia({
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
- Lorsque l'utilisateur clique sur le bouton Snap, récupérez un instantané (une image vidéo) du flux vidéo et affichez-le dans un élément
canvas:
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');
function snapPhoto() {
photoContext.drawImage(video, 0, 0, photo.width, photo.height);
show(photo, sendBtn);
}
- Lorsque l'utilisateur clique sur le bouton Envoyer, convertissez l'image en octets et envoyez-la via un canal de données :
function sendPhoto() {
// Split data channel message in chunks of this byte length.
var CHUNK_LEN = 64000;
var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
len = img.data.byteLength,
n = len / CHUNK_LEN | 0;
console.log('Sending a total of ' + len + ' byte(s)');
dataChannel.send(len);
// split the photo and send in chunks of about 64KB
for (var i = 0; i < n; i++) {
var start = i * CHUNK_LEN,
end = (i + 1) * CHUNK_LEN;
console.log(start + ' - ' + (end - 1));
dataChannel.send(img.data.subarray(start, end));
}
// send the reminder, if any
if (len % CHUNK_LEN) {
console.log('last ' + len % CHUNK_LEN + ' byte(s)');
dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}
}
- Le côté réception reconvertit les octets du message du canal de données en image et l'affiche à l'utilisateur :
function receiveDataChromeFactory() {
var buf, count;
return function onmessage(event) {
if (typeof event.data === 'string') {
buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
count = 0;
console.log('Expecting a total of ' + buf.byteLength + ' bytes');
return;
}
var data = new Uint8ClampedArray(event.data);
buf.set(data, count);
count += data.byteLength;
console.log('count: ' + count);
if (count === buf.byteLength) {
// we're done: all data chunks have been received
console.log('Done. Rendering photo.');
renderPhoto(buf);
}
};
}
function renderPhoto(data) {
var canvas = document.createElement('canvas');
canvas.width = photoContextW;
canvas.height = photoContextH;
canvas.classList.add('incomingPhoto');
// trail is the element holding the incoming images
trail.insertBefore(canvas, trail.firstChild);
var context = canvas.getContext('2d');
var img = context.createImageData(photoContextW, photoContextH);
img.data.set(data);
context.putImageData(img, 0, 0);
}
Obtenir le code
Remplacez le contenu de votre dossier work par celui de step-06. Votre fichier index.html dans work devrait maintenant se présenter comme suit :
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<h2>
<span>Room URL: </span><span id="url">...</span>
</h2>
<div id="videoCanvas">
<video id="camera" autoplay></video>
<canvas id="photo"></canvas>
</div>
<div id="buttons">
<button id="snap">Snap</button><span> then </span><button id="send">Send</button>
<span> or </span>
<button id="snapAndSend">Snap & Send</button>
</div>
<div id="incoming">
<h2>Incoming photos</h2>
<div id="trail"></div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Si vous ne suivez pas cet atelier de programmation depuis votre répertoire work, vous devrez peut-être installer les dépendances pour le dossier step-06 ou votre dossier de travail actuel. Il vous suffit d'exécuter la commande suivante à partir de votre répertoire de travail :
npm install
Une fois installé, si votre serveur Node.js n'est pas en cours d'exécution, démarrez-le en appelant la commande suivante depuis votre répertoire work :
node index.js
Assurez-vous d'utiliser la version de index.js qui implémente Socket.IO et n'oubliez pas de redémarrer votre serveur Node.js si vous apportez des modifications. Pour en savoir plus sur Node et Socket IO, consultez la section "Configurer un service de signalisation pour échanger des messages".
Si nécessaire, cliquez sur le bouton Autoriser pour permettre à l'application d'utiliser votre webcam.
L'application crée un ID de salle aléatoire et l'ajoute à l'URL. Ouvrez l'URL de la barre d'adresse dans un nouvel onglet ou une nouvelle fenêtre de navigateur.
Cliquez sur le bouton Snap & Send (Prendre et envoyer), puis consultez la section "Incoming" (Reçus) dans l'autre onglet en bas de la page. L'application transfère les photos entre les onglets.
L'écran qui s'affiche devrait ressembler à ce qui suit :

Points bonus
- Comment modifier le code pour pouvoir partager n'importe quel type de fichier ?
En savoir plus
- L'API MediaStream Image Capture : une API permettant de prendre des photos et de contrôler des caméras, bientôt disponible dans votre navigateur !
- L'API MediaRecorder, pour enregistrer de l'audio et des vidéos : démonstration, documentation.
Ce que vous avez appris
- Comment prendre une photo et obtenir les données à partir de celle-ci à l'aide de l'élément canvas.
- Comment échanger ces données avec un utilisateur distant.
Une version complète de cette étape se trouve dans le dossier step-06.
10. Félicitations
Vous avez créé une application pour le streaming vidéo et l'échange de données en temps réel.
Ce que vous avez appris
Dans cet atelier de programmation, vous avez appris ce qui suit :
- Obtenez la vidéo de votre webcam.
- Diffuser des vidéos en streaming avec RTCPeerConnection.
- Transmettez des données avec RTCDataChannel.
- Configurez un service de signalisation pour échanger des messages.
- Combiner la connexion peer-to-peer et la signalisation.
- Prenez une photo et partagez-la via un canal de données.
Étapes suivantes
- Consultez le code et l'architecture de l'application de chat WebRTC canonique AppRTC : app, code.
- Essayez les démonstrations en direct sur github.com/webrtc/samples.
En savoir plus
- De nombreuses ressources pour vous aider à débuter avec WebRTC sont disponibles sur webrtc.org.