Webrtc là gì? Tìm hiểu về Webrtc
Webrtc là gì?
WebRTC (Web Real-Time Communication) là một tập hợp các hàm lập trình (web API) được phát triển bởi World Wide Web Consortium (W3C). Khả năng hỗ trợ trình duyệt giao tiếp với nhau theo thời gian thực bằng video, âm thanh hay truyền tải dữ liệu “Peer-to-Peer” (P2P) mà không cần browser phải cài thêm plugins hay phần mềm hỗ trợ nào từ bên ngoài. WebRTC có thể giúp chúng ta gọi điện video ngay trong trình duyệt mà không cần đăng kí tài khoản, ngoài ra chúng còn được xài để phát triển game chơi trực tiếp trong trình duyệt và rất nhiều loại ứng dụng khác.
Cách thức hoạt động
WebRTC cung cấp khả năng đa phương tiện một cách mạnh mẽ cho Web, bao gồm hỗ trợ âm thanh, video, trao đổi tệp… Kết nối giữa các peer có thể được thực hiện mà không yêu cầu một trình điều khiển đặc biệt hoặc plugin nào và nó có thể thực hiện mà không cần bất kỳ máy chủ trung gian nào.
Note: thuật ngữ "Peer-to-Peer"
nói rõ ra là mạng ngang hàng / mạng đồng đẳng
. Một mạng đồng đẳng đúng nghĩa không có khái niệm máy chủ và máy khách, nói cách khác, tất cả các máy tham gia đều bình đẳng và được gọi là peer, là một nút mạng đóng vai trò đồng thời là máy khách và máy chủ đối với các máy khác trong mạng.
Kết nối giữa 2 peer được tạo nên và đại diện bởi interface RTCPeerConnection. Một khi kết nối được thiết lập và mở thì các luồn MediaStreams hoặc RTCDataChannels có thể được thêm vào kết nối.
Lịch sử của WebRTC
Ý tưởng phát triển WebRTC được nhóm kỹ sư chịu trách nhiệm cho Google Hangouts đưa ra từ tận năm 2009. Vào thời gian đó, để truyền tải video, hình ảnh trên web thì người ta thường phải xài đến Flash. Nhóm kỹ sư Hangouts lại không muốn sử dụng công nghệ sắp lỗi thời này, và họ bắt đầu tự làm một chuẩn riêng cho mình. Đến năm 2010, Google thâu tóm hai công ty On2 và Global IP Solutions (GIPS) để sở hữu bản quyền công nghệ truyền dữ liệu thời gian thực làm nền tảng cho WebRTC về sau.
Vào tháng 5/2011, Google ra mắt một dự án nguồn mở dành cho việc giao tiếp thời gian thực giữa trình duyệt với nhau, và từ lúc này dự án mang tên WebRTC. Song song đó, Hiệp hội World Wide Web (W3C) và Hiệp hội Kĩ sư quốc tế (IETF) cũng đang phát triển một số giao thức để dùng cho việc việc kết nối thời gian thực, thế nên họ bắt tay nhau tiếp tục hoàn thiện để rồi quyết định kết hợp chung vào WebRTC.
Đến 27/10/2011, W3C ra mắt bản nháp đầu tiên của WebRTC. Tháng 11/2011, Chrome 23 ra mắt, trở thành trình duyệt đầu tiên có tích hợp WebRTC. Cho đến thời điểm này, WebRTC vẫn còn đang tiếp tục được phát triển chứ chưa hoàn thiện một cách chính thức.
Demo – Viết ứng dụng gọi video giữa 2 người bằng Webrtc và Firebase
Tham khảo nguồn tại đây.
1.Cài đặt Firebase
Firebase là gì: Đó là một dịch vụ cơ sở dữ liệu thời gian thực hoạt động trên nền tảng đám mây được cung cấp bởi Google nhằm giúp các lập trình phát triển nhanh các ứng dụng bằng cách đơn giản hóa các thao tác với cơ sở dữ liệu.
Vào trang Firebase để đăng ký một tài khoản và chọn “Create New Project”. Sau khi tạo được project xong thì chọn “Add Firebase to your web app”, tại đây bạn sẽ có một đoạn mã Javascript có chứa key ứng dung.
<script> // Initialize Firebase // TODO: Replace with your project's customized code snippet var config = { apiKey: "<API_KEY>", authDomain: "<PROJECT_ID>.firebaseapp.com", databaseURL: "https://<DATABASE_NAME>.firebaseio.com", projectId: "<PROJECT_ID>", storageBucket: "<BUCKET>.appspot.com", messagingSenderId: "<SENDER_ID>", }; firebase.initializeApp(config); </script>
Trong phần Rules của database thay đổi giá trị .read
và .write
chuyển sang thành true
2.Đến phần Code
Code 1 giao diện html có thêm thẻ <video></video>
để show camera:
<html> <head> <script src="https://www.gstatic.com/firebasejs/5.0.4/firebase.js"></script> <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body onload="showMyFace()"> <video id="yourVideo" autoplay muted></video> <video id="friendsVideo" autoplay></video> <br /> <button onclick="showFriendsFace()" type="button" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-facetime-video" aria-hidden="true"></span> Call</button> </body> </html>
Và css:
video { background-color: #ddd; border-radius: 7px; margin: 10px 0px 0px 10px; width: 320px; height: 240px; } button { margin: 5px 0px 0px 10px !important; width: 654px; }
Javascript
-
- Cấp quyền truy cập vào thư mục gốc của cơ sở dữ liệu Firebase
var database = firebase.database().ref();
- Hàm
sendMessage
để thêm cơ sở dữ liệu vào Firebase:database.on('child_added', readMessage);
- Tạo
yourId
random:var yourId = Math.floor(Math.random()*1000000000);
- Khai báo máy chủ sử dụng: Hai máy chủ STUN sử dụng ở đây là của Google và Firefox, bạn cũng có thể thêm nhiều STUN khác tùy thích:
var servers = {'iceServers': [ {'urls': 'stun:stun.services.mozilla.com'}, {'urls': 'stun:stun.l.google.com:19302'} ]};
Note: STUN là gì? STUN (Session Traversal Utilities for NAT) là một giao thức mạng cho phép các máy khách tìm ra địa chỉ công khai của mình, loại NAT mà chúng đang đứng sau và cổng phía Internet được NAT gắn liền với cổng nội bộ nào đó. Thông tin này được sử dụng để thiết lập giao tiếp UDP giữa 2 host mà đều nằm sau NAT router. Giao thức STUN được định nghĩa trong RFC 5389.
- Tạo một object PeerConnection
var pc = new RTCPeerConnection(servers);
- Chờ đợi đối tượng ICE Candidate được tạo trên máy tính của bạn:
pc.onicecandidate = (event => event.candidate ? sendMessage(yourId, JSON.stringify({'ice': event.candidate})) : console.log('Sent All Ice') );
Chức năng này sẽ được gọi nhiều lần, một lần cho mỗi
ICE
Candidate (khó dịch sang tiếng Việt nên để nguyên) được tạo ra. Khi mộtICE
Candidate được tạo ra thì hàm này sẽ biến object thành một chuỗi. Sau đó, nó gửi chuỗi này cho bạn bè của bạn thông qua Firebase. Máy tính bạn bè của bạn cũng sẽ thực hiện tương tự như trên.ICE (Interactive Communication Establishment) nôm na dễ hiểu là một giao thức được cùng để thiết lập phiên media dựa trên UDP đi qua NAT một cách nhanh nhất. ICE sẽ tìm đường tốt nhất để kết nối giữa các peer, nó thử tất cả khả năng có thể kết nối một cách song song và lựa chọn con đường hiệu quả nhất.
- Khi bạn và người nhận nhận được một ICE Candidate dưới dạng chuỗi được gửi từ Firebase, thì
JSON.stringify
sẽ chuyển đổi chuỗi trở lại thành đối tượngICE
Candidate. - Chờ đợi cho các đối tượng Offer, Answer,
ICE
Candidates được gửi:pc.onaddstream = (event => friendsVideo.srcObject = event.stream);
- Event
onaddstream
được gọi và đặtfriendsVideo.srcObject
thành object MediaStream. Thao tác này sẽ hiển thị video người kia trên máy tính của bạn và ngược lại.friendsVideo
được gọi đến từ HTML. - Thêm dữ liệu vào Firebase bằng hàm
sendMessage
:function sendMessage(senderId, data) { var msg = database.push({ sender: senderId, message: data }); msg.remove(); }
- Thêm hàm show camera của mình
showMyFace
:function showMyFace() { navigator.mediaDevices.getUserMedia({audio:true, video:true}) .then(stream => yourVideo.srcObject = stream) .then(stream => pc.addStream(stream)); }
Khi gọi hàm
getUserMedia
, trình duyệt sẽ yêu cầu quyền truy cập camera. Nó sẽ trả về một đối tượng MediaStream cái mà bạn có thể đặt bằngyourVideo.srcObject
. Đoạn này có chức năng hiển thị mặt của bạn trên chính máy tính của bạn. Sau đó, ta cần thêm cùng một đối tượng MediaStream vào đối tượng PeerConnection của bạn. Trên máy tính đối phương thực hiện cuộc gọi cũng thực hiện tương tự. Hàm này được gọi ngay khi tải trang, vì vậy bạn sẽ thấy khuôn mặt của mình ngay khi load trang. - Hàm
showFriendsFace
:function showFriendsFace() { pc.createOffer() .then(offer => pc.setLocalDescription(offer) ) .then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})) ); }
Tạo đối tượng Offer bằng cách gọi
pc.createOffer()
. Đặt local description cho offer này bằng cách gọipc.setLocalDescription(offer)
. Cuối cùng gửi đối tượng Offer cho bạn của bạn bằng cách gọisendMessage
. - Hàm
readMessage
function readMessage(data) { var msg = JSON.parse(data.val().message); var sender = data.val().sender; if (sender != yourId) { if (msg.ice != undefined) { pc.addIceCandidate(new RTCIceCandidate(msg.ice)); } else if (msg.sdp.type == "offer") { pc.setRemoteDescription(new RTCSessionDescription(msg.sdp)) .then(() => pc.createAnswer()) .then(answer => pc.setLocalDescription(answer)) .then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription}))); } else if (msg.sdp.type == "answer") { pc.setRemoteDescription(new RTCSessionDescription(msg.sdp)); } } };
Người nhận cuộc gọi có thể đọc được tin nhắn thông qua hàm
readMessage
. Với kiểu tin nhắn làoffer
thì bạn đã gửi cho người nhận một đối tượng Offer mà bạn đã tạo. Người kia sẽ thiết lập mô tả từ xa (remote decription) của họ cho đối tượng Offer mà bạn đã gửi cho họ bằng cách gọipc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
. Người nhận sẽ tạo một đối tượng Answer bằng cách gọi đếnpc.createAnswer()
. Hàm này trả về một đối tượng Answer mà bạn sẽ thiết lập ở mô tả local. Người nhận làm được điều này bằng cách gọi đếnpc.setLocalDescription(answer)
. Sau đó, người nhận lấy đối tượng Answer và gửi nó cho bạn bằng cách gọisendMessage
.Bởi vì kiểu của tin nhắn bây giờ đã là answer nên đoạn sau sẽ thực hiện
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
- Toàn bộ code trên:
var config = { apiKey: "<API_KEY>", authDomain: "<PROJECT_ID>.firebaseapp.com", databaseURL: "https://<DATABASE_NAME>.firebaseio.com", projectId: "<PROJECT_ID>", storageBucket: "<BUCKET>.appspot.com", messagingSenderId: "<SENDER_ID>", }; firebase.initializeApp(config); var database = firebase.database().ref(); var yourVideo = document.getElementById("yourVideo"); var friendsVideo = document.getElementById("friendsVideo"); var yourId = Math.floor(Math.random()*1000000000); var servers = {'iceServers': [ {'urls': 'stun:stun.services.mozilla.com'}, {'urls': 'stun:stun.l.google.com:19302'} ]}; var pc = new RTCPeerConnection(servers); pc.onicecandidate = (event => event.candidate?sendMessage(yourId, JSON.stringify({'ice': event.candidate})):console.log("Sent All Ice") ); pc.onaddstream = (event => friendsVideo.srcObject = event.stream); function sendMessage(senderId, data) { var msg = database.push({ sender: senderId, message: data }); msg.remove(); } function readMessage(data) { var msg = JSON.parse(data.val().message); var sender = data.val().sender; if (sender != yourId) { if (msg.ice != undefined) { pc.addIceCandidate(new RTCIceCandidate(msg.ice)); } else if (msg.sdp.type == "offer") { pc.setRemoteDescription(new RTCSessionDescription(msg.sdp)) .then(() => pc.createAnswer()) .then(answer => pc.setLocalDescription(answer)) .then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription}))); } else if (msg.sdp.type == "answer") { pc.setRemoteDescription(new RTCSessionDescription(msg.sdp)); } } }; database.on('child_added', readMessage); function showMyFace() { navigator.mediaDevices.getUserMedia({audio:true, video:true}) .then(stream => yourVideo.srcObject = stream) .then(stream => pc.addStream(stream)); } function showFriendsFace() { pc.createOffer() .then(offer => pc.setLocalDescription(offer) ) .then(() => sendMessage(yourId, JSON.stringify({'sdp': pc.localDescription})) ); }
- Cấp quyền truy cập vào thư mục gốc của cơ sở dữ liệu Firebase
Tổng kết
Nói tóm lại, WebRTC có thể được sử dụng cho nhiều mục đích, từ việc truyền tải video, âm thanh cho đến gửi dữ liệu theo thời gian thực giữa hai hoặc nhiều thiết bị với nhau mà không nhất thiết phải đi qua server trung gian. Điều này giúp giảm độ trễ trong việc truyền tải, giảm độ phức tạp khi phát triển ứng dụng cũng như giảm chi phí vận hành (vì không phải trả tiền thuê server, tiền điện, tiền bảo dưỡng…), kéo theo đó giá bán dịch vụ nếu có thì cũng sẽ thấp hơn.
- S State trong frontend là gì? Tại sao nên giỏi cái này?
- D Distributed Lock Manager – Vấn đề liệu có được giải quyết
- V ViteJS – Một Build Tool “Hackspeed” dành cho dev thích tốc độ
- i iOS 17.5 Beta 1 vừa được phát hành, những thay đổi nào đáng chú ý?
- C ChatGPT và những người bạn (Phần 3)
- T Tấn công các cụm Kubernetes qua lỗi API Kubelet misconfigure
- C ChatGPT và những người bạn (Phần 2)
- C Con đường trở thành Software Architect chẳng hề dễ dàng!
- C ChatGPT và những người bạn (Phần 1)
- K Kinh nghiệm cấu trúc thư mục trong dự án React