WebRTC basic phần 2 : Cách thiết lập một cuộc gọi thông qua WebRTC
Bài đăng này đã không được cập nhật trong 8 năm
Tiếp tục serial về WebRTC
.
Hôm nay mình sẽ giới thiệu về cơ chế hoạt động của WebRTC
. Cần những gì để tạo được một application WebRTC.
Như mấy bạn từng nghe, các cậu có cu, chết, nhầm, các cụ có câu "Dục tốc bất đạt" nên các bạn đừng nóng vội nhé (Có thể áp dụng trong các phương thức khác của cuộc sống như tán gái, tán dép hay tán dóc etc).
Cứ đi từ từ từng thứ một thì đến khi ngồi xún code thì tay nó tự hoạt động luôn ấy chứ (giống như lúc bạn làm gì gì gì gì gì ấy )))).
Chém gió tào lao bí đao vậy đủ rồi. Vào phần chính thôi nào.
Vậy WebRTC có những phần gì?
Nó cung cấp 3 APIs chính (From wikipedia)
getUserMedia
: cho phép trình duyệt web truy cập vào camera và/hoặc microphone để lấy dữ liệu hình ảnh âm thanh cho việc truyền tải.
RTCPeerConnection
: dùng để cài đặt videocall/voicecall dùng cho việc truyền tải.
RTCDataChannel
: cho phép trình duyệt chia sẻ dữ liệu peer-to-peer.
Dựa vào đó có thể liệt kê các step cho một ứng dụng WebRTC
như sau:
- Sử dụng
getUserMedia
API để truy cập vào camera và microphone - Lấy thông tin network như địa chỉ IP, ports và trao đổi thông tin đó với các peer khác (những peer mà mình muốn connect tới) để tạo connection (kết nối) dù cho có bị ngăn cản bởi
NATs
hayfirewalls
. (Các bạn thấy phần 1 giúp ích như thế nào cho phần này rồi chứ? Click here để đọc lại phần 1 nha) - Sau đó thì dùng
RTCPeerConnection
vàRTCDataChannel
để voice call/ video call hoặc chia sẻ dữ liệu sau khi đã có kết nối peer-to-peer
Nghe thật quá đơn giản đúng hem? OK đi sâu vào từng phần nha
MediaStream
Một MediaStream
đại diện cho sự đồng bộ dữ liệu âm thanh và hình ảnh. MediaStream
được khởi tạo bằng cách gọi hàm getUserMedia
(Web API thôi nhé, native code như Android và iOS thì có khác tý, tên hàm vs gọi hàm, nhưng về cơ bản là cùng cha khác ông nội thôi) . Sau khi một kết nối WebRTC
tới một máy tính khác được thiết lập, chúng ta có khả năng truy cập vào stream của máy tính đó. Mỗi peer sẽ có một local media stream riêng.
Một MediaStream
sẽ có input/output. Input là sẽ lấy những dữ liệu âm thanh và hình ảnh của local và output dùng để hiển thị lên view hoặc được RTCPeerConnection
sử dụng (đề cập sau)
Mỗi một MediaStream
đều có một cái nhãn tên(label), ví dụ như "Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ"
Sử dụng MediaStream (getUserMedia)
như thế nào?
Hiện giờ thì mình mới chỉ có khả năng làm trên Android vs JavaScript thôi nên mình chỉ show ít code cùi bắp vậy thôi nha các ngôn ngữ khác thì các bạn tự tim hiểu nha
Javascript
:
API getUserMedia
sẽ nhận vào 3 tham số:
- Một constraints object
- Một hàm callback khi success
- Một hàm callback khi failure
Ta có thể dùng hai hàm getAudioTracks()
và getVideoTracks()
để lấy một mảng dữ liệu media stream. Nếu không lấy được audio hoặc video thì nó sẽ trả về một mảng rỗng (Empty)
constraints dùng để config audio hay video sẽ được dùng, camera trước hoặc camera sau, frame rate, chiều dài và chiều rộng (min/max height or width) (min/max FrameRate)
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var constraints = { audio: false, video: true }; // cái constraints này dùng để config audio hay video sẽ được sử dụng ah
var video = document.querySelector("video");
function successCallback(stream) {
video.src = window.URL.createObjectURL(stream);
}
function errorCallback(error){
console.log("getUserMedia lỗi cmnr :(((( : ", error);
}
navigator.getUserMedia(constraints, successCallback, errorCallback);
Vậy đó Javascript
thì đơn giản vậy thôi. Nhưng bên Android
thì có biến tấu chút đỉnh, bợ võ công về xào nấu lại (đại khái giống như kịch tà kiếm phổ vs quỳnh châu bảo điển ấy )) )
OK quay lại với môn phái Android
và xem cách nó luyện công với thằng MediaStream
như thế nào nhé? (Tất nhiên bước đầu tiên là phải tự cung
rồi ))))) )
Có khá nhiều library WebRTC
cho Android
nhưng trong bài này thì mình sử dụng lib môn phái Pristine
(compile 'io.pristine:libjingle:11139@aar'), iOS
thì xài
pod "libjingle_peerconnection"
các bạn chú ý đoạn code nhé, mình sẽ giải thích cụ thể
localStream = factory.createLocalMediaStream("ARDAMS");// khởi tạo local media local stream
if (pcParams.videoCallEnabled) { //nếu sử dụng video
MediaConstraints videoConstraints = new MediaConstraints();//config thông số cho video constraints
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", Integer.toString(pcParams.videoHeight)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", Integer.toString(pcParams.videoWidth)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxFrameRate", Integer.toString(pcParams.videoFps)));
videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minFrameRate", Integer.toString(pcParams.videoFps)));
videoSource = factory.createVideoSource(getVideoCapturer(), videoConstraints);//tao video source dựa trên thông số constraints và camera trước hoặc camera sau
localStream.addTrack(factory.createVideoTrack("ARDAMSv0", videoSource));
}
AudioSource audioSource = factory.createAudioSource(new MediaConstraints());//tương tự cho audio
localStream.addTrack(factory.createAudioTrack("ARDAMSa0", audioSource));
Các bạn thấy rồi đấy, đâu có gì khó khăn đúng ko nào? Nhớ là phải tự cung trước khi luyện nhé
)))
Vậy là xong phần Media Stream
rồi nhé, giờ qua tiếp phần khác. Mình sẽ sớm gặp lại Media Stream
thôi
Vấn đề tiếp theo là : Media Stream
đã có, vậy thì làm gì tiếp theo nhỉ?
à há phải cần có
Signaling: session control, network and media information
Vậy Signaling
là gì?
Như đã nói sơ qua thì WebRTC
sử dụng RTCPeerConnection
để giao tiếp và truyền dữ liệu giữa hai peers với nhau. Nhưng trước đó thì hai bên vẫn không biết nhau thì làm quéo gì mà "quan hệ" được
Và khi đang "quan hệ" thì một bên muốn drop thì bên còn lại sao bik mà dừng
Vì thế Signaling
ra đời, trước khi đi vào khái niệm thì cho mình đính chính là signaling không phải là một
thành phần của WebRTC
nhé. Signaling
chỉ là tên gọi của một phương thức, một protocol giúp cho ta tạo được liên hệ giữa các peer với nhau. Signaling
là do bạn chọn (có thể là XMPP, SIP, Socket
), nghe tới đây chắc nhiều bạn ngộ ra Signaling
thực chất là cái gì rồi nhỉ?
Vậy Signaling
dùng để làm gì?
Session control messages
: khởi tạo và chấm dứt một kết nốiNetwork configuration
: dùng để hỏi xem địa chỉ public IP của tau là gì, port bao nhiêu (hỏi ai thì các bạn biết rồi đấy, quay lại phần 1)Media capabilities
: các loại codecs và resolutions cần thiết
Những cái trên là cần thiết và nhất định phải làm trước và phải đảm bảo thành công trước khi gởi nhận stream giữa peer-to-peer
Lý thuyết hơi khô khan đúng hem? Vậy thì đọc tiếp nha, lý thuyết nữa đây )))
Bài toán đặt ra là làm sao Ronaldo(R)
có thể gọi cho Messi(M)
để rủ đi nhà nghỉ?
Khoan nói sâu vào WebRTC
cơ chế đã nha, bạn có thể hiểu nôm na như thế này.
Nếu các bạn nhác đọc chữ (như mình) thì xài tạm cái hình này. Nếu nhìn hình và đọc chữ ở dưới nữa thì perfect. Quá hoàn hảo
P/s: mình vẽ hơi khó hiểu nên nhớ đọc theo số thứ tự mình đánh dấu nhé
1 - Đầu tiên là phải cần một Server (Signaling
) ( ở đây Server chỉ có nhiệm vụ transfer message thôi, những bài tiếp theo mình sẽ sử dụng Nodejs để làm ví dụ, nhưng các bạn có thể sử dụng các công nghệ khác như GCM, XMPP etc)
2 - R vs M phải biết số điện thoại của nhau đã chứ (giống như skype id vậy, cái này dễ rồi đúng ko)
3 - Cả R vs M đều phải khởi tạo MediaStream
mới có thể gọi cho nhau đúng ko? Vậy thì R sẽ khởi tạo media stream sau đó
4 - R sẽ gởi một message tới M (Gởi tới Server và Server transfer message đó tới Messi) để wake up M bảo là ê anh muốn gọi chú, chú avaiable ko?
5 - M thức dậy (nhận đc message) setup media stream local và một số thứ cần thiết để sử dụng WebRTC (bàn sau nha)
6 - Xong rồi thì M gởi một message back to R (Server transfer message) bảo là anh mày sẵn sàng rồi, chuẩn bị nở hoa nhé
7 - Sau đó R khởi tạo object RTCPeerConnection và tạo một createOffer() với M (không gởi gì tại step này nhé, cứ tạo offer thôi)
Khi khởi tạo peer connection với createOffer thành công thì R sẽ nhận được một call back với thông tin của RTCSessionDescription.
Peer peer = peers.get(peerId);
peer.pc.createOffer(peer, pcConstraints);
Trong hàm callback, R sẽ gọi setLocalDescription và sau đó sẽ gởi cái session này cho M thông qua signaling server, và cái session này sẽ được đặt tên là OFFER (Nhớ nhé, tạo create offer nhưng không send gì tại bước này cho tới khi nhận đc call back thông tin về SessionDescription rồi mới gởi cho thằng cờ hó kia)
JSONObject payload = new JSONObject();
payload.put("type", sdp.type.canonicalForm());
payload.put("sdp", sdp.description);
sendMessage(id, sdp.type.canonicalForm(), payload);
peerConnection.setLocalDescription(Peer.this, sdp);
Chú ý rằng RTCPeerConnection sẽ không bắt đầu "Gathering Candidates" cho tới khi nào hàm setLocalDescription được gọi.
8 - Messi nhận được và cũng tạo một RTCPeerConnection và gọi hàm setRemoteDescription với thông tin của R, sau đó lại gọi createAnswer
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
peer.pc.createAnswer(peer, pcConstraints);
và hàm createAnswer cũng trả về call back với thông tin là RTCSessionDescription của Messi, sau đó M sẽ gọi hàm setLocalDescription trước khi gởi message answer cho R (tương tự bước trên của thằng R)
9 - Sau khi nhận đc SessionDescription của M thì R cũng làm tương tự như trên, setRemoteDescription
SessionDescription sdp = new SessionDescription(
SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
payload.getString("sdp")
);
peer.pc.setRemoteDescription(peer, sdp);
10 - Và từ đây thì R vs M tự do quan hệ rồi nhé (thiết lập đc kết nối Peer-to-Peer)
À còn về phần gathering candiate, sau khi gọi hàm setLocalDescription thì R và M đều sẽ bắt đầu send thông tin candidate cho đối phương (thông tin candidate ví dụ như IP, port bla bla như đã kể trên)
Phần sau mình sẽ hướng dẫn config bằng code nhé
All rights reserved