WebRTC - การสาธิตด้วยเสียง

ในบทนี้เราจะสร้างแอปพลิเคชันไคลเอนต์ที่อนุญาตให้ผู้ใช้สองคนบนอุปกรณ์แยกกันสื่อสารกันโดยใช้สตรีมเสียง WebRTC ใบสมัครของเราจะมีสองหน้า หนึ่งสำหรับล็อกอินและอีกอันสำหรับโทรด้วยเสียงไปยังผู้ใช้รายอื่น

สองหน้าจะเป็นแท็กdiv การป้อนข้อมูลส่วนใหญ่ทำผ่านตัวจัดการเหตุการณ์อย่างง่าย

เซิร์ฟเวอร์สัญญาณ

ในการสร้างไคลเอนต์การเชื่อมต่อ WebRTC ต้องสามารถถ่ายโอนข้อความโดยไม่ต้องใช้การเชื่อมต่อแบบเพียร์ WebRTC นี่คือที่ที่เราจะใช้ HTML5 WebSockets - การเชื่อมต่อซ็อกเก็ตแบบสองทิศทางระหว่างจุดสิ้นสุดสองจุด - เว็บเซิร์ฟเวอร์และเว็บเบราว์เซอร์ ตอนนี้เริ่มใช้ไลบรารี WebSocket สร้างไฟล์server.jsและใส่รหัสต่อไปนี้ -

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

บรรทัดแรกต้องการไลบรารี WebSocket ซึ่งเราได้ติดตั้งไว้แล้ว จากนั้นเราสร้างเซิร์ฟเวอร์ซ็อกเก็ตบนพอร์ต 9090 ต่อไปเราจะฟังเหตุการณ์การเชื่อมต่อ รหัสนี้จะดำเนินการเมื่อผู้ใช้ทำการเชื่อมต่อ WebSocket กับเซิร์ฟเวอร์ จากนั้นเราจะรับฟังข้อความที่ผู้ใช้ส่งมา ในที่สุดเราก็ตอบกลับไปยังผู้ใช้ที่เชื่อมต่อว่า“ สวัสดีจากเซิร์ฟเวอร์”

ในเซิร์ฟเวอร์การส่งสัญญาณของเราเราจะใช้ชื่อผู้ใช้แบบสตริงสำหรับการเชื่อมต่อแต่ละครั้งเพื่อให้เราทราบว่าจะส่งข้อความไปที่ใด มาเปลี่ยนตัวจัดการการเชื่อมต่อของเราสักหน่อย -

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

วิธีนี้เรายอมรับเฉพาะข้อความ JSON ต่อไปเราต้องจัดเก็บผู้ใช้ที่เชื่อมต่อทั้งหมดไว้ที่ใดที่หนึ่ง เราจะใช้วัตถุ Javascript ธรรมดาสำหรับมัน เปลี่ยนด้านบนของไฟล์ของเรา -

//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 = {};

เราจะเพิ่มฟิลด์ประเภทสำหรับทุกข้อความที่มาจากไคลเอนต์ ตัวอย่างเช่นหากผู้ใช้ต้องการเข้าสู่ระบบเขาจะส่งข้อความประเภทการเข้าสู่ระบบ มากำหนดกัน -

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

หากผู้ใช้ส่งข้อความด้วยประเภทการเข้าสู่ระบบเรา -

  • ตรวจสอบว่ามีใครเข้าสู่ระบบด้วยชื่อผู้ใช้นี้แล้ว
  • หากเป็นเช่นนั้นให้แจ้งผู้ใช้ว่าเขาเข้าสู่ระบบไม่สำเร็จ
  • หากไม่มีใครใช้ชื่อผู้ใช้นี้เราจะเพิ่มชื่อผู้ใช้เป็นกุญแจสำคัญในออบเจ็กต์การเชื่อมต่อ
  • หากไม่รู้จักคำสั่งเราจะส่งข้อผิดพลาด

รหัสต่อไปนี้เป็นฟังก์ชันตัวช่วยสำหรับส่งข้อความไปยังการเชื่อมต่อ เพิ่มลงในไฟล์server.js -

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

