1. บทนำ
WebRTC เป็นโปรเจ็กต์โอเพนซอร์สที่ช่วยให้การสื่อสารแบบเรียลไทม์ของเสียง วิดีโอ และข้อมูลในเว็บและแอปเนทีฟเป็นไปได้
WebRTC มี JavaScript API หลายรายการ คลิกที่ลิงก์เพื่อดูการสาธิต
getUserMedia(): บันทึกเสียงและวิดีโอMediaRecorder: บันทึกเสียงและวิดีโอRTCPeerConnection: สตรีมเสียงและวิดีโอระหว่างผู้ใช้RTCDataChannel: สตรีมข้อมูลระหว่างผู้ใช้
ฉันจะใช้ WebRTC ได้ที่ใด
ใน Firefox, Opera และใน Chrome บนเดสก์ท็อปและ Android WebRTC ยังพร้อมใช้งานสำหรับแอปเนทีฟบน iOS และ Android ด้วย
การส่งสัญญาณคืออะไร
WebRTC ใช้ RTCPeerConnection เพื่อสื่อสารข้อมูลการสตรีมระหว่างเบราว์เซอร์ แต่ก็ต้องมีกลไกในการประสานงานการสื่อสารและส่งข้อความควบคุม ซึ่งเป็นกระบวนการที่เรียกว่าการส่งสัญญาณ WebRTC ไม่ได้ระบุวิธีการและโปรโตคอลการส่งสัญญาณ ใน Codelab นี้ คุณจะได้ใช้ Socket.IO สำหรับการรับส่งข้อความ แต่ก็มีทางเลือกอื่นๆ อีกมากมาย
STUN และ TURN คืออะไร
WebRTC ออกแบบมาให้ทำงานแบบเพียร์ทูเพียร์ เพื่อให้ผู้ใช้เชื่อมต่อได้ด้วยเส้นทางที่ตรงที่สุดเท่าที่จะเป็นไปได้ อย่างไรก็ตาม WebRTC สร้างขึ้นเพื่อรับมือกับการเชื่อมต่อเครือข่ายในโลกแห่งความเป็นจริง โดยแอปพลิเคชันไคลเอ็นต์ต้องผ่านเกตเวย์ NAT และไฟร์วอลล์ และการเชื่อมต่อเครือข่ายแบบเพียร์ทูเพียร์ต้องมีตัวเลือกสำรองในกรณีที่การเชื่อมต่อโดยตรงล้มเหลว ในกระบวนการนี้ API ของ WebRTC จะใช้เซิร์ฟเวอร์ STUN เพื่อรับที่อยู่ IP ของคอมพิวเตอร์ และใช้เซิร์ฟเวอร์ TURN เพื่อทำหน้าที่เป็นเซิร์ฟเวอร์รีเลย์ในกรณีที่การสื่อสารแบบเพียร์ทูเพียร์ล้มเหลว (WebRTC ในโลกแห่งความเป็นจริงอธิบายรายละเอียดเพิ่มเติม)
WebRTC ปลอดภัยไหม
การเข้ารหัสเป็นข้อบังคับสำหรับคอมโพเนนต์ WebRTC ทั้งหมด และจะใช้ JavaScript API ได้จากต้นทางที่ปลอดภัย (HTTPS หรือ localhost) เท่านั้น มาตรฐาน WebRTC ไม่ได้กำหนดกลไกการส่งสัญญาณ ดังนั้นคุณจึงต้องตรวจสอบว่าได้ใช้โปรโตคอลที่ปลอดภัย
2. ภาพรวม
สร้างแอปเพื่อรับวิดีโอและถ่ายสแนปชอตด้วยเว็บแคม แล้วแชร์แบบเพียร์ทูเพียร์ผ่าน WebRTC ในระหว่างนี้ คุณจะได้เรียนรู้วิธีใช้ WebRTC API หลักและตั้งค่าเซิร์ฟเวอร์รับส่งข้อความโดยใช้ Node.js
สิ่งที่คุณจะได้เรียนรู้
- รับวิดีโอจากเว็บแคม
- สตรีมวิดีโอด้วย RTCPeerConnection
- สตรีมข้อมูลด้วย RTCDataChannel
- ตั้งค่าบริการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความ
- รวมการเชื่อมต่อเพียร์และการส่งสัญญาณ
- ถ่ายรูปและแชร์ผ่านช่องทางข้อมูล
สิ่งที่คุณต้องมี
- Chrome 47 ขึ้นไป
- Web Server for Chrome หรือใช้เว็บเซิร์ฟเวอร์ที่คุณเลือก
- โค้ดตัวอย่าง
- โปรแกรมแก้ไขข้อความ
- มีความรู้พื้นฐานเกี่ยวกับ HTML, CSS และ JavaScript
3. รับโค้ดตัวอย่าง
ดาวน์โหลดรหัส
หากคุ้นเคยกับ Git คุณสามารถดาวน์โหลดโค้ดสำหรับ Codelab นี้จาก GitHub ได้โดยการโคลน
git clone https://github.com/googlecodelabs/webrtc-web
หรือคลิกปุ่มต่อไปนี้เพื่อดาวน์โหลดไฟล์ .zip ของโค้ด
เปิดไฟล์ ZIP ที่ดาวน์โหลด การดำเนินการนี้จะแตกโฟลเดอร์โปรเจ็กต์ (adaptive-web-media) ซึ่งมีโฟลเดอร์สำหรับแต่ละขั้นตอนของ Codelab นี้ พร้อมกับทรัพยากรทั้งหมดที่คุณจะต้องใช้
คุณจะทำงานด้านการเขียนโค้ดทั้งหมดในไดเรกทอรีที่ชื่อ work
โฟลเดอร์ step-nn มีเวอร์ชันที่เสร็จสมบูรณ์แล้วสำหรับแต่ละขั้นตอนของโค้ดแล็บนี้ โดยมีไว้เพื่อใช้อ้างอิง
ติดตั้งและยืนยันเว็บเซิร์ฟเวอร์
แม้ว่าคุณจะใช้เว็บเซิร์ฟเวอร์ของคุณเองได้ แต่ Codelab นี้ออกแบบมาให้ทำงานได้ดีกับ Chrome Web Server หากยังไม่ได้ติดตั้งแอปดังกล่าว คุณสามารถติดตั้งได้จาก Chrome เว็บสโตร์

