Làm quen với WebRTC

WebRTC là một mặt trận mới trong cuộc chiến lâu dài vì một web mở và không bị cản trở.

Brendan Eich, người phát minh ra JavaScript

Giao tiếp theo thời gian thực mà không cần trình bổ trợ

Hãy tưởng tượng một thế giới nơi điện thoại, TV và máy tính của bạn có thể giao tiếp trên một nền tảng chung. Hãy tưởng tượng bạn có thể dễ dàng thêm tính năng trò chuyện video và chia sẻ dữ liệu ngang hàng vào ứng dụng web của mình. Đó là mục tiêu của WebRTC.

Bạn muốn dùng thử? WebRTC có trên máy tính và thiết bị di động trong Google Chrome, Safari, Firefox và Opera. Một nơi phù hợp để bắt đầu là ứng dụng trò chuyện video đơn giản tại appr.tc:

  1. Mở appr.tc trong trình duyệt.
  2. Nhấp vào Tham gia để tham gia một phòng trò chuyện và cho phép ứng dụng sử dụng webcam của bạn.
  3. Mở URL xuất hiện ở cuối trang trong một thẻ mới hoặc tốt hơn là trên một máy tính khác.

Bắt đầu nhanh

Bạn không có thời gian đọc bài viết này hoặc chỉ muốn xem mã?

Hoặc bạn có thể chuyển thẳng đến lớp học lập trình WebRTC, một hướng dẫn từng bước giải thích cách tạo một ứng dụng trò chuyện video hoàn chỉnh, bao gồm cả một máy chủ báo hiệu đơn giản.

Lịch sử rất ngắn của WebRTC

Một trong những thách thức lớn cuối cùng đối với web là cho phép con người giao tiếp thông qua giọng nói và video: giao tiếp theo thời gian thực (RTC). RTC phải tự nhiên như khi nhập văn bản vào một đầu vào văn bản trong ứng dụng web. Nếu không có dữ liệu, bạn sẽ bị hạn chế khả năng đổi mới và phát triển những cách thức mới để mọi người tương tác.

Trước đây, RTC là một công nghệ phức tạp và mang tính doanh nghiệp, đòi hỏi các công nghệ âm thanh và video đắt tiền phải được cấp phép hoặc phát triển nội bộ. Việc tích hợp công nghệ RTC với nội dung, dữ liệu và dịch vụ hiện có rất khó khăn và tốn thời gian, đặc biệt là trên web.

Tính năng trò chuyện video của Gmail trở nên phổ biến vào năm 2008 và đến năm 2011, Google ra mắt Hangouts, sử dụng Talk (giống như Gmail). Google đã mua GIPS, một công ty phát triển nhiều thành phần cần thiết cho RTC, chẳng hạn như bộ mã hoá và giải mã cũng như các kỹ thuật khử tiếng vọng. Google đã cung cấp mã nguồn mở cho các công nghệ do GIPS phát triển và hợp tác với các tổ chức tiêu chuẩn có liên quan tại Lực lượng đặc nhiệm kỹ thuật Internet (IETF) và World Wide Web Consortium (W3C) để đảm bảo sự đồng thuận của ngành. Vào tháng 5 năm 2011, Ericsson đã xây dựng phiên bản triển khai đầu tiên của WebRTC.

WebRTC triển khai các tiêu chuẩn mở để giao tiếp bằng video, âm thanh và dữ liệu theo thời gian thực mà không cần trình bổ trợ. Nhu cầu là có thật:

  • Nhiều dịch vụ web sử dụng RTC, nhưng cần tải xuống, ứng dụng gốc hoặc trình bổ trợ. Những ứng dụng này bao gồm Skype, Facebook và Hangouts.
  • Việc tải xuống, cài đặt và cập nhật trình bổ trợ rất phức tạp, dễ xảy ra lỗi và gây phiền toái.
  • Các trình bổ trợ rất khó triển khai, gỡ lỗi, khắc phục sự cố, kiểm thử và duy trì, đồng thời có thể yêu cầu cấp phép và tích hợp với công nghệ phức tạp, tốn kém. Thông thường, rất khó để thuyết phục mọi người cài đặt trình bổ trợ ngay từ đầu!

Các nguyên tắc chỉ đạo của dự án WebRTC là API của dự án này phải là nguồn mở, miễn phí, được chuẩn hoá, tích hợp vào trình duyệt web và hiệu quả hơn các công nghệ hiện có.

Chúng ta đang ở đâu?

WebRTC được dùng trong nhiều ứng dụng, chẳng hạn như Google Meet. WebRTC cũng được tích hợp với WebKitGTK+ và các ứng dụng gốc Qt.

WebRTC triển khai 3 API sau: - MediaStream (còn gọi là getUserMedia) - RTCPeerConnection - RTCDataChannel

Các API được xác định trong 2 quy cách sau:

Cả 3 API này đều được Chrome, Safari, Firefox, Edge và Opera hỗ trợ trên thiết bị di động và máy tính.

getUserMedia: Để xem bản minh hoạ và mã, hãy xem các mẫu WebRTC hoặc thử các ví dụ tuyệt vời của Chris Wilson sử dụng getUserMedia làm dữ liệu đầu vào cho âm thanh trên web.

RTCPeerConnection: Để xem bản minh hoạ đơn giản và ứng dụng trò chuyện video có đầy đủ chức năng, hãy xem WebRTC samples Peer connection (Kết nối ngang hàng của mẫu WebRTC) và appr.tc tương ứng. Ứng dụng này sử dụng adapter.js, một shim JavaScript do Google duy trì với sự trợ giúp của cộng đồng WebRTC, để loại bỏ những điểm khác biệt giữa các trình duyệt và các thay đổi về thông số kỹ thuật.

RTCDataChannel: Để xem ví dụ này hoạt động, hãy xem các mẫu WebRTC để xem một trong các bản minh hoạ kênh dữ liệu.

Lớp học lập trình WebRTC cho biết cách sử dụng cả 3 API để tạo một ứng dụng đơn giản cho cuộc trò chuyện video và chia sẻ tệp.

WebRTC đầu tiên của bạn

Các ứng dụng WebRTC cần thực hiện một số việc:

  • Nhận âm thanh, video hoặc dữ liệu khác ở dạng phát trực tuyến.
  • Nhận thông tin mạng, chẳng hạn như địa chỉ IP và cổng, rồi trao đổi thông tin đó với các ứng dụng WebRTC khác (còn gọi là thiết bị ngang hàng) để cho phép kết nối, ngay cả thông qua NAT và tường lửa.
  • Điều phối thông tin liên lạc báo hiệu để báo cáo lỗi và bắt đầu hoặc đóng phiên.
  • Trao đổi thông tin về nội dung nghe nhìn và khả năng của ứng dụng khách, chẳng hạn như độ phân giải và bộ mã hoá/giải mã.
  • Truyền đạt âm thanh, video hoặc dữ liệu phát trực tiếp.

Để thu thập và truyền dữ liệu phát trực tuyến, WebRTC triển khai các API sau:

  • MediaStream có quyền truy cập vào các luồng dữ liệu, chẳng hạn như từ camera và micrô của người dùng.
  • RTCPeerConnection cho phép gọi điện thoại hoặc gọi video với các tiện ích mã hoá và quản lý băng thông.
  • RTCDataChannel cho phép giao tiếp ngang hàng về dữ liệu chung.

(Sau này, chúng ta sẽ thảo luận chi tiết về các khía cạnh mạng và báo hiệu của WebRTC.)

API MediaStream (còn gọi là API getUserMedia)

API MediaStream biểu thị các luồng nội dung nghe nhìn được đồng bộ hoá. Ví dụ: luồng lấy từ đầu vào của camera và micrô có các bản âm thanh và video được đồng bộ hoá. (Đừng nhầm lẫn MediaStreamTrack với phần tử <track>. Đây là một phần tử hoàn toàn khác.)

Có lẽ cách dễ nhất để hiểu API MediaStream là xem API này trong thực tế:

  1. Trong trình duyệt, hãy truy cập vào WebRTC samples getUserMedia (Các mẫu WebRTC).
  2. Mở bảng điều khiển.
  3. Kiểm tra biến stream trong phạm vi chung.

Mỗi MediaStream đều có một đầu vào (có thể là MediaStream do getUserMedia() tạo) và một đầu ra (có thể được truyền đến một phần tử video hoặc RTCPeerConnection).

Phương thức getUserMedia() nhận một tham số đối tượng MediaStreamConstraints và trả về một Promise phân giải thành một đối tượng MediaStream.

Mỗi MediaStream đều có một label, chẳng hạn như 'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'. Một mảng MediaStreamTrack được trả về bằng các phương thức getAudioTracks()getVideoTracks().

Đối với ví dụ getUserMedia, stream.getAudioTracks() sẽ trả về một mảng trống (vì không có âm thanh) và giả sử bạn đã kết nối một webcam đang hoạt động, stream.getVideoTracks() sẽ trả về một mảng gồm một MediaStreamTrack đại diện cho luồng từ webcam. Mỗi MediaStreamTrack đều có một loại ('video' hoặc 'audio'), một label (chẳng hạn như 'FaceTime HD Camera (Built-in)') và đại diện cho một hoặc nhiều kênh âm thanh hoặc video. Trong trường hợp này, chỉ có một video và không có âm thanh, nhưng bạn có thể dễ dàng hình dung các trường hợp sử dụng có nhiều video và âm thanh hơn, chẳng hạn như một ứng dụng trò chuyện nhận các luồng từ camera trước, camera sau, micrô và một ứng dụng chia sẻ màn hình.

