Code

WebRTC – #1 Video-Chat in javascript

23 Dicembre 2015 - 5 minuti di lettura

Con la tecnologia WebRTC è possibile integrare, all’interno di applicazioni che comprendono JavaScript, funzionalità di audio e video-chat, registrazione chat, condivisione schermo e signaling.
Questo permette di offrire per il nostro applicativo una serie di servizi aggiuntivi che possono essere direttamente controllati senza dover ricorrere ad applicazioni esterne.

Di seguito verrà fornito un tutorial dove verranno utilizzati:

  1. una pagina HTML e relativo JavaScript;
  2. un file di configurazione e gestione per un server Node.js.

Per i differenti browser che supportano WebRTC ci sono alcune piccole differenze nel codice. Per semplificare la comprensione di questo tutorial si è deciso di utilizzare solo Chrome.

Architettura

Tramite l’utilizzo di RTCPeerConnection è possibile instaurare una connessione peer-to-peer tra client e trasmettere in maniera efficiente un flusso di dati. Quando un client vuole condividere informazioni crea un’offerta a cui gli altri client possono rispondere.
In questo modo i client acquisiscono i rispettivi riferimenti ed è possibile il transito delle informazioni. A questo punto i vari client decidono cosa condividere tra di loro.

Il Web server dell’applicazione deve gestire la semplice parte iniziale di comunicazione con la quale vengono scambiate offerta e risposte. In questo modo possiamo anche controllare chi può ricevere le offerte da un chiamante tramite una lista di contatti oppure con la creazione di “stanze” nelle quali ospitare le chat.

Normalmente questo tipo di comunicazioni avvengono tra client non appartenenti alla stessa rete locale e quindi è possibile che si debbano superare proxy, firewall e associare  indirizzi pubblici. Per questo è necessario un server Interactive Connectivity Establishment che permette di superare questi ostacoli.

Per questo tutorial utilizzeremo un server ICE messo a disposizione gratuitamente da Google “stun.l.google.com:19302”

Sviluppo

Pagina HTML

La pagina da realizzare prevede due tag <video> nelle quali inseriremo il nostro video locale e il video remoto. Inoltre sono presenti quattro pulsanti per attivare la connessione, chiamare l’interlocutore, condividere audio e video, chiudere la connessione.
Ecco il codice:

<script src=”https://cdn.socket.io/socket.io-1.3.7.js”></script>
<video id=”myVideo” autoplay=”autoplay” width=”300″ height=”150″></video>
<video id=”otherVideo” autoplay=”autoplay” width=”300″ height=”150″></video>
<div><button id=”connectButton”>Connect</button>
<button id=”callButton”>Call</button>
<button id=”shareButton”>Share</button>
<button id=”disconnectButton”>Disconnect</button></div>
<script><br />
var localStream, peerConnection;<br />
var RTCPeerConnection = null;<br />
var getUserMedia = null;<br />
var servers = null;<br />
var socket = null;<br />
function setupPage(){<br />
var myVideo = document.getElementById(“myVideo”);<br />
var otherVideo = document.getElementById(“otherVideo”);<br />
var connectButton = document.getElementById(“connectButton”);<br />
var callButton = document.getElementById(“callButton”);<br />
var shareButton = document.getElementById(“shareButton”);<br />
var disconnectButton = document.getElementById(“disconnectButton”);<br />
connectButton.onclick = connect;<br />
callButton.onclick = callOther;<br />
shareButton.onclick = toggleSharing;<br />
disconnectButton.onclick = disconnect; setupButtonState();<br />
}<br />
function setupButtonState(){<br />
connectButton.disabled = false;<br />
callButton.disabled = true;<br />
shareButton.disabled = true;<br />
disconnectButton.disabled = true;<br />
}<br />
function setupVariables(){<br />
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);<br />
RTCPeerConnection = webkitRTCPeerConnection;<br />
servers =[‘stun:stun.l.google.com:19302’];<br />
}<br />
setupPage();<br />
</script>

Acquisire audio e video locali

La prima funzionalità da sviluppare è l’acquisizione di audio e video locali.
Tramite le Media Stream API è possibile accedere all’audio e al video del dispositivo in uso.

Inoltre tramite il primo pulsante inizializzeremo l’oggetto RTCPeerConnection e avvieremo la connessione con il webserver.

