+5

WebRTC basic phần 2 : Cách thiết lập một cuộc gọi thông qua WebRTC

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

alt text

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 😃)))).

alt text

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:

  1. Sử dụng getUserMedia API để truy cập vào camera và microphone
  2. 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 hay firewalls. (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)
  3. Sau đó thì dùng RTCPeerConnectionRTCDataChannel để 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 alt text

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()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 😃)) )

alt text

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 😄

alt text

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

  1. Session control messages : khởi tạo và chấm dứt một kết nối
  2. Network 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) 😄
  3. 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 😃)))

alt text

Bài toán đặt ra là làm sao Ronaldo(R) có thể gọi cho Messi(M) để rủ đi nhà nghỉ?

alt text

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é

alt text

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí