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 môi trường web mở và không có cồng kềnh.

Brendan Eich, nhà phát minh ra JavaScript

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 thật 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 bạn. Đó là tầm nhìn của WebRTC.

Bạn có muốn dùng thử tính năng này không? Dịch vụ 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 tốt để 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 phòng trò chuyện và cho phép ứng dụng dùng webcam của bạn.
  3. Mở URL hiển thị ở cuối trang trong thẻ mới hoặc trên một máy tính khác, tốt hơn là mở URL đó.

Bắt đầu nhanh

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

Ngoài ra, bạn có thể chuyển thẳng vào 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 xây dựng một ứng dụng trò chuyện video hoàn chỉnh, bao gồm một máy chủ báo hiệu đơn giản.

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

Một trong những thách thức lớn cuối cùng đối với web là cho phép giao tiếp của con người thông qua giọng nói và video: giao tiếp theo thời gian thực hoặc gọi tắt là RTC. RTC phải tự nhiên trong ứng dụng web như nhập văn bản vào một mục nhập văn bản. Nếu không có YouTube Create, bạn bị hạn chế khả năng đổi mới và phát triển các cách mới để mọi người tương tác với nhau.

Trong quá khứ, RTC là một công ty và phức tạp, đòi hỏi các công nghệ âm thanh và video đắt đỏ 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ó thật khó khăn và tốn thời gian, đặc biệt là trên web.