function setupVariables(){
  getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
  RTCPeerConnection = webkitRTCPeerConnection;
  servers =['stun:stun.l.google.com:19302'];
}
setupVariables();
function connect() {
  connectButton.disabled = true;
  disconnectButton.disabled = false;
  connectToSignalServer();
  getUserMedia({audio:false, video:false}, showStream, handleError);
}
function connectToSignalServer(){
  socket = io.connect('http://192.168.5.103:2013');
  socket.on('incomingCall', function(message){
    createAnswer(message);
  });
  socket.on('receiveAnswer', function(message){
    elaborateAnswer(message);
  });
  socket.on('receiveIce', function(message){ 
    receiveIce(message); 
  });
}
function showStream(stream){
  myVideo.src = window.URL.createObjectURL(stream);
  localStream = stream;
  callButton.disabled = false;
  setupPeerConnection();
}
function setupPeerConnection(stream){
  peerConnection = new RTCPeerConnection(servers);
  peerConnection.onicecandidate = gotIceCandidate;
  peerConnection.addStream(localStream);
  peerConnection.onaddstream = showRemoteStream;
}

Avvio della chiamata

Alla pressione del pulsante di chiamata viene creata l’offerta che riporta informazioni utili sull’utente locale. Questa offerta viene poi inviata al Web server che la distribuirà secondo determinate regole (lista contatti, room di chat) agli altri client in ascolto.

function callOther() {
  callButton.disabled = true;
  peerConnection.createOffer(gotLocalDescriptionOnCall,handleError);
}
function gotLocalDescriptionOnCall(offer) {
  peerConnection.setLocalDescription(offer);
  socket.emit('callRequest', JSON.stringify({'sdp': offer}));
};

Alla ricezione di un’offerta i client interessati rispondono inviando a loro volta le medesime informazioni.

function createAnswer(message){
  var signal = JSON.parse(message);
  peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp), function() {
    peerConnection.createAnswer(gotLocalDescriptionOnAnswer); 
  });
}
function gotLocalDescriptionOnAnswer(answer) {
  peerConnection.setLocalDescription(answer);
  socket.emit('sendAnswer', JSON.stringify({'sdp': answer}));
};

Alla ricezione delle risposte vengono create le connessioni dirette tra i client sfruttando le informazioni ritornate dal server ICE e che i client si scambiano ancora vicendevolmente tramite il Web server.

function elaborateAnswer(answer){
  var signal = JSON.parse(answer);
  peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp));
}
function gotIceCandidate(event) {
  if(event.candidate != null) {
    socket.emit('sendIce',JSON.stringify({'ice': event.candidate}));
  }
}
function receiveIce(message){
  var signal = JSON.parse(message);
  peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice));
}

A questo punto la connessione è stabilita ed è sufficiente abilitare la condivisione di audio e video perché le informazioni vengano trasmesse agli altri client.

function showRemoteStream(event){
  otherVideo.src = window.URL.createObjectURL(event.stream);
}
function toggleSharing = function(){
  localStream.getVideoTracks()[0].enabled = !(localStream.getVideoTracks()[0].enabled);
  localStream.getAudioTracks()[0].enabled = !(localStream.getAudioTracks()[0].enabled);
}

Server

Per l’implementazione del Web server abbiamo utilizzato Node.Js con la configurazione qui sotto riportata.
Salvare la configurazione in un file con nome server.js, e nello stesso folder salvare il file index.html.
Lanciare il terminale, portarsi nella directory contenente i file ed eseguire “node server.js”.

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

A questo punto è sufficiente aprire Chrome e richiamare l’indirizzo http://localhost:2003.
Utilizzando lo stesso browser aprire un’altra tab e richiamare lo stesso indirizzo.
Eseguire la sequenza di connessione, chiamata e condivisione per vedere su entrambe le tab lo scambio dei due stream.
Nel prossimo articolo modificheremo la configurazione del server per condividere le informazioni in HTTPS e poter effettuare la video-chat tra due dispositivi differenti.

Nelle prossime uscite

  1. #2 Aggiungere una chat testuale alla videoconferenza.
  2. #3 Inviare e ricevere segnali codificati per eseguire azioni.
  3. #4 Inviare e rivedere file.
  4. #5 Condividere lo schermo.
Articolo scritto da