เมื่อผู้ใช้ตัดการเชื่อมต่อเราควรล้างการเชื่อมต่อ เราสามารถลบผู้ใช้ได้เมื่อปิดเหตุการณ์ เพิ่มรหัสต่อไปนี้ในการเชื่อมต่อ handler-

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

หลังจากเข้าสู่ระบบสำเร็จผู้ใช้ต้องการโทรหาคนอื่น เขาควรยื่นข้อเสนอให้กับผู้ใช้รายอื่นเพื่อให้บรรลุ เพิ่มตัวจัดการข้อเสนอ -

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;

ประการแรกเราได้รับการเชื่อมต่อของผู้ใช้ที่เราพยายามโทรหา หากมีอยู่เราจะส่งรายละเอียดข้อเสนอให้เขา เรายังเพิ่มotherNameให้กับวัตถุการเชื่อมต่อ สิ่งนี้สร้างขึ้นเพื่อความง่ายในการค้นหาในภายหลัง

การตอบกลับมีรูปแบบที่คล้ายกันกับที่เราใช้ในตัวจัดการข้อเสนอ เซิร์ฟเวอร์ของเราส่งผ่านข้อความทั้งหมดเป็นคำตอบให้กับผู้ใช้รายอื่น เพิ่มรหัสต่อไปนี้หลังตัวจัดการข้อเสนอ -

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;

ส่วนสุดท้ายคือการจัดการผู้สมัคร ICE ระหว่างผู้ใช้ เราใช้เทคนิคเดียวกันในการส่งข้อความระหว่างผู้ใช้ ความแตกต่างที่สำคัญคือข้อความของผู้สมัครอาจเกิดขึ้นหลายครั้งต่อผู้ใช้ในลำดับใดก็ได้ เพิ่มตัวจัดการผู้สมัคร -

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;

นอกจากนี้ยังจะส่งเหตุการณ์การลาให้กับผู้ใช้รายอื่นเพื่อให้เขาสามารถยกเลิกการเชื่อมต่อกับเพียร์ได้ นอกจากนี้เราควรจัดการกรณีที่ผู้ใช้หลุดการเชื่อมต่อจากเซิร์ฟเวอร์การส่งสัญญาณ มาแก้ไขตัวจัดการระยะใกล้ของเรา-

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

ต่อไปนี้เป็นรหัสทั้งหมดของเซิร์ฟเวอร์การส่งสัญญาณของเรา -

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

แอปพลิเคชันไคลเอนต์

วิธีหนึ่งในการทดสอบแอปพลิเคชันนี้คือการเปิดแท็บเบราว์เซอร์สองแท็บและพยายามโทรด้วยเสียงซึ่งกันและกัน

ก่อนอื่นเราต้องติดตั้งไลบรารีbootstrap Bootstrap เป็นเฟรมเวิร์กส่วนหน้าสำหรับการพัฒนาเว็บแอปพลิเคชัน สามารถเรียนรู้เพิ่มเติมได้ที่http://getbootstrap.com/.สร้างโฟลเดอร์ที่เรียกว่า "การแชทเสียง" นี่จะเป็นโฟลเดอร์รูทแอปพลิเคชันของเรา ภายในโฟลเดอร์นี้สร้างไฟล์package.json (จำเป็นสำหรับการจัดการการอ้างอิง npm) และเพิ่มสิ่งต่อไปนี้ -

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

จากนั้นเรียกNPM ติดตั้งบูต สิ่งนี้จะติดตั้งไลบรารี bootstrap ในโฟลเดอร์audiochat / node_modules

ตอนนี้เราต้องสร้างหน้า HTML พื้นฐาน สร้างไฟล์index.htmlในโฟลเดอร์รูทด้วยรหัสต่อไปนี้ -

<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>

หน้านี้น่าจะคุ้นเคยกับคุณ เราได้เพิ่มไฟล์bootstrap css เราได้กำหนดสองหน้าด้วย ในที่สุดเราได้สร้างช่องข้อความและปุ่มต่างๆสำหรับรับข้อมูลจากผู้ใช้ คุณควรเห็นองค์ประกอบเสียงสองรายการสำหรับสตรีมเสียงภายในและระยะไกล สังเกตว่าเราได้เพิ่มลิงค์ไปยังไฟล์client.js