Trò chuyện video trong Gmail trở nên phổ biến vào năm 2008 và vào năm 2011, Google đã giới thiệu Hangouts, ứng dụng này sử dụng Talk (cũ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ã và kỹ thuật huỷ tiếng vọng. Google đã cung cấp nguồn mở cho những công nghệ do GIPS phát triển và hợp tác với các cơ quan tiêu chuẩn có liên quan tại Lực lượng đặc nhiệm kỹ thuật Internet (IETF) và tổ chức the World Wide Web Consortium (W3C) để đảm bảo sự đồng thuận của toàn ngành. Vào tháng 5 năm 2011, nhà phát triển thiết bị Android đã triển khai lần triển khai đầu tiên của WebRTC.

WebRTC triển khai các tiêu chuẩn mở để giao tiếp qua video, âm thanh và dữ liệu theo thời gian thực, không cần trình bổ trợ. Nhu cầu này 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 plugin. Các ứ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 khó chịu.
  • 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à bảo trì, đồng thời có thể cần phải được cấp phép và tích hợp với công nghệ phức tạp và đắt đỏ. 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!

Nguyên tắc định hướng 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?

Dịch vụ 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 các ứng dụng gốc WebKitGTK+ và Qt.

WebRTC triển khai 3 API sau: – MediaStream (còn được gọi là getUserMedia) – RTCPeerConnectionRTCDataChannel

API được xác định theo 2 thông số kỹ thuật sau:

Cả ba 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: Đối với các bản minh hoạ và mã, hãy xem Mẫu WebRTC hoặc thử các ví dụ thú vị của Chris Wilson sử dụng getUserMedia làm đầu vào cho âm thanh web.

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

RTCDataChannel: Để xem ví dụ thực tế, hãy xem 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ả ba API để tạo một ứng dụng đơn giản cho 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 sau:

  • Nghe trực tuyến âm thanh, video hoặc dữ liệu khác.
  • Lấy thông tin mạng, chẳng hạn như địa chỉ IP và cổng, đồng thời trao đổi thông tin đó với các ứng dụng WebRTC khác (còn gọi là ứng dụng 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 hoạt động 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ề khả năng của nội dung đa phương tiện và ứng dụng, chẳng hạn như độ phân giải và bộ mã hoá và giải mã.
  • Giao tiếp với âm thanh, video hoặc dữ liệu phát trực tuyến.

Để thu thập và giao tiếp dữ liệu truyền trực tuyến, WebRTC sẽ 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ư máy ảnh và micrô của người dùng.
  • RTCPeerConnection cho phép gọi thoại hoặc gọi video bằng các phương tiện để mã hoá và quản lý băng thông.
  • RTCDataChannel cho phép giao tiếp ngang hàng trong dữ liệu chung.

(Sẽ có nội dung thảo luận chi tiết về mạng và các khía cạnh báo hiệu của WebRTC sau.)

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

API MediaStream đại diện cho các luồng nội dung nghe nhìn được đồng bộ hoá. Ví dụ: một luồng lấy từ đầu vào máy ảnh 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>, vì đây là một phần hoàn toàn khác.)

Có lẽ cách dễ nhất để hiểu về API MediaStream là xem nó trong môi trường tự nhiên:

  1. Trong trình duyệt, hãy chuyển đến phần Mẫu WebRTC getUserMedia.
  2. Mở bảng điều khiển.
  3. Kiểm tra biến stream, nằm trong phạm vi toàn cầu.

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

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

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

Đối với ví dụ về getUserMedia, stream.getAudioTracks() trả về một mảng trống (vì không có âm thanh) và giả sử một webcam đang hoạt động được kết nối, stream.getVideoTracks() sẽ trả về một mảng MediaStreamTrack đại diện cho luồng dữ liệu từ webcam. Mỗi MediaStreamTrack có một loại ('video' hoặc 'audio'), một label (tương tự như 'FaceTime HD Camera (Built-in)') và đại diện cho một hoặc nhiều kênh của âm thanh hoặc video. Trong trường hợp này, chỉ có một bản video và không có âm thanh, nhưng bạn có thể dễ dàng tưởng tượng những trường hợp sử dụng có nhiều bản nhạc hơn, chẳng hạn như một ứng dụng nhắn tin nhận luồng phát 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 thành phần video bằng cách đặt thuộc tính srcObject. Trước đây, bạn có thể thực hiện việc này 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 tính năng này đã ngừng hoạt động.

Bạn cũng có thể sử dụng getUserMedia làm nút đầu vào cho API Web âm thanh:

// 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ể tích hợp getUserMedia. Việc thêm quyền audioCapture và/hoặc videoCapture vào tệp kê khai sẽ cho phép yêu cầu quyền và chỉ cấp quyền một lần sau 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 máy ảnh hoặc micrô.

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

Ý định có thể là để bật MediaStream cho mọi nguồn dữ liệu truyền trực tuyến, chứ không chỉ máy ảnh hoặc micrô. Điều này cho phép truyền trực tuyến 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 nguồn đầu vào khác.

getUserMedia() thực sự hoạt độ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 photobooth sử dụng WebGL để thêm các hiệu ứng lạ và tuyệt vời vào ảnh có thể chia sẻ hoặc lưu 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.
  • Máy ảnh ASCII sử dụng Canvas API để tạo hình ảnh ASCII.
Hình ảnh ASCII do idevelop.ro/ascii-camera tạo
Nghệ thuật ASCII của gUM!

Giới hạn

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

Để biết ví dụ, hãy xem phần Mẫu WebRTC getUserMedia: chọn độ phân giải.

Việc đặt một giá trị ràng buộc không được phép sẽ cung cấp DOMException hoặc OverconstrainedError trong trường hợp không có độ phân giải mà bạn yêu cầu. Để xem ví dụ thực tế, hãy xem phần Mẫu WebRTC getUserMedia: chọn độ phân giải để xem bản minh hoạ.

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

Các ứng dụng Chrome cũng giúp bạn chia sẻ video trực tiếp trên một thẻ trình duyệt hoặc toàn bộ màn hình thông qua API chrome.tabCapturechrome.desktopCapture. (Để xem bản minh hoạ và thông tin khác, hãy xem bài viết Chia sẻ màn hình qua WebRTC. Bài viết đã được vài năm tuổi, nhưng vẫn còn 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 quy tắc ràng buộc thử nghiệm chromeMediaSource. Xin lưu ý rằng tính năng chụp ảnh màn hình yêu cầu HTTPS và chỉ nên dùng để phát triển do tính năng này được bật thông qua cờ dòng lệnh như giải thích trong bài đăng này.

Tín hiệu: Kiểm soát phiên, mạng và thông tin đa phương tiện

WebRTC sử dụng RTCPeerConnection để giao tiếp dữ liệu phát trực tuyến giữa các trình duyệt (còn gọi là ứng dụng ngang hàng), nhưng cũng cần có 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. Dịch vụ WebRTC không chỉ định các phương thức tín hiệu và giao thức. Tín hiệu không nằm trong API RTCPeerConnection.

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

Tín 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 tạo hoặc đóng hoạt động giao tiếp và báo cáo lỗi.
  • Cấu hình mạng: với bên ngoài, địa chỉ IP và cổng cho máy tính của bạn là gì?
  • Khả năng truyền thông: những bộ mã hoá và giải mã nào có thể được trình duyệt của bạn và trình duyệt giao tiếp xử lý?

Trao đổi thông tin thông qua tín hiệu phải hoàn tất thành công trước khi quá trình truyền trực tuyến ngang hàng có thể bắt đầu.

Ví dụ: hãy tưởng tượng Alice muốn giao tiếp với Bob. Dưới đây là mã mẫu từ thông số kỹ thuật WebRTC W3C, cho thấy quy trình truyền tín hiệu trong thực tế. Mã này giả định rằng tồn tại một 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 thê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);
  }
};