Bạn có thể đính kèm MediaStream vào một phần tử video bằng cách đặt thuộc tính srcObject. Trước đây, việc này được thực hiện bằng cách đặt thuộc tính src thành một URL đối tượng được tạo bằng URL.createObjectURL(), nhưng thuộc tính này đã ngừng hoạt động.

getUserMedia cũng có thể được dùng làm nút đầu vào cho Web Audio API:

// Cope with browser differences.
let audioContext;
if (typeof AudioContext === 'function') {
  audioContext = new AudioContext();
} else if (typeof webkitAudioContext === 'function') {
  audioContext = new webkitAudioContext(); // eslint-disable-line new-cap
} else {
  console.log('Sorry! Web Audio not supported.');
}

// Create a filter node.
var filterNode = audioContext.createBiquadFilter();
// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section
filterNode.type = 'highpass';
// Cutoff frequency. For highpass, audio is attenuated below this frequency.
filterNode.frequency.value = 10000;

// Create a gain node to change audio volume.
var gainNode = audioContext.createGain();
// Default is 1 (no change). Less than 1 means audio is attenuated
// and vice versa.
gainNode.gain.value = 0.5;

navigator.mediaDevices.getUserMedia({audio: true}, (stream) => {
  // Create an AudioNode from the stream.
  const mediaStreamSource =
    audioContext.createMediaStreamSource(stream);
  mediaStreamSource.connect(filterNode);
  filterNode.connect(gainNode);
  // Connect the gain node to the destination. For example, play the sound.
  gainNode.connect(audioContext.destination);
});

Các ứng dụng và tiện ích dựa trên Chromium cũng có thể kết hợp getUserMedia. Việc thêm audioCapture và/hoặc videoCapture quyền vào tệp kê khai cho phép yêu cầu và cấp quyền chỉ một lần khi cài đặt. Sau đó, người dùng sẽ không được yêu cầu cấp quyền truy cập vào camera hoặc micrô nữa.

Bạn chỉ cần cấp quyền một lần cho getUserMedia(). Lần đầu tiên, nút Cho phép sẽ xuất hiện trong thanh thông tin của trình duyệt. Chrome đã ngừng sử dụng quyền truy cập HTTP cho getUserMedia() vào cuối năm 2015 do quyền này được phân loại là Tính năng mạnh mẽ.

Mục đích là có thể bật MediaStream cho mọi nguồn dữ liệu phát trực tuyến, không chỉ là camera hoặc micrô. Điều này sẽ cho phép truyền phát trực tiếp từ dữ liệu đã lưu trữ hoặc các nguồn dữ liệu tuỳ ý, chẳng hạn như cảm biến hoặc các đầu vào khác.

getUserMedia() thực sự trở nên sống động khi kết hợp với các API và thư viện JavaScript khác:

  • Webcam Toy là một ứng dụng buồng chụp ảnh sử dụng WebGL để thêm các hiệu ứng kỳ lạ và thú vị vào ảnh. Bạn có thể chia sẻ hoặc lưu ảnh cục bộ.
  • FaceKat là một trò chơi theo dõi khuôn mặt được xây dựng bằng headtrackr.js.
  • ASCII Camera sử dụng Canvas API để tạo hình ảnh ASCII.
Hình ảnh ASCII do idevelop.ro/ascii-camera tạo
gUM ASCII art!

Giới hạn

Bạn có thể dùng Constraints để đặt giá trị cho độ phân giải video cho getUserMedia(). Điều này cũng cho phép hỗ trợ các ràng buộc khác, chẳng hạn như tỷ lệ khung hình; chế độ camera (camera trước hoặc sau); tốc độ khung hình, chiều cao và chiều rộng; và phương thức applyConstraints().

Để xem ví dụ, hãy tham khảo Các mẫu WebRTC getUserMedia: chọn độ phân giải.

Việc đặt giá trị ràng buộc không được phép sẽ cho ra DOMException hoặc OverconstrainedError nếu, chẳng hạn như, độ phân giải được yêu cầu không có sẵn. Để xem tính năng này hoạt động, hãy xem các mẫu WebRTC getUserMedia: chọn độ phân giải để xem bản minh hoạ.

Chụp màn hình và thẻ

Các ứng dụng Chrome cũng cho phép chia sẻ video trực tiếp của một thẻ trình duyệt hoặc toàn bộ màn hình máy tính thông qua API chrome.tabCapturechrome.desktopCapture. (Để xem bản minh hoạ và biết thêm thông tin, hãy xem phần Chia sẻ màn hình bằng WebRTC. Bài viết này đã được đăng tải vài năm, nhưng vẫn rất thú vị.)

Bạn cũng có thể sử dụng tính năng chụp ảnh màn hình làm nguồn MediaStream trong Chrome bằng cách sử dụng ràng buộc chromeMediaSource thử nghiệm. Xin lưu ý rằng tính năng chụp màn hình yêu cầu HTTPS và chỉ nên được dùng cho mục đích phát triển do được bật thông qua một cờ dòng lệnh như giải thích trong bài đăng này.