หลังจากติดตั้งแอป Web Server for Chrome แล้ว ให้คลิกทางลัดแอป Chrome จากแถบบุ๊กมาร์ก หน้าแท็บใหม่ หรือจาก App Launcher

คลิกไอคอนเว็บเซิร์ฟเวอร์

จากนั้นคุณจะเห็นกล่องโต้ตอบนี้ ซึ่งช่วยให้คุณกำหนดค่าเว็บเซิร์ฟเวอร์ในเครื่องได้

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

จากนั้นหยุดและรีสตาร์ทเซิร์ฟเวอร์โดยเลื่อนปุ่มสลับที่มีป้ายกำกับว่าเว็บเซิร์ฟเวอร์: เริ่มต้นแล้วไปทางซ้าย แล้วเลื่อนกลับไปทางขวา

ตอนนี้ให้ไปที่เว็บไซต์งานในเว็บเบราว์เซอร์โดยคลิก URL ของเว็บเซิร์ฟเวอร์ที่ไฮไลต์ คุณควรเห็นหน้าเว็บที่มีลักษณะดังนี้ ซึ่งสอดคล้องกับ work/index.html

แน่นอนว่าแอปนี้ยังไม่ได้ทำอะไรที่น่าสนใจเลย ตอนนี้เป็นเพียงโครงสร้างพื้นฐานที่เราใช้เพื่อให้แน่ใจว่าเว็บเซิร์ฟเวอร์ของคุณทำงานได้อย่างถูกต้อง คุณจะเพิ่มฟังก์ชันการทำงานและฟีเจอร์เลย์เอาต์ในขั้นตอนถัดไป
4. สตรีมวิดีโอจากเว็บแคม
สิ่งที่คุณจะได้เรียนรู้
ในขั้นตอนนี้ คุณจะได้ทราบวิธีทำสิ่งต่อไปนี้
- รับสตรีมวิดีโอจากเว็บแคม
- จัดการการเล่นสตรีม
- ใช้ CSS และ SVG เพื่อปรับแต่งวิดีโอ
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-01
HTML เล็กน้อย...
เพิ่มองค์ประกอบ video และองค์ประกอบ script ลงใน index.html ในไดเรกทอรี 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>
...และ JavaScript เล็กน้อย
เพิ่มโค้ดต่อไปนี้ลงใน main.js ในโฟลเดอร์ 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);
ลองเลย
เปิด index.html ในเบราว์เซอร์ แล้วคุณจะเห็นลักษณะคล้ายกับภาพนี้ (แน่นอนว่ามีภาพจากเว็บแคมของคุณด้วย)

