WebRTC - Demo vocale

In questo capitolo, creeremo un'applicazione client che consente a due utenti su dispositivi separati di comunicare utilizzando flussi audio WebRTC. La nostra applicazione avrà due pagine. Uno per il login e l'altro per effettuare una chiamata audio a un altro utente.

Le due pagine saranno i tag div . La maggior parte dell'input viene eseguita tramite semplici gestori di eventi.

Server di segnalazione

Per creare una connessione WebRTC, i client devono essere in grado di trasferire messaggi senza utilizzare una connessione peer WebRTC. Qui è dove utilizzeremo HTML5 WebSocket, una connessione socket bidirezionale tra due endpoint: un server web e un browser web. Ora iniziamo a utilizzare la libreria WebSocket. Crea il file server.js e inserisci il seguente codice:

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090});
  
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server"); 
});

La prima riga richiede la libreria WebSocket che abbiamo già installato. Quindi creiamo un server socket sulla porta 9090. Successivamente, ascoltiamo l' evento di connessione . Questo codice verrà eseguito quando un utente effettua una connessione WebSocket al server. Quindi ascoltiamo tutti i messaggi inviati dall'utente. Infine, inviamo una risposta all'utente connesso dicendo "Hello from server".

Nel nostro server di segnalazione, utilizzeremo un nome utente basato su stringa per ogni connessione in modo da sapere dove inviare i messaggi. Cambiamo un po 'il nostro gestore di connessione -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON");
      data = {}; 
   } 
});

In questo modo accettiamo solo messaggi JSON. Successivamente, dobbiamo archiviare tutti gli utenti connessi da qualche parte. Useremo un semplice oggetto Javascript per questo. Cambia la parte superiore del nostro file -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

Aggiungeremo un campo tipo per ogni messaggio proveniente dal client. Ad esempio, se un utente desidera accedere, invia il messaggio del tipo di accesso . Definiamolo -

connection.on('message', function(message) {
  
   var data; 
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login",
               success: false 
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
			
         break;
			
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break; 
   }  
});

Se l'utente invia un messaggio con il tipo di login , noi:

  • Controlla se qualcuno ha già effettuato l'accesso con questo nome utente.
  • In tal caso, informa l'utente che non ha effettuato correttamente l'accesso.
  • Se nessuno utilizza questo nome utente, aggiungiamo nome utente come chiave all'oggetto connessione.
  • Se un comando non viene riconosciuto inviamo un errore.

Il codice seguente è una funzione di supporto per l'invio di messaggi a una connessione. Aggiungilo al file server.js -

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Quando l'utente si disconnette, dobbiamo pulire la sua connessione. Possiamo eliminare l'utente quando viene attivato l' evento di chiusura . Aggiungere il codice seguente al gestore della connessione -

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

Dopo aver effettuato correttamente l'accesso, l'utente desidera chiamare un altro. Dovrebbe fare un'offerta a un altro utente per ottenerlo. Aggiungi il gestore dell'offerta -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name); 
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      //setting that UserA connected with UserB 
      connection.otherName = data.name; 
      sendTo(conn, { 
         type: "offer", 
         offer: data.offer, 
         name: connection.name 
      });
   }		
	
   break;

In primo luogo, otteniamo la connessione dell'utente che stiamo cercando di chiamare. Se esiste, gli inviamo i dettagli dell'offerta . Aggiungiamo anche otherName al collegamento all'oggetto. Questo è fatto per la semplicità di trovarlo in seguito.

La risposta alla risposta ha un modello simile che abbiamo utilizzato nel gestore dell'offerta . Il nostro server passa semplicemente attraverso tutti i messaggi come risposta a un altro utente. Aggiungi il codice seguente dopo il gestore dell'offerta :

case "answer": 
   console.log("Sending answer to: ", data.name); 
   //for ex. UserB answers UserA
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name;
		
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
	
   break;

La parte finale sta gestendo il candidato ICE tra gli utenti. Usiamo la stessa tecnica solo passando i messaggi tra gli utenti. La differenza principale è che i messaggi candidati potrebbero essere visualizzati più volte per utente in qualsiasi ordine. Aggiungi il gestore candidato -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
	
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
	
   break;