Báo hiệu: Thông tin về phiên, mạng và nội dung nghe nhìn

WebRTC sử dụng RTCPeerConnection để truyền dữ liệu phát trực tuyến giữa các trình duyệt (còn gọi là các thành phần ngang hàng), nhưng cũng cần một cơ chế để điều phối hoạt động giao tiếp và gửi thông báo kiểm soát, một quy trình được gọi là báo hiệu. WebRTC không chỉ định các phương thức và giao thức báo hiệu. Việc báo hiệu không thuộc API RTCPeerConnection.

Thay vào đó, nhà phát triển ứng dụng WebRTC có thể chọn giao thức nhắn tin mà họ muốn, chẳng hạn như SIP hoặc XMPP, và mọi kênh giao tiếp song công (hai chiều) phù hợp. Ví dụ appr.tc sử dụng XHR và Channel API làm cơ chế báo hiệu. codelab này sử dụng Socket.io chạy trên máy chủ Node.

Quy trình báo hiệu được dùng để trao đổi 3 loại thông tin:

  • Thông báo kiểm soát phiên: để khởi động hoặc đóng thông tin liên lạc và báo cáo lỗi.
  • Cấu hình mạng: đối với thế giới bên ngoài, địa chỉ IP và cổng của máy tính là gì?
  • Khả năng về nội dung nghe nhìn: trình duyệt của bạn và trình duyệt mà trình duyệt đó muốn giao tiếp có thể xử lý những bộ mã hoá và giải mã cũng như độ phân giải nào?

Quá trình trao đổi thông tin thông qua việc báo hiệu phải hoàn tất thì hoạt động phát trực tiếp ngang hàng mới có thể bắt đầu.

Ví dụ: giả sử Alice muốn giao tiếp với Bob. Dưới đây là một đoạn mã mẫu trong thông số kỹ thuật WebRTC của W3C, cho thấy quy trình báo hiệu đang hoạt động. Mã giả định sự tồn tại của một số cơ chế báo hiệu được tạo trong phương thức createSignalingChannel(). Ngoài ra, xin lưu ý rằng trên Chrome và Opera, RTCPeerConnection hiện được đặt làm tiền tố.

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // Send the offer to the other peer.
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// Once remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

Trước tiên, Alice và Bob trao đổi thông tin mạng. (Biểu thức tìm ứng viên đề cập đến quy trình tìm giao diện mạng và cổng bằng cách sử dụng khung ICE.)

  1. Alice tạo một đối tượng RTCPeerConnection bằng trình xử lý onicecandidate. Trình xử lý này sẽ chạy khi các ứng viên mạng có sẵn.
  2. Alice gửi dữ liệu đề xuất được chuyển đổi tuần tự cho Bob thông qua bất kỳ kênh báo hiệu nào mà họ đang sử dụng, chẳng hạn như WebSocket hoặc một cơ chế nào đó khác.
  3. Khi Bob nhận được một thông báo đề xuất từ Alice, anh ta sẽ gọi addIceCandidate để thêm đề xuất đó vào nội dung mô tả của thiết bị ngang hàng từ xa.

Các ứng dụng WebRTC (còn được gọi là peer hoặc Alice và Bob trong ví dụ này) cũng cần xác định và trao đổi thông tin về nội dung nghe nhìn cục bộ và từ xa, chẳng hạn như độ phân giải và khả năng codec. Việc báo hiệu để trao đổi thông tin cấu hình nội dung nghe nhìn diễn ra bằng cách trao đổi một đề nghị và một câu trả lời bằng cách sử dụng Giao thức mô tả phiên (SDP):

  1. Alice chạy phương thức RTCPeerConnection createOffer(). Kết quả trả về từ lệnh này là RTCSessionDescription – nội dung mô tả phiên cục bộ của Alice.
  2. Trong lệnh gọi lại, Alice đặt nội dung mô tả cục bộ bằng cách sử dụng setLocalDescription() rồi gửi nội dung mô tả phiên này cho Bob thông qua kênh báo hiệu của họ. Xin lưu ý rằng RTCPeerConnection sẽ không bắt đầu thu thập các đề xuất cho đến khi setLocalDescription() được gọi. Điều này được quy định trong bản nháp JSEP của IETF.
  3. Bob đặt nội dung mô tả mà Alice gửi cho anh làm nội dung mô tả từ xa bằng cách sử dụng setRemoteDescription().
  4. Bob chạy phương thức RTCPeerConnection createAnswer(), truyền cho phương thức này nội dung mô tả từ xa mà anh ta nhận được từ Alice để có thể tạo một phiên cục bộ tương thích với phiên của cô ấy. Lệnh gọi lại createAnswer() được truyền một RTCSessionDescription. Bob đặt thông tin đó làm nội dung mô tả cục bộ và gửi cho Alice.
  5. Khi Alice nhận được nội dung mô tả phiên của Bob, cô ấy sẽ đặt nội dung đó làm nội dung mô tả từ xa bằng setRemoteDescription.
  6. Ping!