Đầu 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 và cổng mạng bằ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 ứng viên đã 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 số cơ chế khác.
  3. Khi Bob nhận được tin nhắn ứng viên từ Alice, anh ta gọi addIceCandidate để thêm ứng viên đó vào phần mô tả ứng viên từ xa.

Các ứng dụng WebRTC (còn gọi là ứng dụng ngang hàng hoặc Alice và Bob trong ví dụ này) cũng cần xác định và trao đổi thông tin phương tiện âm thanh và video cục bộ và từ xa, chẳng hạn như khả năng về độ phân giải và bộ mã hoá và giải mã. Hoạt động báo hiệu để trao đổi thông tin cấu hình nội dung đa phương tiện sẽ tiếp tục bằng cách trao đổi ưu đãicâu trả lời thông qua Giao thức mô tả phiên (SDP):

  1. Alice chạy phương thức RTCPeerConnection createOffer(). Trả về từ sự kiện này được chuyển một RTCSessionDescription – phần 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(), sau đó gửi nội dung mô tả phiên này cho Bob thông qua kênh báo hiệu của người đó. Lưu ý rằng RTCPeerConnection sẽ không bắt đầu thu thập các ứng cử viên cho đến khi setLocalDescription() được gọi. Thông tin này được mã hoá trong bản nháp JSEP IETF.
  3. Bob thiết lập nội dung mô tả mà Alice gửi cho anh dưới dạng 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 phương thức này vào nội dung mô tả từ xa mà anh nhận được từ Alice để tạo một phiên cục bộ tương thích với phương thức của cô. Lệnh gọi lại createAnswer() được truyền một RTCSessionDescription. Bob đặt câu trả lời đó 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 đặt đó làm nội dung mô tả từ xa bằng setRemoteDescription.
  6. Ping!

Đối tượng RTCSessionDescription là các blob phù hợp với Giao thức mô tả phiên, SDP. Đã 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

Việc thu nạp và trao đổi thông tin về mạng và nội dung đa phương tiện có thể được thực hiện đồng thời, nhưng cả hai quy trình đều phải hoàn tất thì quá trình truyền trực tuyến âm thanh và video giữa các ứng dụng ngang hàng có thể bắt đầu.