วิธีการทำงาน
หลังจากเรียกใช้ getUserMedia() แล้ว เบราว์เซอร์จะขอสิทธิ์จากผู้ใช้เพื่อเข้าถึงกล้อง (หากนี่เป็นครั้งแรกที่มีการขอสิทธิ์เข้าถึงกล้องสำหรับต้นทางปัจจุบัน) หากสำเร็จ ระบบจะแสดง MediaStream ซึ่งองค์ประกอบสื่อจะใช้ได้ผ่านแอตทริบิวต์ srcObject ดังนี้
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
}
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
อาร์กิวเมนต์ constraints ช่วยให้คุณระบุได้ว่าจะรับสื่อใด ในตัวอย่างนี้ จะมีเพียงวิดีโอเท่านั้น เนื่องจากระบบปิดใช้เสียงโดยค่าเริ่มต้น
const mediaStreamConstraints = {
video: true,
};
คุณใช้ข้อจำกัดสำหรับข้อกำหนดเพิ่มเติม เช่น ความละเอียดของวิดีโอ ได้ดังนี้
const hdConstraints = {
video: {
width: {
min: 1280
},
height: {
min: 720
}
}
}
ข้อกำหนด MediaTrackConstraints แสดงรายการประเภทข้อจำกัดที่เป็นไปได้ทั้งหมด แม้ว่าเบราว์เซอร์บางตัวอาจไม่รองรับตัวเลือกบางอย่าง หากกล้องที่เลือกในปัจจุบันไม่รองรับความละเอียดที่ขอ getUserMedia() จะถูกปฏิเสธพร้อมกับOverconstrainedError และระบบจะไม่แจ้งให้ผู้ใช้ให้สิทธิ์เข้าถึงกล้อง
หาก getUserMedia() สำเร็จ ระบบจะตั้งค่าสตรีมวิดีโอจากเว็บแคมเป็นแหล่งที่มาขององค์ประกอบวิดีโอ
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
คะแนนโบนัส
- ออบเจ็กต์
localStreamที่ส่งไปยังgetUserMedia()อยู่ในขอบเขตส่วนกลาง คุณจึงตรวจสอบได้จากคอนโซลของเบราว์เซอร์ โดยเปิดคอนโซล พิมพ์ stream แล้วกด Return (หากต้องการดูคอนโซลใน Chrome ให้กด Ctrl-Shift-J หรือ Command-Option-J หากใช้ Mac) localStream.getVideoTracks()จะแสดงผลอะไร- ลองโทรหา
localStream.getVideoTracks()[0].stop() - ดูออบเจ็กต์ข้อจำกัด: จะเกิดอะไรขึ้นเมื่อคุณเปลี่ยนเป็น
{audio: true, video: true} - องค์ประกอบวิดีโอมีขนาดเท่าใด คุณจะรับขนาดดั้งเดิมของวิดีโอจาก JavaScript ได้อย่างไร แทนที่จะเป็นขนาดที่แสดง ใช้เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome เพื่อตรวจสอบ
- ลองเพิ่มฟิลเตอร์ CSS ลงในองค์ประกอบวิดีโอ เช่น
video {
filter: blur(4px) invert(1) opacity(0.5);
}
- ลองเพิ่มฟิลเตอร์ SVG เช่น
video {
filter: hue-rotate(180deg) saturate(200%);
}
สิ่งที่คุณได้เรียนรู้
ในขั้นตอนนี้ คุณได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- รับวิดีโอจากเว็บแคม
- กำหนดข้อจำกัดของสื่อ
- แก้ไของค์ประกอบวิดีโอ
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-01
เคล็ดลับ
- อย่าลืมใส่แอตทริบิวต์
autoplayในองค์ประกอบvideoหากไม่มี คุณจะเห็นเพียงเฟรมเดียว getUserMedia()มีตัวเลือกอื่นๆ อีกมากมายสำหรับการจำกัด ดูเดโมได้ที่ webrtc.github.io/samples/src/content/peerconnection/constraints คุณจะเห็นว่ามีตัวอย่าง WebRTC ที่น่าสนใจมากมายในเว็บไซต์ดังกล่าว
แนวทางปฏิบัติแนะนำ
- ตรวจสอบว่าองค์ประกอบวิดีโอไม่ล้นคอนเทนเนอร์ เราได้เพิ่ม
widthและmax-widthเพื่อกำหนดขนาดที่ต้องการและขนาดสูงสุดสำหรับวิดีโอ เบราว์เซอร์จะคำนวณความสูงโดยอัตโนมัติ
video {
max-width: 100%;
width: 320px;
}
ถัดไป
คุณมีวิดีโอ แต่จะสตรีมอย่างไร ดูได้ในขั้นตอนถัดไป
5. สตรีมวิดีโอด้วย RTCPeerConnection
สิ่งที่คุณจะได้เรียนรู้
ในขั้นตอนนี้ คุณจะได้ทราบวิธีทำสิ่งต่อไปนี้
- แยกความแตกต่างของเบราว์เซอร์ด้วย WebRTC Shim adapter.js
- ใช้ RTCPeerConnection API เพื่อสตรีมวิดีโอ
- ควบคุมการจับภาพและการสตรีมสื่อ
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-2
RTCPeerConnection คืออะไร
RTCPeerConnection เป็น API สำหรับการโทรผ่าน WebRTC เพื่อสตรีมวิดีโอและเสียง รวมถึงแลกเปลี่ยนข้อมูล
ตัวอย่างนี้จะตั้งค่าการเชื่อมต่อระหว่างออบเจ็กต์ RTCPeerConnection 2 รายการ (เรียกว่าเพียร์) ในหน้าเดียวกัน
ไม่มีประโยชน์ในทางปฏิบัติมากนัก แต่เหมาะสําหรับทําความเข้าใจวิธีการทํางานของ RTCPeerConnection
เพิ่มองค์ประกอบวิดีโอและปุ่มควบคุม
ใน index.html ให้แทนที่องค์ประกอบวิดีโอเดียวด้วยองค์ประกอบวิดีโอ 2 รายการและปุ่ม 3 ปุ่ม
<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>
องค์ประกอบวิดีโอหนึ่งจะแสดงสตรีมจาก getUserMedia() และอีกองค์ประกอบหนึ่งจะแสดงวิดีโอเดียวกันที่สตรีมผ่าน RTCPeerconnection (ในการใช้งานจริง องค์ประกอบวิดีโอหนึ่งจะแสดงสตรีมในพื้นที่ ส่วนอีกองค์ประกอบจะแสดงสตรีมจากระยะไกล)
เพิ่มโปรแกรมจำลอง adapter.js
เพิ่มลิงก์ไปยัง adapter.js เวอร์ชันปัจจุบันเหนือลิงก์ไปยัง main.js ดังนี้
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
ตอนนี้ Index.html ควรมีลักษณะดังนี้
<!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>
ติดตั้งโค้ด RTCPeerConnection
แทนที่ main.js ด้วยเวอร์ชันในโฟลเดอร์ step-02
โทรออก
เปิด index.html คลิกปุ่มเริ่มเพื่อรับวิดีโอจากเว็บแคม แล้วคลิกโทรเพื่อสร้างการเชื่อมต่อแบบเพียร์ทูเพียร์ คุณควรเห็นวิดีโอเดียวกัน (จากเว็บแคม) ในองค์ประกอบวิดีโอทั้ง 2 รายการ ดูบันทึก WebRTC ในคอนโซลของเบราว์เซอร์
วิธีการทำงาน
ขั้นตอนนี้ทำหลายอย่าง...
WebRTC ใช้ RTCPeerConnection API เพื่อตั้งค่าการเชื่อมต่อเพื่อสตรีมวิดีโอระหว่างไคลเอ็นต์ WebRTC ซึ่งเรียกว่าเพียร์
ในตัวอย่างนี้ ออบเจ็กต์ RTCPeerConnection ทั้ง 2 รายการอยู่ในหน้าเดียวกัน ได้แก่ pc1 และ pc2 ไม่มีประโยชน์ในทางปฏิบัติมากนัก แต่เหมาะสำหรับการสาธิตวิธีการทำงานของ API
การตั้งค่าการโทรระหว่างเพียร์ WebRTC มี 3 งานดังนี้
- สร้าง RTCPeerConnection สำหรับปลายสายแต่ละด้าน และเพิ่มสตรีมในเครื่องจาก
getUserMedia()ที่ปลายสายแต่ละด้าน - รับและแชร์ข้อมูลเครือข่าย: ปลายทางการเชื่อมต่อที่เป็นไปได้เรียกว่าผู้สมัคร ICE
- รับและแชร์คำอธิบายในพื้นที่และระยะไกล: ข้อมูลเมตาเกี่ยวกับสื่อในพื้นที่ในรูปแบบ SDP
สมมติว่าอลิซและบ็อบต้องการใช้ RTCPeerConnection เพื่อตั้งค่าวิดีโอแชท
ก่อนอื่น อลิสาและสุเทพจะแลกเปลี่ยนข้อมูลเครือข่าย คำว่า "การค้นหาแคนดิเดต" หมายถึงกระบวนการค้นหาอินเทอร์เฟซเครือข่ายและพอร์ตโดยใช้เฟรมเวิร์ก ICE
- อลิซสร้างออบเจ็กต์ RTCPeerConnection ด้วยแฮนเดิล
onicecandidate (addEventListener('icecandidate'))ซึ่งสอดคล้องกับโค้ดต่อไปนี้จาก main.js
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
'iceconnectionstatechange', handleConnectionChange);
- Alice โทรหา
getUserMedia()และเพิ่มสตรีมที่ส่งไปยังgetUserMedia()ดังนี้
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.');
- ระบบจะเรียกใช้แฮนเดิล
onicecandidateจากขั้นตอนที่ 1 เมื่อแคนดิเดตเครือข่ายพร้อมใช้งาน - ขวัญใจส่งข้อมูลผู้สมัครที่แปลงเป็นอนุกรมให้บัญชา ในแอปพลิเคชันจริง กระบวนการนี้ (เรียกว่าการส่งสัญญาณ) จะเกิดขึ้นผ่านบริการรับส่งข้อความ ซึ่งคุณจะได้เรียนรู้วิธีการในขั้นตอนต่อๆ ไป แน่นอนว่าในขั้นตอนนี้ ออบเจ็กต์ RTCPeerConnection ทั้ง 2 รายการจะอยู่ในหน้าเดียวกันและสื่อสารกันได้โดยตรงโดยไม่ต้องใช้การรับส่งข้อความภายนอก
- เมื่อบ็อบได้รับข้อความผู้สมัครจากอลิซ เขาจะเรียกใช้
addIceCandidate()เพื่อเพิ่มผู้สมัครลงในคำอธิบายเพียร์ระยะไกล
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}.`);
}
}
เพียร์ WebRTC ยังต้องค้นหาและแลกเปลี่ยนข้อมูลสื่อเสียงและวิดีโอในเครื่องและระยะไกล เช่น ความละเอียดและความสามารถของตัวแปลงรหัส การส่งสัญญาณเพื่อแลกเปลี่ยนข้อมูลการกำหนดค่าสื่อจะดำเนินการโดยการแลกเปลี่ยน Blob ของข้อมูลเมตาที่เรียกว่าข้อเสนอและคำตอบ โดยใช้รูปแบบ Session Description Protocol หรือที่เรียกว่า SDP
- อลิซเรียกใช้เมธอด
createOffer()ของ RTCPeerConnection Promise ที่ส่งคืนจะให้ RTCSessionDescription ซึ่งเป็นคำอธิบายเซสชันในเครื่องของอลิซ
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
.then(createdOffer).catch(setSessionDescriptionError);
- หากสำเร็จ อลิซจะตั้งค่าคำอธิบายในเครื่องโดยใช้
setLocalDescription()จากนั้นจะส่งคำอธิบายเซสชันนี้ให้บ็อบผ่านช่องทางการส่งสัญญาณ - บัญชาตั้งค่าคำอธิบายที่ขวัญใจส่งให้เป็นคำอธิบายระยะไกลโดยใช้
setRemoteDescription() - บ็อบเรียกใช้เมธอด
createAnswer()ของ RTCPeerConnection โดยส่งคำอธิบายระยะไกลที่ได้รับจากอลิซ เพื่อให้ระบบสร้างเซสชันในเครื่องที่เข้ากันได้กับเซสชันของอลิซcreateAnswer()Promise จะส่งต่อ RTCSessionDescription: บัญชาตั้งค่าดังกล่าวเป็นคำอธิบายในเครื่องและส่งให้ขวัญใจ - เมื่ออลิสาได้รับคำอธิบายเซสชันของสุเทพ เธอจะตั้งค่าดังกล่าวเป็นคำอธิบายระยะไกลด้วย
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!
คะแนนโบนัส
- ดู chrome://webrtc-internals ซึ่งจะให้สถิติ WebRTC และข้อมูลการแก้ไขข้อบกพร่อง (ดูรายการ URL ของ Chrome ทั้งหมดได้ที่ chrome://about)
- จัดรูปแบบหน้าเว็บด้วย CSS โดยทำดังนี้
- วางวิดีโอไว้ข้างกัน
- ทำให้ปุ่มมีความกว้างเท่ากันและมีข้อความที่ใหญ่ขึ้น
- ตรวจสอบว่าเลย์เอาต์ใช้งานได้บนอุปกรณ์เคลื่อนที่
- จากคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ให้ดูที่
localStream,localPeerConnectionและremotePeerConnection - จากคอนโซล ให้ดูที่
localPeerConnectionpc1.localDescriptionรูปแบบ SDP มีลักษณะอย่างไร
สิ่งที่คุณได้เรียนรู้
ในขั้นตอนนี้ คุณได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- แยกความแตกต่างของเบราว์เซอร์ด้วย WebRTC Shim adapter.js
- ใช้ RTCPeerConnection API เพื่อสตรีมวิดีโอ
- ควบคุมการจับภาพและการสตรีมสื่อ
- แชร์ข้อมูลสื่อและเครือข่ายระหว่างเพียร์เพื่อเปิดใช้การโทรผ่าน WebRTC
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-2
เคล็ดลับ
- ขั้นตอนนี้มีหลายสิ่งให้เรียนรู้ หากต้องการค้นหาแหล่งข้อมูลอื่นๆ ที่อธิบาย RTCPeerConnection โดยละเอียด โปรดดูที่ webrtc.org หน้านี้มีคำแนะนำสำหรับเฟรมเวิร์ก JavaScript หากคุณต้องการใช้ WebRTC แต่ไม่ต้องการจัดการ API
- ดูข้อมูลเพิ่มเติมเกี่ยวกับโปรแกรมจำลอง adapter.js ได้จากที่เก็บข้อมูล GitHub ของ adapter.js
- อยากรู้ไหมว่าแอปวิดีโอแชทที่ดีที่สุดในโลกมีหน้าตาเป็นอย่างไร ดู AppRTC ซึ่งเป็นแอปมาตรฐานของโปรเจ็กต์ WebRTC สำหรับการโทรผ่าน WebRTC ได้ที่ app และโค้ด เวลาตั้งค่าการโทรน้อยกว่า 500 มิลลิวินาที
แนวทางปฏิบัติแนะนำ
- หากต้องการให้โค้ดพร้อมใช้งานในอนาคต ให้ใช้ API ใหม่ที่อิงตาม Promise และเปิดใช้ความเข้ากันได้กับเบราว์เซอร์ที่ไม่รองรับโดยใช้ adapter.js
ถัดไป
ขั้นตอนนี้แสดงวิธีใช้ WebRTC เพื่อสตรีมวิดีโอระหว่างเพียร์ แต่ Codelab นี้ยังเกี่ยวข้องกับข้อมูลด้วย
ในขั้นตอนถัดไป ให้ดูวิธีสตรีมข้อมูลที่กำหนดเองโดยใช้ RTCDataChannel
6. ใช้ RTCDataChannel เพื่อแลกเปลี่ยนข้อมูล
สิ่งที่คุณจะได้เรียนรู้
- วิธีแลกเปลี่ยนข้อมูลระหว่างอุปกรณ์ปลายทาง (เพียร์) ของ WebRTC
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-03
อัปเดต HTML
ในขั้นตอนนี้ คุณจะใช้ช่องข้อมูล WebRTC เพื่อส่งข้อความระหว่างองค์ประกอบ textarea 2 รายการในหน้าเดียวกัน ซึ่งอาจไม่ค่อยมีประโยชน์ แต่ก็แสดงให้เห็นว่า WebRTC สามารถใช้แชร์ข้อมูลและสตรีมวิดีโอได้
นำองค์ประกอบวิดีโอและปุ่มออกจาก index.html แล้วแทนที่ด้วย HTML ต่อไปนี้
<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>
โดยช่องข้อความหนึ่งจะใช้สำหรับป้อนข้อความ ส่วนอีกช่องจะแสดงข้อความที่สตรีมระหว่างเพียร์
ตอนนี้ index.html ควรมีลักษณะดังนี้
<!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>
อัปเดต JavaScript
แทนที่ main.js ด้วยเนื้อหาของ step-03/js/main.js
ลองสตรีมข้อมูลระหว่างเพียร์โดยเปิด index.html แล้วกดเริ่มเพื่อตั้งค่าการเชื่อมต่อเพียร์ จากนั้นป้อนข้อความใน textarea ทางด้านซ้าย แล้วคลิกส่งเพื่อโอนข้อความโดยใช้ช่องข้อมูล WebRTC
วิธีการทำงาน
โค้ดนี้ใช้ RTCPeerConnection และ RTCDataChannel เพื่อเปิดใช้การแลกเปลี่ยนข้อความ
โค้ดส่วนใหญ่ในขั้นตอนนี้จะเหมือนกับตัวอย่าง RTCPeerConnection
ฟังก์ชัน sendData() และ createConnection() มีโค้ดใหม่ส่วนใหญ่
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);
}
ไวยากรณ์ของ RTCDataChannel ตั้งใจให้คล้ายกับ WebSocket โดยมีเมธอด send() และเหตุการณ์ message
โปรดสังเกตการใช้ dataConstraint คุณสามารถกําหนดค่าช่องทางข้อมูลเพื่อให้แชร์ข้อมูลประเภทต่างๆ ได้ เช่น จัดลําดับความสําคัญของการนําส่งที่เชื่อถือได้มากกว่าประสิทธิภาพ ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกได้ที่ Mozilla Developer Network
คะแนนโบนัส
- SCTP ซึ่งเป็นโปรโตคอลที่ใช้โดยช่องข้อมูล WebRTC จะเปิดใช้การนำส่งข้อมูลที่เชื่อถือได้และเป็นไปตามลำดับโดยค่าเริ่มต้น เมื่อใดที่ RTCDataChannel จำเป็นต้องส่งข้อมูลอย่างน่าเชื่อถือ และเมื่อใดที่ประสิทธิภาพอาจมีความสำคัญมากกว่า แม้ว่าจะหมายถึงการสูญเสียข้อมูลบางส่วนก็ตาม
- ใช้ CSS เพื่อปรับปรุงเลย์เอาต์หน้าเว็บ และเพิ่มแอตทริบิวต์ตัวยึดตำแหน่งลงใน Textarea ของ "dataChannelReceive"
- ทดสอบหน้าเว็บในอุปกรณ์เคลื่อนที่
สิ่งที่คุณได้เรียนรู้
ในขั้นตอนนี้ คุณได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- สร้างการเชื่อมต่อระหว่างเพียร์ WebRTC 2 ราย
- แลกเปลี่ยนข้อมูลข้อความระหว่างเพียร์
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-03
ดูข้อมูลเพิ่มเติม
- ช่องข้อมูล WebRTC (มีมา 2-3 ปีแล้ว แต่ก็ยังควรอ่าน)
- เหตุใดจึงเลือก SCTP สำหรับช่องข้อมูลของ WebRTC
ถัดไป
คุณได้เรียนรู้วิธีแลกเปลี่ยนข้อมูลระหว่างเพียร์ในหน้าเดียวกันแล้ว แต่จะทำอย่างไรหากต้องการแลกเปลี่ยนข้อมูลระหว่างเครื่องที่แตกต่างกัน ก่อนอื่น คุณต้องตั้งค่าช่องการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความข้อมูลเมตา ดูวิธีได้ในขั้นตอนถัดไป
7. ตั้งค่าบริการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความ
สิ่งที่คุณจะได้เรียนรู้
ในขั้นตอนนี้ คุณจะได้ทราบวิธีทำสิ่งต่อไปนี้
- ใช้
npmเพื่อติดตั้งทรัพยากร Dependency ของโปรเจ็กต์ตามที่ระบุไว้ใน package.json - เรียกใช้เซิร์ฟเวอร์ Node.js และใช้ node-static เพื่อแสดงไฟล์แบบคงที่
- ตั้งค่าบริการรับส่งข้อความใน Node.js โดยใช้ Socket.IO
- ใช้ฟีเจอร์นี้เพื่อสร้าง "ห้องแชท" และแลกเปลี่ยนข้อความ
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-04
แนวคิด
ไคลเอ็นต์ (เพียร์) ของ WebRTC ต้องแลกเปลี่ยนข้อมูลเมตาเพื่อตั้งค่าและรักษาการโทรผ่าน WebRTC โดยทำดังนี้
- ข้อมูลผู้สมัคร (เครือข่าย)
- เสนอและตอบข้อความที่ให้ข้อมูลเกี่ยวกับสื่อ เช่น ความละเอียดและตัวแปลงรหัส
กล่าวคือ ต้องมีการแลกเปลี่ยนข้อมูลเมตาก่อนจึงจะสตรีมเสียง วิดีโอ หรือข้อมูลแบบเพียร์ทูเพียร์ได้ กระบวนการนี้เรียกว่าการส่งสัญญาณ
ในขั้นตอนก่อนหน้า ออบเจ็กต์ RTCPeerConnection ของผู้ส่งและผู้รับจะอยู่ในหน้าเดียวกัน ดังนั้น "การส่งสัญญาณ" จึงเป็นเพียงการส่งข้อมูลเมตาระหว่างออบเจ็กต์
ในการใช้งานจริง RTCPeerConnection ของผู้ส่งและผู้รับจะทํางานในหน้าเว็บบนอุปกรณ์ต่างๆ และคุณต้องมีวิธีให้ทั้ง 2 ฝ่ายสื่อสารข้อมูลเมตา
โดยคุณใช้เซิร์ฟเวอร์ส่งสัญญาณ ซึ่งเป็นเซิร์ฟเวอร์ที่ส่งข้อความระหว่างไคลเอ็นต์ (เพียร์) ของ WebRTC ได้ ข้อความจริงเป็นข้อความธรรมดา ซึ่งเป็นออบเจ็กต์ JavaScript ที่แปลงเป็นสตริง
ข้อกำหนดเบื้องต้น: ติดตั้ง Node.js
หากต้องการทำตามขั้นตอนถัดไปของ Codelab นี้ (โฟลเดอร์ step-04 ถึง step-06) คุณจะต้องเรียกใช้เซิร์ฟเวอร์ใน localhost โดยใช้ Node.js
คุณดาวน์โหลดและติดตั้ง Node.js ได้จากลิงก์นี้หรือผ่านเครื่องมือจัดการแพ็กเกจที่คุณต้องการ
เมื่อติดตั้งแล้ว คุณจะสามารถนำเข้าการอ้างอิงที่จำเป็นสำหรับขั้นตอนถัดไป (การเรียกใช้ npm install) รวมถึงเรียกใช้เซิร์ฟเวอร์ localhost ขนาดเล็กเพื่อเรียกใช้ Codelab (การเรียกใช้ node index.js) ได้ โดยระบบจะระบุคำสั่งเหล่านี้ในภายหลังเมื่อจำเป็น
เกี่ยวกับแอป
WebRTC ใช้ JavaScript API ฝั่งไคลเอ็นต์ แต่สำหรับการใช้งานจริงยังต้องมีเซิร์ฟเวอร์การส่งสัญญาณ (การรับส่งข้อความ) รวมถึงเซิร์ฟเวอร์ STUN และ TURN ด้วย ดูข้อมูลเพิ่มเติมได้ที่นี่
ในขั้นตอนนี้ คุณจะสร้างเซิร์ฟเวอร์การส่งสัญญาณ Node.js อย่างง่ายโดยใช้โมดูล Socket.IO Node.js และไลบรารี JavaScript สำหรับการรับส่งข้อความ ประสบการณ์การใช้งาน Node.js และ Socket.IO จะมีประโยชน์ แต่ไม่จำเป็น เนื่องจากคอมโพเนนต์การรับส่งข้อความนั้นเรียบง่ายมาก
ในตัวอย่างนี้ เซิร์ฟเวอร์ (แอปพลิเคชัน Node.js) จะติดตั้งใช้งานใน index.js และไคลเอ็นต์ที่ทำงานบนเซิร์ฟเวอร์ (เว็บแอป) จะติดตั้งใช้งานใน index.html
แอปพลิเคชัน Node.js ในขั้นตอนนี้มี 2 งาน
ประการแรกคือทำหน้าที่เป็นตัวกลางส่งต่อข้อความ
socket.on('message', function (message) {
log('Got message: ', message);
socket.broadcast.emit('message', message);
});
ประการที่ 2 คือจัดการ "ห้อง" แชทวิดีโอ 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);
}
แอปพลิเคชัน WebRTC ที่เรียบง่ายของเราจะอนุญาตให้มีเพียร์สูงสุด 2 คนที่แชร์ห้อง
HTML และ JavaScript
อัปเดต index.html ให้มีลักษณะดังนี้
<!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>
คุณจะไม่เห็นอะไรในหน้านี้ในขั้นตอนนี้ เนื่องจากระบบจะบันทึกทั้งหมดไปยังคอนโซลของเบราว์เซอร์ (หากต้องการดูคอนโซลใน Chrome ให้กด Ctrl-Shift-J หรือ Command-Option-J หากใช้ Mac)
แทนที่ js/main.js ด้วยโค้ดต่อไปนี้
'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);
});
ตั้งค่า Socket.IO ให้ทำงานใน Node.js
ในไฟล์ HTML คุณอาจเห็นว่าคุณกำลังใช้ไฟล์ Socket.IO
<script src="/socket.io/socket.io.js"></script>
ที่ระดับบนสุดของไดเรกทอรี work ให้สร้างไฟล์ชื่อ package.json โดยมีเนื้อหาดังนี้
{
"name": "webrtc-codelab",
"version": "0.0.1",
"description": "WebRTC codelab",
"dependencies": {
"node-static": "^0.7.10",
"socket.io": "^1.2.0"
}
}
นี่คือไฟล์ Manifest ของแอปที่บอก Node Package Manager (npm) ว่าควรติดตั้งทรัพยากร Dependency ของโปรเจ็กต์ใด
หากต้องการติดตั้งส่วนที่ใช้อ้างอิง (เช่น /socket.io/socket.io.js) ให้เรียกใช้คำสั่งต่อไปนี้จากเทอร์มินัลบรรทัดคำสั่งในไดเรกทอรี work
npm install
คุณควรเห็นบันทึกการติดตั้งที่สิ้นสุดด้วยข้อความต่อไปนี้

ดังที่เห็น npm ได้ติดตั้งการอ้างอิงที่กำหนดไว้ใน package.json
สร้างไฟล์ใหม่ชื่อ index.js ที่ระดับบนสุดของไดเรกทอรี work (ไม่ใช่ในไดเรกทอรี js) แล้วเพิ่มโค้ดต่อไปนี้
'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);
}
});
}
});
});
จากเทอร์มินัลบรรทัดคำสั่ง ให้เรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรี work
node index.js
เปิด localhost:8080 จากเบราว์เซอร์
ทุกครั้งที่คุณเปิด URL นี้ ระบบจะแจ้งให้คุณป้อนชื่อห้อง หากต้องการเข้าร่วมห้องเดียวกัน ให้เลือกชื่อห้องเดียวกันทุกครั้ง เช่น "foo"
เปิดหน้าแท็บใหม่ แล้วเปิด localhost:8080 อีกครั้ง เลือกชื่อห้องเดียวกัน
เปิด localhost:8080 ในแท็บหรือหน้าต่างที่ 3 เลือกชื่อห้องเดิมอีกครั้ง
ตรวจสอบคอนโซลในแต่ละแท็บ คุณควรเห็นการบันทึกจาก JavaScript ด้านบน
คะแนนโบนัส
- กลไกการรับส่งข้อความทางเลือกอื่นๆ ที่อาจเป็นไปได้มีอะไรบ้าง คุณอาจพบปัญหาใดบ้างเมื่อใช้ WebSocket แบบ "เพียว"
- การปรับขนาดแอปพลิเคชันนี้อาจมีปัญหาใดบ้าง คุณสามารถพัฒนาวิธีทดสอบคำขอห้องพร้อมกันหลายพันหรือหลายล้านรายการได้ไหม
- แอปนี้ใช้พรอมต์ JavaScript เพื่อรับชื่อห้อง หาวิธีรับชื่อห้องจาก URL เช่น localhost:8080/foo จะให้ชื่อห้อง
foo
สิ่งที่คุณได้เรียนรู้
ในขั้นตอนนี้ คุณได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- ใช้ npm เพื่อติดตั้งทรัพยากร Dependency ของโปรเจ็กต์ตามที่ระบุไว้ใน package.json
- เรียกใช้เซิร์ฟเวอร์ Node.js เพื่อแสดงไฟล์แบบคงที่
- ตั้งค่าบริการรับส่งข้อความใน Node.js โดยใช้ socket.io
- ใช้ฟีเจอร์นี้เพื่อสร้าง "ห้องแชท" และแลกเปลี่ยนข้อความ
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-04
ดูข้อมูลเพิ่มเติม
- ที่เก็บแชทตัวอย่าง Socket.io
- WebRTC ในโลกแห่งความเป็นจริง: STUN, TURN และการส่งสัญญาณ
- คำว่า "การส่งสัญญาณ" ใน WebRTC
ถัดไป
ดูวิธีใช้การส่งสัญญาณเพื่อให้ผู้ใช้ 2 คนสร้างการเชื่อมต่อแบบเพียร์ได้
8. รวมการเชื่อมต่อเพียร์และการส่งสัญญาณ
สิ่งที่คุณจะได้เรียนรู้
ในขั้นตอนนี้ คุณจะได้ทราบวิธีทำสิ่งต่อไปนี้
- เรียกใช้บริการส่งสัญญาณ WebRTC โดยใช้ Socket.IO ที่ทำงานบน Node.js
- ใช้บริการดังกล่าวเพื่อแลกเปลี่ยนข้อมูลเมตา WebRTC ระหว่างเพียร์
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-05
แทนที่ HTML และ JavaScript
แทนที่เนื้อหาของ index.html ด้วยเนื้อหาต่อไปนี้
<!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>
แทนที่ js/main.js ด้วยเนื้อหาของ step-05/js/main.js
เรียกใช้เซิร์ฟเวอร์ Node.js
หากไม่ได้ทำตาม Codelab นี้จากไดเรกทอรีงาน คุณอาจต้องติดตั้งการอ้างอิงสำหรับโฟลเดอร์ step-05 หรือโฟลเดอร์การทำงานปัจจุบัน เรียกใช้คำสั่งต่อไปนี้จากไดเรกทอรีที่ทำงานอยู่
npm install
เมื่อติดตั้งแล้ว หากเซิร์ฟเวอร์ Node.js ไม่ทำงาน ให้เริ่มทำงานโดยเรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรี work
node index.js
ตรวจสอบว่าคุณใช้ไฟล์ index.js เวอร์ชันจากขั้นตอนก่อนหน้าซึ่งใช้ Socket.IO ดูข้อมูลเพิ่มเติมเกี่ยวกับ Node และ Socket IO ได้ที่ส่วน "ตั้งค่าบริการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความ"
เปิด localhost:8080 จากเบราว์เซอร์
เปิด localhost:8080 อีกครั้งในแท็บหรือหน้าต่างใหม่ องค์ประกอบวิดีโอหนึ่งจะแสดงสตรีมในพื้นที่จาก getUserMedia() และอีกองค์ประกอบหนึ่งจะแสดงวิดีโอ "ระยะไกล" ที่สตรีมผ่าน RTCPeerconnection
ดูการบันทึกในคอนโซลของเบราว์เซอร์
คะแนนโบนัส
- แอปพลิเคชันนี้รองรับเฉพาะวิดีโอแชทแบบตัวต่อตัว คุณจะเปลี่ยนการออกแบบอย่างไรเพื่อให้มีผู้แชร์ห้องแชทวิดีโอเดียวกันได้มากกว่า 1 คน
- ตัวอย่างนี้มีชื่อห้อง foo ที่ฮาร์ดโค้ด เราควรทำอย่างไรเพื่อให้ใช้ชื่อห้องอื่นๆ ได้
- ผู้ใช้จะแชร์ชื่อห้องอย่างไร ลองสร้างวิธีอื่นในการแชร์ชื่อห้อง
- คุณจะเปลี่ยนแอปได้อย่างไร
สิ่งที่คุณได้เรียนรู้
ในขั้นตอนนี้ คุณได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- เรียกใช้บริการส่งสัญญาณ WebRTC โดยใช้ Socket.IO ที่ทำงานบน Node.js
- ใช้บริการดังกล่าวเพื่อแลกเปลี่ยนข้อมูลเมตา WebRTC ระหว่างเพียร์
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-05
เคล็ดลับ
- สถิติและข้อมูลการแก้ไขข้อบกพร่องของ WebRTC ดูได้จาก chrome://webrtc-internals
- คุณใช้ test.webrtc.org เพื่อตรวจสอบสภาพแวดล้อมในเครื่องและทดสอบกล้องและไมโครโฟนได้
- หากพบปัญหาแปลกๆ เกี่ยวกับการแคช ให้ลองทำดังนี้
- บังคับรีเฟรชโดยกด Ctrl ค้างไว้แล้วคลิกปุ่มโหลดซ้ำ
- รีสตาร์ทเบราว์เซอร์
- เรียกใช้
npm cache cleanจากบรรทัดคำสั่ง
ถัดไป
ดูวิธีถ่ายรูป รับข้อมูลรูปภาพ และแชร์ข้อมูลดังกล่าวระหว่างเพียร์ที่อยู่ระยะไกล
9. ถ่ายรูปและแชร์ผ่านช่องทางข้อมูล
สิ่งที่คุณจะได้เรียนรู้
ในขั้นตอนนี้ คุณจะได้เรียนรู้วิธีทำสิ่งต่อไปนี้
- ถ่ายรูปและรับข้อมูลจากรูปภาพโดยใช้องค์ประกอบ Canvas
- แลกเปลี่ยนข้อมูลรูปภาพกับผู้ใช้ระยะไกล
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-06
วิธีการทำงาน
ก่อนหน้านี้คุณได้เรียนรู้วิธีแลกเปลี่ยนข้อความโดยใช้ RTCDataChannel
ขั้นตอนนี้จะช่วยให้แชร์ไฟล์ทั้งไฟล์ได้ ในตัวอย่างนี้คือรูปภาพที่ถ่ายผ่าน getUserMedia()
ส่วนหลักของขั้นตอนนี้มีดังนี้
- สร้างช่องทางข้อมูล โปรดทราบว่าคุณไม่ต้องเพิ่มสตรีมสื่อลงในการเชื่อมต่อเพียร์ในขั้นตอนนี้
- บันทึกสตรีมวิดีโอจากเว็บแคมของผู้ใช้ด้วย
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);
});
}
- เมื่อผู้ใช้คลิกปุ่ม Snap ให้รับสแนปชอต (เฟรมวิดีโอ) จากสตรีมวิดีโอและแสดงในองค์ประกอบ
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);
}
- เมื่อผู้ใช้คลิกปุ่มส่ง ให้แปลงรูปภาพเป็นไบต์แล้วส่งผ่านช่องข้อมูล
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));
}
}
- ฝั่งผู้รับจะแปลงไบต์ของข้อความในแชแนลข้อมูลกลับเป็นรูปภาพและแสดงรูปภาพต่อผู้ใช้
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);
}
รับโค้ด
แทนที่เนื้อหาของโฟลเดอร์ work ด้วยเนื้อหาของ step-06 ตอนนี้ไฟล์ index.html ใน 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>
<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>
หากไม่ได้ทำตาม Codelab นี้จากไดเรกทอรีงาน คุณอาจต้องติดตั้งการอ้างอิงสำหรับโฟลเดอร์ step-06 หรือโฟลเดอร์การทำงานปัจจุบัน เพียงเรียกใช้คำสั่งต่อไปนี้จากไดเรกทอรีที่ทำงานอยู่
npm install
เมื่อติดตั้งแล้ว หากเซิร์ฟเวอร์ Node.js ไม่ทำงาน ให้เริ่มทำงานโดยเรียกใช้คำสั่งต่อไปนี้จากไดเรกทอรีงาน
node index.js
ตรวจสอบว่าคุณใช้ index.js เวอร์ชันที่ใช้ Socket.IO และอย่าลืมรีสตาร์ทเซิร์ฟเวอร์ Node.js หากคุณทำการเปลี่ยนแปลง ดูข้อมูลเพิ่มเติมเกี่ยวกับ Node และ Socket IO ได้ที่ส่วน "ตั้งค่าบริการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความ"
หากจำเป็น ให้คลิกปุ่มอนุญาตเพื่อให้แอปใช้เว็บแคมได้
แอปจะสร้างรหัสห้องแบบสุ่มและเพิ่มรหัสนั้นลงใน URL เปิด URL จากแถบที่อยู่ในแท็บหรือหน้าต่างเบราว์เซอร์ใหม่
คลิกปุ่ม Snap & Send แล้วดูส่วน "ขาเข้า" ในแท็บอื่นที่ด้านล่างของหน้า แอปจะโอนรูปภาพระหว่างแท็บ
คุณควรเห็นข้อความคล้ายกับข้อความต่อไปนี้

คะแนนโบนัส
- คุณจะเปลี่ยนโค้ดเพื่อให้แชร์ไฟล์ประเภทใดก็ได้ได้อย่างไร
ดูข้อมูลเพิ่มเติม
- MediaStream Image Capture API: API สำหรับถ่ายภาพและควบคุมกล้อง ซึ่งจะพร้อมใช้งานในเบราว์เซอร์เร็วๆ นี้
- MediaRecorder API สำหรับบันทึกเสียงและวิดีโอ: เดโม เอกสารประกอบ
สิ่งที่คุณได้เรียนรู้
- วิธีถ่ายรูปและรับข้อมูลจากรูปภาพโดยใช้องค์ประกอบ Canvas
- วิธีแลกเปลี่ยนข้อมูลดังกล่าวกับผู้ใช้ระยะไกล
เวอร์ชันที่สมบูรณ์ของขั้นตอนนี้อยู่ในโฟลเดอร์ step-06
10. ขอแสดงความยินดี
คุณสร้างแอปเพื่อสตรีมวิดีโอและแลกเปลี่ยนข้อมูลแบบเรียลไทม์
สิ่งที่คุณได้เรียนรู้
ในโค้ดแล็บนี้ คุณได้เรียนรู้วิธีการต่อไปนี้
- รับวิดีโอจากเว็บแคม
- สตรีมวิดีโอด้วย RTCPeerConnection
- สตรีมข้อมูลด้วย RTCDataChannel
- ตั้งค่าบริการส่งสัญญาณเพื่อแลกเปลี่ยนข้อความ
- รวมการเชื่อมต่อเพียร์และการส่งสัญญาณ
- ถ่ายรูปและแชร์ผ่านช่องข้อมูล
ขั้นตอนถัดไป
- ดูโค้ดและสถาปัตยกรรมของแอปพลิเคชันแชท WebRTC ที่เป็นมาตรฐาน AppRTC ได้ที่ app และcode
- ลองใช้การสาธิตแบบสดจาก github.com/webrtc/samples
ดูข้อมูลเพิ่มเติม
- แหล่งข้อมูลมากมายสำหรับการเริ่มต้นใช้งาน WebRTC มีให้บริการที่ webrtc.org