Các đối tượng RTCSessionDescription là các blob tuân theo Giao thức mô tả phiên (SDP). Khi được chuyển đổi tuần tự, một đối tượng SDP sẽ có dạng như sau:

v=0
o=- 3883943731 1 IN IP4 127.0.0.1
s=
t=0 0
a=group:BUNDLE audio video
m=audio 1 RTP/SAVPF 103 104 0 8 106 105 13 126

// ...

a=ssrc:2223794119 label:H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810

Bạn có thể đồng thời thu thập và trao đổi thông tin về mạng và nội dung nghe nhìn, nhưng cả hai quy trình này phải hoàn tất trước khi có thể bắt đầu truyền phát trực tiếp âm thanh và video giữa các thiết bị ngang hàng.

Cấu trúc đề nghị/câu trả lời mà chúng ta đã mô tả trước đó được gọi là Giao thức thiết lập phiên JavaScript (JSEP). (Có một ảnh động tuyệt vời giải thích quy trình báo hiệu và truyền phát trực tuyến trong video minh hoạ của Ericsson cho lần triển khai WebRTC đầu tiên.)

Sơ đồ cấu trúc JSEP
Cấu trúc JSEP

Sau khi quá trình báo hiệu hoàn tất thành công, dữ liệu có thể được truyền trực tiếp ngang hàng, giữa người gọi và người nhận cuộc gọi – hoặc nếu không thành công, dữ liệu sẽ được truyền qua một máy chủ chuyển tiếp trung gian (sẽ nói thêm về vấn đề này sau). Truyền trực tuyến là công việc của RTCPeerConnection.

RTCPeerConnection

RTCPeerConnection là thành phần WebRTC xử lý hoạt động giao tiếp ổn định và hiệu quả của dữ liệu truyền trực tuyến giữa các thiết bị ngang hàng.

Sau đây là sơ đồ cấu trúc WebRTC cho thấy vai trò của RTCPeerConnection. Như bạn sẽ nhận thấy, các phần màu xanh lục rất phức tạp!

Sơ đồ cấu trúc WebRTC
Cấu trúc WebRTC (từ webrtc.org)

Theo quan điểm của JavaScript, điều chính cần hiểu từ sơ đồ này là RTCPeerConnection bảo vệ các nhà phát triển web khỏi vô số sự phức tạp ẩn chứa bên dưới. Các giao thức và bộ mã hoá/giải mã mà WebRTC sử dụng thực hiện rất nhiều công việc để giúp giao tiếp theo thời gian thực trở nên khả thi, ngay cả trên các mạng không đáng tin cậy:

  • Che giấu tình trạng mất gói dữ liệu
  • Khử tiếng vọng
  • Khả năng thích ứng băng thông
  • Lưu vào bộ đệm dao động linh động
  • Điều chỉnh khuếch đại tự động
  • Giảm và khử tiếng ồn
  • Làm sạch hình ảnh

Đoạn mã W3C trước đó cho thấy một ví dụ đơn giản về WebRTC theo góc độ báo hiệu. Sau đây là hướng dẫn về 2 ứng dụng WebRTC đang hoạt động. Ví dụ đầu tiên là một ví dụ đơn giản minh hoạ RTCPeerConnection và ví dụ thứ hai là một ứng dụng trò chuyện video hoạt động đầy đủ.

RTCPeerConnection không có máy chủ

Đoạn mã sau được lấy từ WebRTC samples Peer connection (Kết nối ngang hàng của các mẫu WebRTC), có RTCPeerConnection cục bộ (và video cục bộ và từ xa) trên một trang web. Điều này không mang lại nhiều lợi ích (người gọi và người được gọi ở trên cùng một trang), nhưng nó giúp hoạt động của API RTCPeerConnection trở nên rõ ràng hơn một chút vì các đối tượng RTCPeerConnection trên trang có thể trao đổi dữ liệu và thông báo trực tiếp mà không cần sử dụng cơ chế báo hiệu trung gian.

Trong ví dụ này, pc1 đại diện cho người dùng ngang hàng cục bộ (người gọi) và pc2 đại diện cho người dùng ngang hàng từ xa (người nhận cuộc gọi).