ตอนนี้เราจำเป็นต้องสร้างการเชื่อมต่อกับเซิร์ฟเวอร์การส่งสัญญาณของเรา สร้างไฟล์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)); 
};

ตอนนี้ใช้เซิร์ฟเวอร์การส่งสัญญาณของเราผ่านทางเซิร์ฟเวอร์โหนด จากนั้นภายในโฟลเดอร์รูทให้เรียกใช้คำสั่งแบบคงที่และเปิดเพจภายในเบราว์เซอร์ คุณควรเห็นเอาต์พุตคอนโซลต่อไปนี้ -

ขั้นตอนต่อไปคือการนำผู้ใช้เข้าสู่ระบบด้วยชื่อผู้ใช้เฉพาะ เราเพียงแค่ส่งชื่อผู้ใช้ไปยังเซิร์ฟเวอร์ซึ่งจะแจ้งให้เราทราบว่าถูกยึดหรือไม่ เพิ่มรหัสต่อไปนี้ในไฟล์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 
      //**********************
		         
   } 
	
};

ประการแรกเราเลือกการอ้างอิงถึงองค์ประกอบในหน้า เราซ่อนหน้าการโทร จากนั้นเพิ่มตัวฟังเหตุการณ์ที่ปุ่มเข้าสู่ระบบ เมื่อผู้ใช้คลิกเราจะส่งชื่อผู้ใช้ของเขาไปยังเซิร์ฟเวอร์ สุดท้ายเราใช้การเรียกกลับ handleLogin หากการเข้าสู่ระบบสำเร็จเราจะแสดงหน้าการโทรและเริ่มตั้งค่าการเชื่อมต่อแบบเพียร์

ในการเริ่มการเชื่อมต่อแบบเพียร์เราต้องการ -

  • รับสตรีมเสียงจากไมโครโฟน
  • สร้างวัตถุ RTCPeerConnection

เพิ่มรหัสต่อไปนี้ใน“ บล็อกตัวเลือก UI” -

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

var yourConn; 
var stream;

ปรับเปลี่ยนฟังก์ชัน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); 
      }); 
		
   } 
};

ตอนนี้ถ้าคุณรันโค้ดหน้าควรอนุญาตให้คุณเข้าสู่ระบบและแสดงสตรีมเสียงในเครื่องของคุณบนหน้า

ตอนนี้เราพร้อมที่จะเริ่มต้นการโทร ประการแรกเราส่งข้อเสนอไปยังผู้ใช้รายอื่น เมื่อผู้ใช้ได้รับข้อเสนอเขาจะสร้างคำตอบและเริ่มซื้อขายผู้สมัคร ICE เพิ่มรหัสต่อไปนี้ในไฟล์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)); 
};

เราเพิ่มตัวจัดการการคลิกลงในปุ่มโทรซึ่งจะเริ่มต้นข้อเสนอ จากนั้นเราใช้ตัวจัดการหลายตัวที่ตัวจัดการonmessageคาดไว้ โดยจะประมวลผลแบบอะซิงโครนัสจนกว่าผู้ใช้ทั้งสองจะทำการเชื่อมต่อ

ขั้นตอนสุดท้ายคือการใช้งานคุณลักษณะการวางสาย การดำเนินการนี้จะหยุดการส่งข้อมูลและบอกให้ผู้ใช้รายอื่นปิดการโทร เพิ่มรหัสต่อไปนี้ -

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

เมื่อผู้ใช้คลิกที่ปุ่มวางสาย -

  • มันจะส่งข้อความ "ลา" ไปยังผู้ใช้รายอื่น
  • มันจะปิด RTCPeerConnection และทำลายการเชื่อมต่อภายในเครื่อง

ตอนนี้เรียกใช้รหัส คุณควรจะสามารถเข้าสู่เซิร์ฟเวอร์โดยใช้สองแท็บเบราว์เซอร์ จากนั้นคุณสามารถโทรด้วยเสียงไปที่แท็บและวางสายได้

ต่อไปนี้คือไฟล์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; 
};