Cấu trúc ưu đãi/câu trả lời được mô tả trước đó gọi là Giao thức thiết lập phiên JavaScript hay JSEP. (Có một ảnh động tuyệt vời giải thích quy trình truyền tín hiệu và 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

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 phương thức gọi và hàm được gọi - hoặc nếu không thành công, thông qua máy chủ chuyển tiếp trung gian (sẽ tìm hiểu thêm về điều này ở phần sau). Phát trực tiếp 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ả trong việc truyền dữ liệu trực tuyến giữa các ứng dụng ngang hàng.

Dưới đây là sơ đồ cấu trúc WebRTC thể hiện vai trò của RTCPeerConnection. Như bạn sẽ 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 (theo webrtc.org)

Từ góc độ JavaScript, điều chính cần hiểu được qua sơ đồ này là RTCPeerConnection bảo vệ các nhà phát triển web khỏi vô số phức tạp ẩn náu bên dưới. Những bộ mã hoá và giải mã và giao thức mà WebRTC sử dụng phải thực hiện rất nhiều hoạt động để có thể giao tiếp theo thời gian thực, kể cả qua những mạng không ổn định:

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

Mã W3C trước đây là một ví dụ đơn giản về WebRTC từ góc độ tín hiệu. Sau đây là hướng dẫn từng bước về 2 ứng dụng WebRTC đang hoạt động. Ví dụ đầu tiên là ví dụ đơn giản để minh hoạ RTCPeerConnection, còn ví dụ thứ hai là ứng dụng trò chuyện video hoàn chỉnh hoạt động.

RTCPeerConnection không có máy chủ

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

Trong ví dụ này, pc1 đại diện cho ứng dụng ngang hàng cục bộ (phương thức gọi) và pc2 đại diện cho ứng dụng ngang hàng từ xa (phương thức gọi).

Người gọi

  1. Tạo RTCPeerConnection mới và thêm luồng từ getUserMedia(): ```js // Máy chủ là tệp cấu hình tuỳ chọn. (Xem cuộc thảo luận về TURN và STUN sau.) pc1 = RTCPeerConnection(máy chủ) mới; // ... localStream.getTracks().forEach((track) =&gt; { pc1.addTrack(theo dõi, localStream); });
  1. Tạo ưu đãi rồi đặt ưu đãi đó làm nội dung mô tả địa phương 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 sử dụng tín hiệu vì cả phương thức gọi và hàm đượ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 );

Được gọi

  1. Tạo pc2 và khi luồng từ pc1 được thêm, 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ủ

Trong thực tế, WebRTC cần máy chủ nhưng đơn giản để những điều sau có thể xảy ra:

  • Người dùng tìm thấy nhau và trao đổi thông tin thực tế, chẳng hạn như tên.
  • Thông tin về mạng trao đổi ứng dụng khách WebRTC (ứng dụng ngang hàng).
  • Các ứng dụng 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 của video.
  • Ứng dụng khách WebRTC truyền tải cổng NAT và tường lửa.

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

  • Khám phá và giao tiếp với người dùng
  • Tín hiệu
  • Truyền tải 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

Truyền tải NAT, kết nối mạng ngang hàng và các yêu cầu đối với việc tạo ứng dụng máy chủ để khám phá và gửi tín hiệu cho người dùng nằm ngoài phạm vi của bài viết này. Có thể nói rằng giao thức STUN và phần mở rộng của giao thức này, TURN, được khung ICE sử dụng để cho phép RTCPeerConnection đối phó với quá trình truyền tải NAT và những biến động khác về mạng.

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

Đang tìm các đề xuất kết nối
Tìm các đề xuất kết nối

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

Đường dẫn dữ liệu WebRTC
Đường dẫ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ản trình bày WebRTC của Google I/O năm 2013. (Các trang trình bày đưa ra ví dụ về cách triển khai máy chủ TURN và STUN.)

Ứng dụng trò chuyện video đơn giản

Bản minh hoạ về tính năng trò chuyện qua video tại appr.tc là một nơi phù hợp để bạn dùng thử WebRTC, chẳng hạn như tính năng truyền tín hiệu và truyền tải NAT/tường lửa bằng máy chủ STUN. Ứng dụng này sử dụng adapter.js, một đoạn mã đệm để cách ly ứ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ố ý trình bày chi tiết trong việc ghi nhật ký. Hãy 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 từng bước chi tiết về mã.

Xin lỗi mạng

WebRTC, như hiện đã triển khai, chỉ hỗ trợ giao tiếp một với một nhưng có thể dùng trong các tình huống mạng phức tạp hơn, chẳng hạn như với nhiều thiết bị 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à 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 Bộ điều khiển đa điểm
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ỉ thể hiện hoạt động giao tiếp giữa các trình duyệt web, nhưng máy chủ cổng vào có thể cho phép ứ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 hệ thống VOIP. Vào tháng 5 năm 2012, Doubango Telecom đã cung cấp nguồn mở ứng dụng SIP sipml5 được xây dựng bằng WebRTC và WebSocket (cùng với các mục đích sử dụng tiềm năng khác) cho phép 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 cơ cấu thông tin liên lạc trong thảm hoạ trong một chiếc cặp sử dụng hộp OpenBTS để cho phép giao tiếp giữa điện thoại phổ thông và máy tính thông qua WebRTC. Liên lạc qua điện thoại mà không cần nhà mạng!

Bản minh hoạ về Tethr/Tropo tại Google I/O 2012
Tethr/Tropo: Thông tin liên lạc về thảm hoạ trong cặp tài liệu

API RTCDataChannel<

Cũng như âm thanh và video, WebRTC hỗ trợ giao tiếp theo thời gian thực đối với các loại dữ liệu khác.

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

Có nhiều trường hợp có thể sử dụng API, bao gồm:

  • Trò chơi
  • Ứng dụng máy tính từ xa
  • Trò chuyện bằng tin nhắ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 giúp khai thác tối đa RTCPeerConnection và cho phép giao tiếp ngang hàng mạnh mẽ và linh hoạt:

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

Cú pháp tương tự có chủ ý với 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 lỗ hổng bảo mật để xử lý tường lửa và NAT.

RTCDataChannel hoạt động trên Chrome, Safari, Firefox, Opera và Samsung Internet. Trò chơi CubeSau sử dụng API để thông báo trạng thái trò chơi. Chơi bạn bè hoặc chơi gấu! Nền tảng đổi mới Sharefest đã cho phép chia sẻ tệp qua RTCDataChannelpeerCDN để hé lộ cách WebRTC có thể 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 về giao thức nháp của IETF.

Bảo mật

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

  • Nội dung nghe nhìn hoặc dữ liệu không được 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ể quay 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 trình bổ trợ hoặc ứng dụng xem như vô hại.

WebRTC có một số tính năng giúp tránh những sự cố sau đây:

  • Quá trình triển khai WebRTC sử dụng các giao thức bảo mật, chẳng hạn như DTLSSRTP.
  • Mã hóa là bắt buộc đối với tất cả các thành phần WebRTC, bao gồm cả các cơ chế báo hiệu.
  • WebRTC không phải là 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 tiến trình riêng biệt. Các thành phần không yêu cầu cài đặt riêng biệt và được cập nhật mỗi khi trình duyệt được cập nhật.
  • Bạn phải cấp quyền truy cập máy ảnh và micrô một cách rõ ràng. Khi máy ảnh hoặc micrô đang chạy, điều này sẽ được hiển thị rõ ràng qua giao diện người dùng.

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

Kết luận

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

Công nghệ không vì thế mà bị gián đoạn nhiều hơn thế.

Như người viết blog Phil Edholm đã nói, "Có khả năng WebRTC và HTML5 có thể thực hiện cùng một chuyển đổi để giao tiếp theo thời gian thực như trình duyệt ban đầu đã thực hiện cho thông tin".

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

  • Bạn có thể xem số liệu thống kê WebRTC cho một phiên đang diễn ra tại:
    • about://webrtc-internals trong Chrome
    • opera://webrtc-internals trong Opera
    • about:webrtc trong Firefox
      trang chrome://webrtc-internals
      ảnh chụp màn hình chrome://webrtc-internals
  • Ghi chú tương tác trên nhiều trình duyệt
  • adapter.js là một đoạn mã đệm JavaScript cho WebRTC do Google duy trì với sự trợ giúp của cộng đồng WebRTC. Các tiền tố đó tóm tắt các tiền tố của nhà cung cấp, những điểm khác biệt về trình duyệt và những thay đổi về thông số kỹ thuật.
  • Để tìm hiểu thêm về các quy trình truyền tín hiệu WebRTC, hãy kiểm tra đầu ra nhật ký appr.tc vào bảng điều khiển.
  • Nếu đã quá nhiều, bạn nên sử dụng khung WebRTC hoặc thậm chí là một dịch vụ WebRTC hoàn chỉnh.
  • Báo cáo lỗi và yêu cầu tính năng luôn được coi trọng:

Tìm hiểu thêm

Các tiêu chuẩn và giao thức

Tóm tắt về dịch vụ hỗ trợ WebRTC

API MediaStreamgetUserMedia

  • Chrome trên máy tính để bàn 18.0.1008 trở lên; Chrome dành cho Android 29 trở lên
  • Opera 18 trở lên; Opera dành 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 dành cho máy tính để bàn 20 trở lên; Chrome dành cho Android 29 trở lên (không gắn cờ)
  • Opera 18 trở lên (được bật theo mặc định); Opera cho Android 20 trở lên (được 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 dành 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 dành 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ề tính năng hỗ trợ nhiều nền tảng cho các API, chẳng hạn như getUserMediaRTCPeerConnection, hãy xem bài viết caniuse.comTrạng thái nền tảng Chrome.

API gốc dành cho RTCPeerConnection cũng có tại tài liệu trên webrtc.org.