Người gọi

  1. Tạo một RTCPeerConnection mới và thêm luồng từ getUserMedia(): ```js // Servers is an optional configuration file. (Xem phần thảo luận về TURN và STUN sau.) pc1 = new RTCPeerConnection(servers); // ... localStream.getTracks().forEach((track) => { pc1.addTrack(track, localStream); });
  1. Tạo một đề nghị và đặt đề nghị đó làm nội dung mô tả cục bộ cho pc1 và làm nội dung mô tả từ xa cho pc2. Bạn có thể thực hiện việc này ngay trong mã mà không cần dùng tín hiệu vì cả người gọi và người nhận cuộc gọi đều ở trên cùng một trang: js pc1.setLocalDescription(desc).then(() => { onSetLocalSuccess(pc1); }, onSetSessionDescriptionError ); trace('pc2 setRemoteDescription start'); pc2.setRemoteDescription(desc).then(() => { onSetRemoteSuccess(pc2); }, onSetSessionDescriptionError );

Callee

  1. Tạo pc2 và khi luồng từ pc1 được thêm vào, hãy hiển thị luồng đó trong một phần tử video: js pc2 = new RTCPeerConnection(servers); pc2.ontrack = gotRemoteStream; //... function gotRemoteStream(e){ vid2.srcObject = e.stream; }

RTCPeerConnection API và máy chủ

Trên thực tế, WebRTC cần có các máy chủ, dù đơn giản đến đâu, nên có thể xảy ra những trường hợp sau:

  • Người dùng khám phá và trao đổi thông tin chi tiết về nhau trong thế giới thực, chẳng hạn như tên.
  • Các ứng dụng WebRTC (các thiết bị ngang hàng) trao đổi thông tin mạng.
  • Các thiết bị ngang hàng trao đổi dữ liệu về nội dung nghe nhìn, chẳng hạn như định dạng và độ phân giải video.
  • Các ứng dụng WebRTC đi qua các cổng NAT và tường lửa.

Nói cách khác, WebRTC cần 4 loại chức năng phía máy chủ:

  • Khám phá và giao tiếp với người dùng
  • Báo hiệu
  • Truyền tải qua NAT/tường lửa
  • Chuyển tiếp máy chủ trong trường hợp giao tiếp ngang hàng không thành công

Bài viết này không đề cập đến việc truyền tải NAT, kết nối mạng ngang hàng và các yêu cầu để tạo một ứng dụng máy chủ cho việc phát hiện và báo hiệu người dùng. Chỉ cần nói rằng giao thức STUN và tiện ích TURN của giao thức này được khung ICE dùng để cho phép RTCPeerConnection đối phó với việc truyền tải NAT và các vấn đề khác về mạng.

ICE là một khung cho phép kết nối các thiết bị ngang hàng, chẳng hạn như hai ứng dụng trò chuyện video. Ban đầu, ICE cố gắng kết nối các thiết bị ngang hàng trực tiếp với độ trễ thấp nhất có thể thông qua UDP. Trong quy trình này, các máy chủ STUN có một nhiệm vụ duy nhất: cho phép một thiết bị ngang hàng đằng sau NAT tìm ra địa chỉ và cổng công khai của thiết bị đó. (Để biết thêm thông tin về STUN và TURN, hãy xem bài viết Tạo các dịch vụ phụ trợ cần thiết cho một ứng dụng WebRTC.)

Tìm các đề xuất kết nối
Tìm người có thể kết nối

Nếu UDP không thành công, ICE sẽ thử TCP. Nếu kết nối trực tiếp không thành công (đặc biệt là do tường lửa và cơ chế truyền tải NAT của doanh nghiệp), ICE sẽ sử dụng máy chủ TURN trung gian (chuyển tiếp). Nói cách khác, trước tiên, ICE sẽ sử dụng STUN với UDP để kết nối trực tiếp các thiết bị ngang hàng và nếu không thành công, thì sẽ quay lại máy chủ chuyển tiếp TURN. Cụm từ tìm kiếm các ứng viên đề cập đến quy trình tìm kiếm các cổng và giao diện mạng.

Đường dẫn dữ liệu WebRTC
Đường truyền dữ liệu WebRTC

Kỹ sư WebRTC Justin Uberti cung cấp thêm thông tin về ICE, STUN và TURN trong bài thuyết trình về WebRTC tại Google I/O 2013. (Các trang trình bày đưa ra ví dụ về cách triển khai máy chủ TURN và STUN.)

Một ứng dụng trò chuyện video đơn giản

Một nơi phù hợp để thử WebRTC, hoàn chỉnh với việc truyền tín hiệu và truyền qua NAT/tường lửa bằng máy chủ STUN là bản minh hoạ trò chuyện video tại appr.tc. Ứng dụng này sử dụng adapter.js, một shim để cách ly các ứng dụng khỏi những thay đổi về thông số kỹ thuật và sự khác biệt về tiền tố.

Mã này cố tình ghi nhật ký một cách chi tiết. Kiểm tra bảng điều khiển để nắm được thứ tự của các sự kiện. Sau đây là hướng dẫn chi tiết về mã.

Cấu trúc liên kết mạng

WebRTC, như được triển khai hiện tại, chỉ hỗ trợ giao tiếp một-một, nhưng có thể được sử dụng trong các trường hợp mạng phức tạp hơn, chẳng hạn như với nhiều người ngang hàng giao tiếp trực tiếp với nhau hoặc thông qua Đơn vị điều khiển đa điểm (MCU), một máy chủ có thể xử lý số lượng lớn người tham gia và thực hiện chuyển tiếp luồng chọn lọc, cũng như trộn hoặc ghi âm thanh và video.

Sơ đồ cấu trúc liên kết của Multipoint Control Unit
Ví dụ về cấu trúc liên kết của Đơn vị điều khiển đa điểm

Nhiều ứng dụng WebRTC hiện có chỉ minh hoạ hoạt động giao tiếp giữa các trình duyệt web, nhưng máy chủ cổng có thể cho phép một ứng dụng WebRTC chạy trên trình duyệt tương tác với các thiết bị, chẳng hạn như điện thoại (còn gọi là PSTN) và với các hệ thống VOIP. Vào tháng 5 năm 2012, Doubango Telecom đã phát hành mã nguồn mở cho máy khách SIP sipml5 được xây dựng bằng WebRTC và WebSocket. Máy khách này (cùng với các mục đích sử dụng tiềm năng khác) cho phép thực hiện cuộc gọi video giữa các trình duyệt và ứng dụng chạy trên iOS và Android. Tại Google I/O, Tethr và Tropo đã trình bày một khung cho hoạt động liên lạc trong trường hợp thảm hoạ trong một chiếc cặp bằng cách sử dụng một ô OpenBTS để cho phép điện thoại phổ thông và máy tính liên lạc với nhau thông qua WebRTC. Giao tiếp qua điện thoại mà không cần nhà mạng!

Bản minh hoạ Tethr/Tropo tại Google I/O 2012
Tethr/Tropo: Thông tin liên lạc trong trường hợp thảm hoạ trong một chiếc cặp

API RTCDataChannel<

Ngoài âm thanh và video, WebRTC còn hỗ trợ giao tiếp theo thời gian thực cho các loại dữ liệu khác.

API RTCDataChannel cho phép trao đổi dữ liệu tuỳ ý giữa các thiết bị ngang hàng với độ trễ thấp và thông lượng cao. Để xem bản minh hoạ một trang và tìm hiểu cách tạo một ứng dụng truyền tệp đơn giản, hãy xem các mẫu WebRTClớp học lập trình WebRTC.

API này có nhiều trường hợp sử dụng tiềm năng, bao gồm:

  • Trò chơi
  • Ứng dụng máy tính từ xa
  • Trò chuyện bằng văn bản theo thời gian thực
  • Chuyển tệp
  • Mạng phi tập trung

API này có một số tính năng để khai thác tối đa RTCPeerConnection và cho phép giao tiếp ngang hàng mạnh mẽ và linh hoạt:

  • Tận dụng chế độ thiết lập phiên RTCPeerConnection
  • Nhiều kênh đồng thời có thứ tự ưu tiên
  • Ngữ nghĩa phân phối đáng tin cậy và không đáng tin cậy
  • Cơ chế bảo mật (DTLS) và kiểm soát tắc nghẽn được tích hợp sẵn
  • Có thể sử dụng có hoặc không có âm thanh hoặc video

Cú pháp này cố tình tương tự như WebSocket với phương thức send() và sự kiện message:

const localConnection = new RTCPeerConnection(servers);
const remoteConnection = new RTCPeerConnection(servers);
const sendChannel =
  localConnection.createDataChannel('sendDataChannel');

// ...

remoteConnection.ondatachannel = (event) => {
  receiveChannel = event.channel;
  receiveChannel.onmessage = onReceiveMessage;
  receiveChannel.onopen = onReceiveChannelStateChange;
  receiveChannel.onclose = onReceiveChannelStateChange;
};

function onReceiveMessage(event) {
  document.querySelector("textarea#send").value = event.data;
}

document.querySelector("button#send").onclick = () => {
  var data = document.querySelector("textarea#send").value;
  sendChannel.send(data);
};

Hoạt động giao tiếp diễn ra trực tiếp giữa các trình duyệt, vì vậy RTCDataChannel có thể nhanh hơn nhiều so với WebSocket ngay cả khi cần có máy chủ chuyển tiếp (TURN) khi không thể thực hiện thao tác đục lỗ để đối phó với tường lửa và NAT.

RTCDataChannel có trong Chrome, Safari, Firefox, Opera và Samsung Internet. Trò chơi Cube Slam sử dụng API này để truyền đạt trạng thái trò chơi. Chơi với bạn bè hoặc chơi với chú gấu! Nền tảng sáng tạo Sharefest cho phép chia sẻ tệp thông qua RTCDataChannelpeerCDN cho thấy WebRTC có thể cho phép phân phối nội dung ngang hàng.

Để biết thêm thông tin về RTCDataChannel, hãy xem quy cách giao thức nháp của IETF.

Bảo mật

Có một số cách mà ứng dụng hoặc trình bổ trợ giao tiếp theo thời gian thực có thể ảnh hưởng đến tính bảo mật. Ví dụ:

  • Phương tiện hoặc dữ liệu chưa mã hoá có thể bị chặn giữa các trình duyệt hoặc giữa trình duyệt và máy chủ.
  • Ứng dụng có thể ghi lại và phân phối video hoặc âm thanh mà người dùng không biết.
  • Phần mềm độc hại hoặc vi-rút có thể được cài đặt cùng với một ứng dụng hoặc trình bổ trợ có vẻ vô hại.

WebRTC có một số tính năng để tránh những vấn đề này:

  • Các hoạt động triển khai WebRTC sử dụng các giao thức bảo mật, chẳng hạn như DTLSSRTP.
  • Bạn bắt buộc phải mã hoá tất cả các thành phần WebRTC, bao gồm cả cơ chế báo hiệu.
  • WebRTC không phải là một trình bổ trợ. Các thành phần của tiện ích này chạy trong hộp cát của trình duyệt chứ không chạy trong một quy trình riêng biệt. Các thành phần không yêu cầu cài đặt riêng và được cập nhật bất cứ khi nào trình duyệt được cập nhật.
  • Bạn phải cấp quyền truy cập vào camera và micrô một cách rõ ràng. Khi camera hoặc micrô đang chạy, giao diện người dùng sẽ cho thấy rõ điều này.

Bài viết này không đề cập đến toàn bộ nội dung thảo luận về bảo mật cho nội dung nghe nhìn phát trực tuyến. Để biết thêm thông tin, hãy xem Cấu trúc bảo mật WebRTC được đề xuất của IETF.

Kết luận

Các API và tiêu chuẩn của WebRTC có thể dân chủ hoá và phân cấp các công cụ để sáng tạo nội dung và giao tiếp, bao gồm cả điện thoại, trò chơi, sản xuất video, sáng tác nhạc và thu thập tin tức.

Công nghệ này có tính đột phá hơn nhiều.

Theo nhận định của blogger Phil Edholm, "WebRTC và HTML5 có thể mang đến sự chuyển đổi tương tự cho hoạt động giao tiếp theo thời gian thực như trình duyệt ban đầu đã làm cho thông tin."

Công cụ dành cho nhà phát triển

Tìm hiểu thêm

  • Phiên WebRTC của Justin Uberti tại Google I/O 2012
  • Alan B. Johnston và Daniel C. Burnett hiện đang duy trì một cuốn sách về WebRTC ở lần xuất bản thứ ba dưới dạng bản in và sách điện tử tại webrtcbook.com.
  • webrtc.org là nơi tập hợp mọi thông tin về WebRTC, bao gồm cả bản minh hoạ, tài liệu và nội dung thảo luận.
  • discuss-webrtc là một Nhóm trên Google để thảo luận về kỹ thuật WebRTC.
  • @webrtc
  • Tài liệu về Talk của Google Developers cung cấp thêm thông tin về tính năng truyền tải NAT, STUN, máy chủ chuyển tiếp và thu thập ứng cử viên.
  • WebRTC trên GitHub
  • Stack Overflow là nơi phù hợp để tìm câu trả lời và đặt câu hỏi về WebRTC.

Tiêu chuẩn và giao thức

Tóm tắt về khả năng hỗ trợ WebRTC

API MediaStreamgetUserMedia

  • Chrome trên máy tính phiên bản 18.0.1008 trở lên; Chrome cho Android phiên bản 29 trở lên
  • Opera 18 trở lên; Opera cho Android 20 trở lên
  • Opera 12, Opera Mobile 12 (dựa trên công cụ Presto)
  • Firefox 17 trở lên
  • Microsoft Edge 16 trở lên
  • Safari 11.2 trở lên trên iOS và 11.1 trở lên trên MacOS
  • UC 11.8 trở lên trên Android
  • Samsung Internet 4 trở lên

API RTCPeerConnection

  • Chrome trên máy tính từ phiên bản 20 trở lên; Chrome cho Android từ phiên bản 29 trở lên (không có cờ)
  • Opera 18 trở lên (bật theo mặc định); Opera cho Android 20 trở lên (bật theo mặc định)
  • Firefox 22 trở lên (bật theo mặc định)
  • Microsoft Edge 16 trở lên
  • Safari 11.2 trở lên trên iOS và 11.1 trở lên trên MacOS
  • Samsung Internet 4 trở lên

API RTCDataChannel

  • Phiên bản thử nghiệm trong Chrome 25, nhưng ổn định hơn (và có khả năng tương tác với Firefox) trong Chrome 26 trở lên; Chrome cho Android 29 trở lên
  • Phiên bản ổn định (và có khả năng tương tác với Firefox) trong Opera 18 trở lên; Opera cho Android 20 trở lên
  • Firefox 22 trở lên (bật theo mặc định)

Để biết thêm thông tin chi tiết về khả năng hỗ trợ API trên nhiều nền tảng, chẳng hạn như getUserMediaRTCPeerConnection, hãy xem caniuse.comTrạng thái của nền tảng Chrome.

Các API gốc cho RTCPeerConnection cũng có trong tài liệu trên webrtc.org.