Per consentire ai nostri utenti di disconnettersi da un altro utente dovremmo implementare la funzione di riaggancio. Inoltre dirà al server di eliminare tutti i riferimenti utente. Aggiungi il gestore delle ferie -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null; 
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, {
         type: "leave" 
      }); 
   }  
	
   break;

Questo invierà anche all'altro utente l' evento di congedo in modo che possa disconnettere la sua connessione peer di conseguenza. Dovremmo anche gestire il caso in cui un utente interrompe la connessione dal server di segnalazione. Modifichiamo il nostro gestore vicino -

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            }); 
         }
			
      } 
   } 
});

Di seguito è riportato l'intero codice del nostro server di segnalazione:

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
	
      var data;
		
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login": 
            console.log("User logged", data.name); 
				
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name;
					
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            } 
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               });
            } 
				
            break;
				
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
				
            break;
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            });
				
            break; 
      }  
   });
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
         delete users[connection.name]; 
			
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
              }); 
            }  
         } 
      } 
   });  
	
   connection.send("Hello world"); 
}); 
 
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Applicazione client

Un modo per testare questa applicazione è aprire due schede del browser e provare a scambiarsi una chiamata audio.

Prima di tutto, dobbiamo installare la libreria bootstrap . Bootstrap è un framework di frontend per lo sviluppo di applicazioni web. Puoi saperne di più suhttp://getbootstrap.com/.Crea una cartella chiamata, ad esempio, "audiochat". Questa sarà la nostra cartella principale dell'applicazione. All'interno di questa cartella crea un file package.json (è necessario per la gestione delle dipendenze npm) e aggiungi quanto segue:

{ 
   "name": "webrtc-audiochat", 
   "version": "0.1.0", 
   "description": "webrtc-audiochat", 
   "author": "Author", 
   "license": "BSD-2-Clause" 
}

Quindi esegui npm install bootstrap . Questo installerà la libreria bootstrap nella cartella audiochat / node_modules .

Ora dobbiamo creare una pagina HTML di base. Crea un file index.html nella cartella principale con il codice seguente:

<html>
 
   <head> 
      <title>WebRTC Voice Demo</title> 
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/> 
   </head>
 
   <style> 
      body { 
         background: #eee; 
         padding: 5% 0; 
      } 
   </style>
	
   <body> 
      <div id = "loginPage" class = "container text-center"> 
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4">
				
               <h2>WebRTC Voice Demo. Please sign in</h2>
				
               <label for = "usernameInput" class = "sr-only">Login</label> 
               <input type = "email" id = "usernameInput" 
                  class = "form-control formgroup"
                  placeholder = "Login" required = "" autofocus = ""> 
               <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock">
                  Sign in</button> 
            </div> 
         </div> 
			
      </div>
		
      <div id = "callPage" class = "call-page">
		
         <div class = "row"> 
			
            <div class = "col-md-6 text-right"> 
               Local audio: <audio id = "localAudio" 
               controls autoplay></audio> 
            </div>
				
            <div class = "col-md-6 text-left"> 
               Remote audio: <audio id = "remoteAudio" 
                  controls autoplay></audio> 
            </div> 
				
         </div> 
			
         <div class = "row text-center"> 
            <div class = "col-md-12"> 
               <input id = "callToUsernameInput" 
                  type = "text" placeholder = "username to call" /> 
               <button id = "callBtn" class = "btn-success btn">Call</button> 
               <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
            </div> 
         </div>
			
      </div> 
		
      <script src = "client.js"></script> 
		
   </body>
	
</html>

Questa pagina dovrebbe esserti familiare. Abbiamo aggiunto il file css bootstrap . Abbiamo anche definito due pagine. Infine, abbiamo creato diversi campi di testo e pulsanti per ottenere informazioni dall'utente. Dovresti vedere i due elementi audio per i flussi audio locali e remoti. Notare che abbiamo aggiunto un collegamento a un file client.js .

Ora dobbiamo stabilire una connessione con il nostro server di segnalazione. Crea il file client.js nella cartella principale con il codice seguente:

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
}; 
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);  
   var data = JSON.parse(msg.data);  
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break;
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};

Ora esegui il nostro server di segnalazione tramite il server del nodo . Quindi, all'interno della cartella principale, esegui il comando statico e apri la pagina all'interno del browser. Dovresti vedere il seguente output della console:

Il passaggio successivo consiste nell'implementazione di un accesso utente con un nome utente univoco. Inviamo semplicemente un nome utente al server, che poi ci dice se è stato preso o meno. Aggiungi il seguente codice al tuo file client.js -

//****** 
//UI selectors block 
//******

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn');
 
var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn');
 
var hangUpBtn = document.querySelector('#hangUpBtn');
  
callPage.style.display = "none";
  
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value;
	
   if (name.length > 0) { 
      send({
         type: "login", 
         name: name 
      }); 
   } 
	
}); 
 
function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //**********************
		         
   } 
	
};

In primo luogo, selezioniamo alcuni riferimenti agli elementi sulla pagina. Nascondiamo la pagina della chiamata. Quindi, aggiungiamo un listener di eventi sul pulsante di accesso. Quando l'utente fa clic, inviamo il suo nome utente al server. Infine, implementiamo il callback handleLogin. Se il login è andato a buon fine, mostriamo la pagina della chiamata e iniziamo a impostare una connessione peer.

Per avviare una connessione peer abbiamo bisogno di:

  • Ottieni un flusso audio da un microfono
  • Crea l'oggetto RTCPeerConnection

Aggiungi il seguente codice al "blocco dei selettori dell'interfaccia utente":

var localAudio = document.querySelector('#localAudio'); 
var remoteAudio = document.querySelector('#remoteAudio'); 

var yourConn; 
var stream;

Modifica la funzione handleLogin -

function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local audio stream 
      navigator.webkitGetUserMedia({ video: false, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local audio stream on the page
         localAudio.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration); 
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteAudio.src = window.URL.createObjectURL(e.stream); 
         }; 
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) { 
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
               }); 
            } 
         };  
			
      }, function (error) { 
         console.log(error); 
      }); 
		
   } 
};

Ora, se esegui il codice, la pagina dovrebbe consentirti di accedere e visualizzare il tuo flusso audio locale sulla pagina.

Ora siamo pronti per avviare una chiamata. In primo luogo, inviamo un'offerta a un altro utente. Una volta che un utente riceve l'offerta, crea una risposta e inizia a fare trading con i candidati ICE. Aggiungi il codice seguente al file client.js :

//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
	
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer",
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};

Aggiungiamo un gestore di clic al pulsante Chiama, che avvia un'offerta. Quindi implementiamo diversi gestori attesi dal gestore onmessage . Verranno elaborati in modo asincrono finché entrambi gli utenti non avranno stabilito una connessione.

L'ultimo passaggio consiste nell'implementazione della funzionalità di blocco. Ciò interromperà la trasmissione dei dati e comunicherà all'altro utente di chiudere la chiamata. Aggiungi il seguente codice -

//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   });  
	
   handleLeave(); 
});
  
function handleLeave() { 
   connectedUser = null; 
   remoteAudio.src = null;
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null;
};

Quando l'utente fa clic sul pulsante Riaggancia:

  • Invierà un messaggio di "permesso" all'altro utente
  • Chiuderà RTCPeerConnection e distruggerà la connessione localmente

Ora esegui il codice. Dovresti essere in grado di accedere al server utilizzando due schede del browser. È quindi possibile effettuare una chiamata audio alla scheda e terminare la chiamata.

Quello che segue è l'intero file client.js -

//our username 
var name; 
var connectedUser;
 
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
 
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
};
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data); 
   var data = JSON.parse(msg.data); 
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
}; 

conn.onerror = function (err) { 
   console.log("Got error", err); 
};
 
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};
 
//****** 
//UI selectors block 
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn');

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
var localAudio = document.querySelector('#localAudio'); 
var remoteAudio = document.querySelector('#remoteAudio'); 

var yourConn; 
var stream; 

callPage.style.display = "none";
 
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local audio stream 
      navigator.webkitGetUserMedia({ video: false, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local audio stream on the page 
         localAudio.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration); 
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteAudio.src = window.URL.createObjectURL(e.stream); 
         }; 
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) { 
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
                  candidate: event.candidate 
               }); 
            } 
         }; 
			
      }, function (error) { 
         console.log(error); 
      }); 
		
   } 
};
 
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      });
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
 
//hang up
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
});
 
function handleLeave() { 
   connectedUser = null; 
   remoteAudio.src = null; 
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null; 
};