Giao tiếp theo thời gian thực bằng WebRTC

1. Giới thiệu

WebRTC là một dự án nguồn mở cho phép giao tiếp âm thanh, video và dữ liệu theo thời gian thực trong ứng dụng web và ứng dụng gốc.

WebRTC có một số API JavaScript – hãy nhấp vào đường liên kết để xem bản minh họa.

Tôi có thể sử dụng WebRTC ở đâu?

Trong Firefox, Opera và trong Chrome trên máy tính và Android. Dịch vụ WebRTC cũng được cung cấp cho các ứng dụng gốc trên iOS và Android.

Báo hiệu là gì?

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, 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 điều khiển, một quy trình được gọi là báo hiệu. WebRTC không chỉ định các giao thức và phương thức tín hiệu. Trong lớp học lập trình này, bạn sẽ sử dụng Socket.IO để nhắn tin, nhưng cũng có nhiều lựa chọn thay thế.

STUN và TURN là gì?

WebRTC được thiết kế để hoạt động ngang hàng, vì vậy người dùng có thể kết nối theo tuyến trực tiếp nhất có thể. Tuy nhiên, WebRTC được xây dựng để đối phó với hoạt động nối mạng trong thế giới thực: các ứng dụng khách cần truyền tải cổng NAT và tường lửa, đồng thời cần có phương thức kết nối mạng ngang hàng dự phòng trong trường hợp không kết nối trực tiếp được. Trong quá trình này, các API WebRTC sử dụng máy chủ STUN để lấy địa chỉ IP của máy tính của bạn, đồng thời sử dụng các máy chủ TURN để hoạt động như máy chủ chuyển tiếp trong trường hợp giao tiếp ngang hàng không thành công. (WebRTC trong thực tế sẽ giải thích chi tiết hơn.)

WebRTC có an toàn không?

Mã hoá là bắt buộc đối với tất cả các thành phần WebRTC. Đồng thời, bạn chỉ có thể dùng các API JavaScript của dịch vụ này từ các nguồn gốc bảo mật (HTTPS hoặc máy chủ cục bộ). Cơ chế tín hiệu không được xác định trong các tiêu chuẩn WebRTC. Vì vậy, bạn có trách nhiệm đảm bảo sử dụng giao thức an toàn.

2. Tổng quan

Tạo một ứng dụng để quay video và chụp nhanh bằng webcam của bạn cũng như chia sẻ các video đó với nhau qua WebRTC. Đồng thời, bạn sẽ tìm hiểu cách sử dụng các API WebRTC chính và thiết lập máy chủ nhắn tin bằng Node.js.

Kiến thức bạn sẽ học được

  • Quay video từ webcam của bạn
  • Phát video bằng RTCPeerConnection
  • Truyền dữ liệu trực tuyến bằng RTCDataChannel
  • Thiết lập một dịch vụ tín hiệu để trao đổi tin nhắn
  • Kết hợp tín hiệu và kết nối ngang hàng
  • Chụp và chia sẻ ảnh qua kênh dữ liệu

Bạn cần có

  • Chrome 47 trở lên
  • Máy chủ web dành cho Chrome hoặc sử dụng máy chủ web mà bạn chọn.
  • Mã mẫu
  • Trình chỉnh sửa văn bản
  • Kiến thức cơ bản về HTML, CSS và JavaScript

3. Nhận mã mẫu

Tải mã xuống

Nếu đã quen với git, bạn có thể tải mã nguồn cho lớp học lập trình này xuống qua GitHub bằng cách sao chép mã:

git clone https://github.com/googlecodelabs/webrtc-web

Ngoài ra, bạn có thể nhấp vào nút sau để tải tệp .zip của mã xuống:

Mở tệp zip đã tải xuống. Thao tác này sẽ giải nén thư mục dự án (adaptive-web-media) chứa một thư mục cho mỗi bước của lớp học lập trình này, cùng với tất cả tài nguyên bạn cần.

Bạn sẽ làm mọi việc lập trình trong thư mục có tên là work.

Các thư mục step-nn chứa phiên bản đã hoàn chỉnh cho từng bước của lớp học lập trình này. Bạn có thể tham khảo các thông tin này.

Cài đặt và xác minh máy chủ web

Mặc dù bạn có thể thoải mái sử dụng máy chủ web của riêng mình, nhưng lớp học lập trình này được thiết kế để hoạt động tốt với Máy chủ web Chrome. Nếu chưa cài đặt ứng dụng đó, bạn có thể cài đặt ứng dụng từ Cửa hàng Chrome trực tuyến.

6ddeb4aee53c0f0e.png.

Sau khi cài đặt ứng dụng Máy chủ web cho Chrome, hãy nhấp vào lối tắt Ứng dụng Chrome từ thanh dấu trang, trang Thẻ mới hoặc từ Trình chạy ứng dụng:

1d2b4aa977ab7e24.pngS

Nhấp vào biểu tượng Máy chủ web:

27fce4494f641883.pngS

Tiếp theo, bạn sẽ thấy hộp thoại này. Hộp thoại này cho phép bạn định cấu hình máy chủ web cục bộ:

Ảnh chụp màn hình lúc 11:48:14 sáng 18/2/2016.png

Nhấp vào nút CHỌN THƯ MỤC rồi chọn thư mục công việc mà bạn vừa tạo. Thao tác này sẽ cho phép bạn xem tiến trình thực hiện của mình trong Chrome thông qua URL được làm nổi bật trong hộp thoại Máy chủ web trong phần (Các) URL máy chủ web.

Trong phần Tùy chọn, hãy chọn hộp bên cạnh mục Tự động hiển thị chỉ mục.html như minh họa dưới đây:

Ảnh chụp màn hình lúc 11:56:30 sáng 18/2/2016.png

Sau đó, dừng và khởi động lại máy chủ bằng cách trượt nút bật/tắt có nhãn Máy chủ web: BẮT ĐẦU sang trái rồi quay lại bên phải.

Ảnh chụp màn hình 18/02/2016 lúc 12:22.18 CH.png

Bây giờ, hãy truy cập trang web làm việc của bạn trong trình duyệt web bằng cách nhấp vào URL máy chủ web được đánh dấu. Bạn sẽ thấy một trang như sau, tương ứng với work/index.html:

18a705cb6cc5181.pngS

Rõ ràng là ứng dụng này chưa làm được gì thú vị — cho đến nay, đây chỉ là bộ xương tối thiểu mà chúng tôi đang sử dụng để đảm bảo máy chủ web của bạn hoạt động bình thường. Bạn sẽ thêm chức năng và tính năng bố cục trong các bước tiếp theo.

4. Phát trực tuyến video từ webcam

Kiến thức bạn sẽ học được

Trong bước này, bạn sẽ tìm hiểu cách:

  • Phát trực tuyến video từ webcam.
  • Thao tác với chế độ phát luồng.
  • Sử dụng CSS và SVG để thao tác với video.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-01.

Một dấu gạch ngang của HTML...

Thêm một phần tử video và một phần tử script vào index.html trong thư mục công việc của bạn:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <video autoplay playsinline></video>

  <script src="js/main.js"></script>

</body>

</html>

...và một chút JavaScript

Thêm đoạn mã sau vào main.js trong thư mục js:

'use strict';

// On this codelab, you will be streaming only video (video: true).
const mediaStreamConstraints = {
  video: true,
};

// Video element where stream will be placed.
const localVideo = document.querySelector('video');

// Local stream that will be reproduced on the video.
let localStream;

// Handles success by adding the MediaStream to the video element.
function gotLocalMediaStream(mediaStream) {
  localStream = mediaStream;
  localVideo.srcObject = mediaStream;
}

// Handles error by logging a message to the console with the error message.
function handleLocalMediaStreamError(error) {
  console.log('navigator.getUserMedia error: ', error);
}

// Initializes media stream.
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);

Dùng thử

Mở index.html trong trình duyệt của bạn và bạn sẽ thấy nội dung tương tự (tất nhiên là hiển thị từ webcam của bạn!):

9297048e43ed0f3d.pngS

Cách hoạt động

Sau lệnh gọi getUserMedia(), trình duyệt sẽ yêu cầu người dùng cấp quyền truy cập vào camera (nếu đây là lần đầu tiên quyền truy cập vào camera được yêu cầu cho nguồn hiện tại). Nếu thành công, một MediaStream sẽ được trả về. Phần tử đa phương tiện có thể sử dụng thuộc tính này thông qua srcObject:

navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
  .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);


}
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

Đối số constraints cho phép bạn chỉ định nội dung nghe nhìn nhận được. Trong ví dụ này là chỉ video vì âm thanh bị tắt theo mặc định:

const mediaStreamConstraints = {
  video: true,
};

Bạn có thể áp dụng các quy tắc ràng buộc cho các yêu cầu bổ sung, chẳng hạn như độ phân giải của video:

const hdConstraints = {
  video: {
    width: {
      min: 1280
    },
    height: {
      min: 720
    }
  }
}

Thông số kỹ thuật MediaTrackConstraints liệt kê mọi loại quy tắc ràng buộc tiềm năng, mặc dù không phải trình duyệt nào cũng hỗ trợ mọi tuỳ chọn. Nếu máy ảnh hiện được chọn không hỗ trợ độ phân giải mà bạn yêu cầu, thì getUserMedia() sẽ bị OverconstrainedError từ chối và người dùng sẽ không được nhắc cấp quyền truy cập vào máy ảnh của họ.

Nếu getUserMedia() thành công, luồng video từ webcam sẽ được đặt làm nguồn của phần tử video:

function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
}

Điểm thưởng

  • Đối tượng localStream được truyền đến getUserMedia() nằm trong phạm vi toàn cục, vì vậy, bạn có thể kiểm tra đối tượng này từ bảng điều khiển của trình duyệt: mở bảng điều khiển, nhập stream và nhấn Return. (Để xem bảng điều khiển trong Chrome, hãy nhấn Ctrl-Shift-J hoặc Command-Option-J nếu bạn đang sử dụng máy Mac.)
  • localStream.getVideoTracks() trả về giá trị nào?
  • Hãy thử gọi localStream.getVideoTracks()[0].stop().
  • Xem xét đối tượng ràng buộc: điều gì xảy ra khi bạn thay đổi đối tượng này thành {audio: true, video: true}?
  • Phần tử video có kích thước như thế nào? Làm cách nào để bạn có được kích thước tự nhiên của video từ JavaScript, thay vì kích thước hiển thị? Sử dụng Công cụ dành cho nhà phát triển Chrome để kiểm tra.
  • Hãy thử thêm bộ lọc CSS vào phần tử video. Ví dụ:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • Hãy thử thêm bộ lọc SVG. Ví dụ:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

Kiến thức bạn học được

Trong bước này, bạn đã tìm hiểu cách:

  • Tải video từ webcam của bạn.
  • Đặt các quy tắc ràng buộc đối với nội dung nghe nhìn.
  • Mối liên hệ với thành phần video.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-01.

Mẹo

  • Đừng quên thuộc tính autoplay trong phần tử video. Nếu không, bạn sẽ chỉ thấy một khung hình!
  • Có nhiều tuỳ chọn khác cho các quy tắc ràng buộc trong getUserMedia(). Hãy xem bản minh hoạ tại webrtc.github.io/samples/src/content/peerconnection/constraints. Như bạn sẽ thấy, có nhiều mẫu WebRTC thú vị trên trang web đó.

Phương pháp hay nhất

  • Đảm bảo phần tử video của bạn không tràn vùng chứa. Chúng tôi thêm widthmax-width để thiết lập kích thước ưu tiên và kích thước tối đa cho video. Trình duyệt sẽ tự động tính chiều cao:
video {
  max-width: 100%;
  width: 320px;
}

Tiếp theo

Bạn đã có video nhưng làm cách nào để phát trực tiếp? Tìm hiểu ở bước tiếp theo!

5. Phát video bằng RTCPeerConnection

Kiến thức bạn sẽ học được

Trong bước này, bạn sẽ tìm hiểu cách:

  • Loại bỏ sự khác biệt của trình duyệt với đoạn mã đệm WebRTC, adapter.js.
  • Sử dụng API RTCPeerConnection để phát trực tuyến video.
  • Điều khiển hoạt động ghi lại và phát trực tuyến nội dung nghe nhìn.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-2.

RTCPeerConnection là gì?

RTCPeerConnection là một API để thực hiện lệnh gọi WebRTC để phát trực tuyến video và âm thanh cũng như trao đổi dữ liệu.

Ví dụ này thiết lập kết nối giữa hai đối tượng RTCPeerConnection (được gọi là ngang hàng) trên cùng một trang.

Không sử dụng nhiều trong thực tế, nhưng tốt để hiểu cách RTCPeerConnection hoạt động.

Thêm các thành phần video và nút điều khiển

Trong index.html, hãy thay thế một phần tử video bằng 2 phần tử video và 3 nút:

<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>


<div>
  <button id="startButton">Start</button>
  <button id="callButton">Call</button>
  <button id="hangupButton">Hang Up</button>
</div>

Một phần tử video sẽ hiển thị luồng từ getUserMedia() và phần tử còn lại sẽ hiển thị cùng một video được phát qua kết nối RTCPeer. (Trong ứng dụng thực tế, một phần tử video sẽ hiển thị luồng cục bộ và phần tử còn lại là luồng từ xa.)

Thêm gtag.js

Thêm một đường liên kết đến phiên bản hiện tại của adapter.js ở phía trên đường liên kết đến main.js:

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

Index.html hiện sẽ có dạng như sau:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>

  <div>
    <button id="startButton">Start</button>
    <button id="callButton">Call</button>
    <button id="hangupButton">Hang Up</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

Cài đặt mã RTCPeerConnection

Thay thế main.js bằng phiên bản trong thư mục step-02.

Gọi điện

Mở index.html, nhấp vào nút Start (Bắt đầu) để tải video từ webcam của bạn rồi nhấp vào Call (Gọi) để tạo kết nối ngang hàng. Bạn sẽ nhìn thấy cùng một video (từ webcam) trong cả hai thành phần video. Xem bảng điều khiển trình duyệt để xem nhật ký WebRTC.

Cách hoạt động

Bước này làm được rất nhiều việc...

WebRTC sử dụng API RTCPeerConnection để thiết lập kết nối truyền trực tuyến video giữa các ứng dụng WebRTC, còn gọi là ứng dụng ngang hàng.

Trong ví dụ này, hai đối tượng RTCPeerConnection nằm trên cùng một trang: pc1pc2. Không có nhiều mục đích sử dụng thực tế, nhưng hữu ích để minh hoạ cách hoạt động của API.

Quy trình thiết lập cuộc gọi giữa các ứng dụng ngang hàng WebRTC bao gồm 3 việc sau:

  • Tạo RTCPeerConnection cho mỗi đầu cuộc gọi và ở mỗi đầu, thêm luồng cục bộ từ getUserMedia().
  • Nhận và chia sẻ thông tin mạng: các điểm cuối kết nối tiềm năng được gọi là ICE đề xuất.
  • Nhận và chia sẻ nội dung mô tả trên thiết bị và từ xa: siêu dữ liệu về nội dung nghe nhìn trên thiết bị ở định dạng SDP.

Hãy tưởng tượng rằng Alice và Bob muốn sử dụng RTCPeerConnection để thiết lập cuộc trò chuyện video.

Đầu tiên, Alice và Bob trao đổi thông tin mạng. Cụm từ 'tìm ứng viên' đề cập đến quy trình tìm giao diện mạng và cổng bằng khung ICE.

  1. Alice tạo một đối tượng RTCPeerConnection bằng trình xử lý onicecandidate (addEventListener('icecandidate')). Mã này tương ứng với mã sau từ main.js:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  1. Alice gọi getUserMedia() và thêm luồng được truyền vào đó:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
  then(gotLocalMediaStream).
  catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
  localVideo.srcObject = mediaStream;
  localStream = mediaStream;
  trace('Received local stream.');
  callButton.disabled = false;  // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
  1. Trình xử lý onicecandidate từ bước 1. được gọi khi các đề xuất mạng có sẵn.
  2. Alice gửi dữ liệu ứng viên đã chuyển đổi tuần tự cho Bob. Trong ứng dụng thực tế, quá trình này (còn gọi là báo hiệu) diễn ra thông qua một dịch vụ nhắn tin – bạn sẽ tìm hiểu cách thực hiện việc đó ở bước sau. Tất nhiên, trong bước này, hai đối tượng RTCPeerConnection trên cùng một trang và có thể giao tiếp trực tiếp mà không cần thông báo bên ngoài.
  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:
function handleConnection(event) {
  const peerConnection = event.target;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer.addIceCandidate(newIceCandidate)
      .then(() => {
        handleConnectionSuccess(peerConnection);
      }).catch((error) => {
        handleConnectionFailure(peerConnection, error);
      });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
          `${event.candidate.candidate}.`);
  }
}

Các ứng dụng ngang hàng WebRTC cũng cần tìm hiểu 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 các blob siêu dữ liệu (còn gọi là ưu đãicâu trả lời) thông qua định dạng Giao thức mô tả phiên, còn gọi là SDP:

  1. Alice chạy phương thức RTCPeerConnection createOffer(). Lời hứa được trả về cung cấp RTCSessionDescription: Nội dung mô tả về phiên cục bộ của Alice:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. Nếu thành công, Alice đặt nội dung mô tả cục bộ bằng setLocalDescription() rồi gửi nội dung mô tả phiên này cho Bob thông qua kênh tín hiệu của họ.
  2. 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().
  3. Bob chạy phương thức RTCPeerConnection createAnswer(), truyền vào phương thức này mô tả từ xa mà anh nhận được từ Alice, vì vậy, một phiên cục bộ có thể được tạo tương thích với phương thức của cô. Lời hứa createAnswer() truyền trên RTCSessionDescription: Bob đặt thông tin đó làm nội dung mô tả cục bộ và gửi cho Alice.
  4. 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().
// Logs offer creation and sets peer connection session descriptions.
function createdOffer(description) {
  trace(`Offer from localPeerConnection:\n${description.sdp}`);

  trace('localPeerConnection setLocalDescription start.');
  localPeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection setRemoteDescription start.');
  remotePeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('remotePeerConnection createAnswer start.');
  remotePeerConnection.createAnswer()
    .then(createdAnswer)
    .catch(setSessionDescriptionError);
}

// Logs answer to offer creation and sets peer connection session descriptions.
function createdAnswer(description) {
  trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

  trace('remotePeerConnection setLocalDescription start.');
  remotePeerConnection.setLocalDescription(description)
    .then(() => {
      setLocalDescriptionSuccess(remotePeerConnection);
    }).catch(setSessionDescriptionError);

  trace('localPeerConnection setRemoteDescription start.');
  localPeerConnection.setRemoteDescription(description)
    .then(() => {
      setRemoteDescriptionSuccess(localPeerConnection);
    }).catch(setSessionDescriptionError);
}
  1. Ping!

Điểm thưởng

  1. Hãy tham khảo chrome://webrtc-internals. Dịch vụ này cung cấp số liệu thống kê và dữ liệu gỡ lỗi của WebRTC. (Danh sách đầy đủ các URL của Chrome có tại chrome://about.)
  2. Tạo kiểu cho trang bằng CSS:
  • Đặt các video cạnh nhau.
  • Làm cho các nút có cùng chiều rộng với chữ lớn hơn.
  • Hãy đảm bảo bố cục này phù hợp trên thiết bị di động.
  1. Từ bảng điều khiển Công cụ dành cho nhà phát triển Chrome, hãy xem localStream, localPeerConnectionremotePeerConnection.
  2. Trên bảng điều khiển, hãy nhìn vào localPeerConnectionpc1.localDescription. Định dạng SDP trông như thế nào?

Kiến thức bạn học được

Trong bước này, bạn đã tìm hiểu cách:

  • Loại bỏ sự khác biệt của trình duyệt với đoạn mã đệm WebRTC, adapter.js.
  • Sử dụng API RTCPeerConnection để phát trực tuyến video.
  • Điều khiển hoạt động ghi lại và phát trực tuyến nội dung nghe nhìn.
  • Chia sẻ thông tin phương tiện và mạng giữa các ứng dụng ngang hàng để bật cuộc gọi WebRTC.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-2.

Mẹo

  • Có rất nhiều điều cần tìm hiểu trong bước này! Để tìm các tài nguyên khác giải thích chi tiết hơn về RTCPeerConnection, hãy xem tại webrtc.org. Trang này bao gồm các đề xuất cho khung JavaScript — nếu bạn muốn sử dụng WebRTC nhưng không muốn tạo lại API.
  • Tìm hiểu thêm về đoạn mã đệm adapter.js từ kho lưu trữ GitHub bộ chuyển đổi.js.
  • Bạn có muốn xem ứng dụng trò chuyện video tốt nhất thế giới trông như thế nào không? Hãy xem AppRTC, ứng dụng chuẩn của dự án WebRTC cho các cuộc gọi WebRTC: ứng dụng, . Thời gian thiết lập cuộc gọi dưới 500 mili giây.

Phương pháp hay nhất

  • Để đảm bảo mã của bạn sẵn sàng cho tương lai, hãy sử dụng các API mới dựa trên Promise và bật khả năng tương thích với các trình duyệt không hỗ trợ mã bằng cách sử dụng adapter.js.

Tiếp theo

Bước này cho biết cách sử dụng WebRTC để phát trực tuyến video giữa các ứng dụng ngang hàng — nhưng lớp học lập trình này cũng giới thiệu về dữ liệu!

Trong bước tiếp theo, hãy tìm hiểu cách truyền trực tuyến dữ liệu tuỳ ý bằng RTCDataChannel.

6. Sử dụng RTCDataChannel để trao đổi dữ liệu

Kiến thức bạn sẽ học được

  • Cách trao đổi dữ liệu giữa các điểm cuối WebRTC (ứng dụng ngang hàng).

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-03.

Cập nhật HTML của bạn

Đối với bước này, bạn sẽ sử dụng các kênh dữ liệu WebRTC để gửi văn bản giữa hai phần tử textarea trên cùng một trang. Điều này không hữu ích lắm, nhưng điều này chứng minh cách WebRTC có thể được sử dụng để chia sẻ dữ liệu cũng như phát video trực tuyến.

Xoá các phần tử nút và video khỏi index.html rồi thay bằng HTML sau:

<textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>

<div id="buttons">
  <button id="startButton">Start</button>
  <button id="sendButton">Send</button>
  <button id="closeButton">Stop</button>
</div>

Một vùng văn bản sẽ dùng để nhập văn bản, vùng văn bản còn lại sẽ hiển thị văn bản dưới dạng được truyền trực tuyến giữa các ứng dụng ngang hàng.

index.html hiện sẽ có dạng như sau:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <textarea id="dataChannelSend" disabled
    placeholder="Press Start, enter some text, then press Send."></textarea>
  <textarea id="dataChannelReceive" disabled></textarea>

  <div id="buttons">
    <button id="startButton">Start</button>
    <button id="sendButton">Send</button>
    <button id="closeButton">Stop</button>
  </div>

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

Cập nhật JavaScript

Thay thế main.js bằng nội dung của step-03/js/main.js.

Thử phát trực tuyến dữ liệu giữa các thiết bị ngang hàng: mở index.html, nhấn Bắt đầu để thiết lập kết nối ngang hàng, nhập một số văn bản vào textarea ở bên trái, sau đó nhấp vào Gửi để chuyển văn bản bằng kênh dữ liệu WebRTC.

Cách hoạt động

Mã này sử dụng RTCPeerConnection và RTCDataChannel để cho phép trao đổi tin nhắn văn bản.

Phần lớn mã trong bước này giống như ví dụ RTCPeerConnection.

Hàm sendData()createConnection() có hầu hết mã mới:

function createConnection() {
  dataChannelSend.placeholder = '';
  var servers = null;
  pcConstraint = null;
  dataConstraint = null;
  trace('Using SCTP based data channels');
  // For SCTP, reliable and ordered delivery is true by default.
  // Add localConnection to global scope to make it visible
  // from the browser console.
  window.localConnection = localConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created local peer connection object localConnection');

  sendChannel = localConnection.createDataChannel('sendDataChannel',
      dataConstraint);
  trace('Created send data channel');

  localConnection.onicecandidate = iceCallback1;
  sendChannel.onopen = onSendChannelStateChange;
  sendChannel.onclose = onSendChannelStateChange;

  // Add remoteConnection to global scope to make it visible
  // from the browser console.
  window.remoteConnection = remoteConnection =
      new RTCPeerConnection(servers, pcConstraint);
  trace('Created remote peer connection object remoteConnection');

  remoteConnection.onicecandidate = iceCallback2;
  remoteConnection.ondatachannel = receiveChannelCallback;

  localConnection.createOffer().then(
    gotDescription1,
    onCreateSessionDescriptionError
  );
  startButton.disabled = true;
  closeButton.disabled = false;
}

function sendData() {
  var data = dataChannelSend.value;
  sendChannel.send(data);
  trace('Sent Data: ' + data);
}

Cú pháp của RTCDataChannel có chủ ý tương tự như WebSocket, với phương thức send() và sự kiện message.

Hãy lưu ý cách sử dụng dataConstraint. Bạn có thể định cấu hình các kênh dữ liệu để bật nhiều hình thức chia sẻ dữ liệu, ví dụ: ưu tiên phân phối đáng tin cậy hơn là hiệu suất. Bạn có thể tìm hiểu thêm thông tin về các tùy chọn tại Mạng nhà phát triển Mozilla.

Điểm thưởng

  1. Với SCTP, giao thức được các kênh dữ liệu WebRTC sử dụng, chế độ phân phối dữ liệu đáng tin cậy và có thứ tự được bật theo mặc định. Khi nào RTCDataChannel có thể cần cung cấp hoạt động phân phối dữ liệu đáng tin cậy và khi nào thì hiệu suất có thể quan trọng hơn – ngay cả khi điều đó có nghĩa là mất một số dữ liệu?
  2. Sử dụng CSS để cải thiện bố cục trang và thêm thuộc tính phần giữ chỗ vào "dataChannelReceive" vùng văn bản.
  3. Kiểm tra trang trên thiết bị di động.

Kiến thức bạn học được

Trong bước này, bạn đã tìm hiểu cách:

  • Thiết lập kết nối giữa hai thiết bị ngang hàng WebRTC.
  • Trao đổi dữ liệu văn bản giữa các ứng dụng ngang hàng.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-03.

Tìm hiểu thêm

Tiếp theo

Bạn đã tìm hiểu cách trao đổi dữ liệu giữa các máy chủ ngang hàng trên cùng một trang, nhưng làm cách nào để thực hiện việc này giữa các máy khác nhau? Trước tiên, bạn cần thiết lập một kênh báo hiệu để trao đổi thông báo siêu dữ liệu. Tìm hiểu cách thực hiện trong bước tiếp theo!

7. Thiết lập một dịch vụ tín hiệu để trao đổi tin nhắn

Kiến thức bạn sẽ học được

Trong bước này, bạn sẽ tìm hiểu cách:

  • Dùng npm để cài đặt các phần phụ thuộc của dự án như đã chỉ định trong package.json
  • Chạy máy chủ Node.js và sử dụng nút tĩnh để phân phát các tệp tĩnh.
  • Thiết lập dịch vụ thông báo trên Node.js bằng Socket.IO.
  • Sử dụng quyền đó để tạo các 'phòng' và trao đổi tin nhắn.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-04.

Khái niệm

Để thiết lập và duy trì cuộc gọi WebRTC, các ứng dụng WebRTC (ứng dụng ngang hàng) cần trao đổi siêu dữ liệu:

  • Thông tin về ứng viên (mạng).
  • Thông báo Ưu đãitrả lời cung cấp thông tin về nội dung nghe nhìn, chẳng hạn như độ phân giải và bộ mã hoá và giải mã.

Nói cách khác, cần phải trao đổi siêu dữ liệu trước khi truyền trực tuyến âm thanh, video hoặc dữ liệu ngang hàng. Quá trình này gọi là báo hiệu.

Trong các bước trước, đối tượng RTCPeerConnection của người gửi và người nhận nằm trên cùng một trang, vì vậy, hãy "báo hiệu" chỉ đơn giản là vấn đề truyền siêu dữ liệu giữa các đối tượng.

Trong ứng dụng thực tế, RTCPeerConnections cho người gửi và người nhận chạy trong các trang web trên các thiết bị khác nhau và bạn cần một cách để chúng truyền tải siêu dữ liệu.

Để làm được việc này, bạn cần sử dụng máy chủ báo hiệu: một máy chủ có thể truyền thông báo giữa các ứng dụng WebRTC (ứng dụng ngang hàng). Thông báo thực tế là văn bản thuần tuý: đối tượng JavaScript dạng chuỗi.

Điều kiện tiên quyết: Cài đặt Node.js

Để chạy các bước tiếp theo của lớp học lập trình này (thư mục từ step-04 đến step-06), bạn sẽ cần chạy một máy chủ trên localhost bằng Node.js.

Bạn có thể tải Node.js xuống và cài đặt qua đường liên kết này hoặc thông qua trình quản lý gói mà bạn ưa thích.

Sau khi cài đặt, bạn có thể nhập các phần phụ thuộc cần thiết cho các bước tiếp theo (chạy npm install) cũng như chạy một máy chủ localhost nhỏ để thực thi lớp học lập trình (chạy node index.js). Các lệnh này sẽ được chỉ định sau, khi cần thiết.

Giới thiệu về ứng dụng

WebRTC sử dụng API JavaScript phía máy khách. Tuy nhiên, để sử dụng trong thế giới thực, bạn cũng phải có máy chủ báo hiệu (nhắn tin), cũng như các máy chủ STUN và TURN. Bạn có thể tìm hiểu thêm tại đây.

Trong bước này, bạn sẽ tạo một máy chủ báo hiệu Node.js đơn giản, sử dụng mô-đun Socket.IO Node.js và thư viện JavaScript để nhắn tin. Trải nghiệm với Node.js và Socket.IO sẽ hữu ích, nhưng không quan trọng; các thành phần nhắn tin rất đơn giản.

Trong ví dụ này, máy chủ (ứng dụng Node.js) được triển khai trong index.js và ứng dụng chạy trên máy chủ đó (ứng dụng web) được triển khai trong index.html.

Ứng dụng Node.js trong bước này có 2 tác vụ.

Đầu tiên, nó hoạt động như một dịch vụ chuyển tiếp tin nhắn:

socket.on('message', function (message) {
  log('Got message: ', message);
  socket.broadcast.emit('message', message);
});

Thứ hai, họ quản lý các "phòng" trò chuyện video WebRTC:

if (numClients === 0) {
  socket.join(room);
  socket.emit('created', room, socket.id);
} else if (numClients === 1) {
  socket.join(room);
  socket.emit('joined', room, socket.id);
  io.sockets.in(room).emit('ready');
} else { // max two clients
  socket.emit('full', room);
}

Ứng dụng WebRTC đơn giản của chúng tôi sẽ cho phép tối đa hai người dùng ngang hàng dùng chung một phòng.

HTML và JavaScript

Cập nhật index.html để URL có dạng như sau:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <script src="/socket.io/socket.io.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>

Bạn sẽ không nhìn thấy nội dung nào trên trang trong bước này: toàn bộ quá trình ghi nhật ký được thực hiện vào bảng điều khiển của trình duyệt. (Để xem bảng điều khiển trong Chrome, hãy nhấn Ctrl-Shift-J hoặc Command-Option-J nếu bạn đang sử dụng máy Mac.)

Thay thế js/main.js bằng:

'use strict';

var isInitiator;

window.room = prompt("Enter room name:");

var socket = io.connect();

if (room !== "") {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room, clientId) {
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('ipaddr', function(ipaddr) {
  console.log('Message from client: Server IP address is ' + ipaddr);
});

socket.on('joined', function(room, clientId) {
  isInitiator = false;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

Thiết lập Socket.IO để chạy trên Node.js

Trong tệp HTML, bạn có thể thấy rằng bạn đang sử dụng tệp Socket.IO:

<script src="/socket.io/socket.io.js"></script>

Ở cấp cao nhất của thư mục work (công việc), hãy tạo một tệp có tên package.json với nội dung sau:

{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

Đây là tệp kê khai ứng dụng cho Trình quản lý gói nút (npm) biết cần cài đặt phần phụ thuộc nào của dự án.

Để cài đặt các phần phụ thuộc (chẳng hạn như /socket.io/socket.io.js), hãy chạy lệnh sau trên thiết bị đầu cuối của dòng lệnh, trong thư mục work của bạn:

npm install

Bạn sẽ thấy nhật ký cài đặt có dạng như sau:

3ab06b7bcc7664b9.pngs

Như bạn có thể thấy, npm đã cài đặt các phần phụ thuộc được xác định trong package.json.

Tạo tệp index.js mới ở cấp cao nhất của thư mục công việc (không phải trong thư mục js) và thêm mã sau:

'use strict';

var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');

var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
  fileServer.serve(req, res);
}).listen(8080);

var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {

  // convenience function to log server messages on the client
  function log() {
    var array = ['Message from server:'];
    array.push.apply(array, arguments);
    socket.emit('log', array);
  }

  socket.on('message', function(message) {
    log('Client said: ', message);
    // for a real app, would be room-only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', function(room) {
    log('Received request to create or join room ' + room);

    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

    log('Room ' + room + ' now has ' + numClients + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);

    } else if (numClients === 1) {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    } else { // max two clients
      socket.emit('full', room);
    }
  });

  socket.on('ipaddr', function() {
    var ifaces = os.networkInterfaces();
    for (var dev in ifaces) {
      ifaces[dev].forEach(function(details) {
        if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
          socket.emit('ipaddr', details.address);
        }
      });
    }
  });

});

Trên thiết bị đầu cuối của dòng lệnh, hãy chạy lệnh sau trong thư mục work:

node index.js

Trên trình duyệt, hãy mở localhost:8080.

Mỗi lần mở URL này, bạn sẽ được nhắc nhập tên phòng. Để tham gia cùng một phòng, hãy chọn cùng một tên phòng mỗi lần tham gia, chẳng hạn như "foo".

Mở một trang thẻ mới rồi mở lại localhost:8080. Chọn cùng một tên phòng.

Mở localhost:8080 trong thẻ hoặc cửa sổ thứ ba. Hãy chọn lại cùng tên phòng.

Kiểm tra bảng điều khiển trong mỗi tab: bạn sẽ thấy nhật ký từ JavaScript ở trên.

Điểm thưởng

  1. Có thể áp dụng cơ chế nhắn tin thay thế nào? Những vấn đề bạn có thể gặp phải khi sử dụng phương thức "thuần" WebSocket?
  2. Vấn đề nào có thể liên quan đến việc mở rộng quy mô của ứng dụng này? Bạn có thể phát triển một phương pháp để thử nghiệm hàng nghìn hoặc hàng triệu yêu cầu sử dụng phòng đồng thời không?
  3. Ứng dụng này sử dụng lời nhắc JavaScript để lấy tên phòng. Tìm cách lấy tên phòng từ URL. Ví dụ: localhost:8080/foo sẽ đặt tên phòng là foo.

Kiến thức bạn học được

Trong bước này, bạn đã tìm hiểu cách:

  • Dùng npm để cài đặt các phần phụ thuộc của dự án như đã chỉ định trong package.json
  • Chạy máy chủ Node.js để lưu trữ các tệp tĩnh trên máy chủ.
  • Thiết lập dịch vụ thông báo trên Node.js bằng cách sử dụng socket.io.
  • Sử dụng quyền đó để tạo các 'phòng' và trao đổi tin nhắn.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-04.

Tìm hiểu thêm

Tiếp theo

Tìm hiểu cách sử dụng tín hiệu để cho phép hai người dùng tạo kết nối ngang hàng.

8. Kết hợp tín hiệu và kết nối ngang hàng

Kiến thức bạn sẽ học được

Trong bước này, bạn sẽ tìm hiểu cách:

  • Chạy dịch vụ tín hiệu WebRTC bằng Socket.IO chạy trên Node.js
  • Sử dụng dịch vụ đó để trao đổi siêu dữ liệu WebRTC giữa các ứng dụng ngang hàng.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-05.

Thay thế HTML và JavaScript

Thay thế nội dung của index.html bằng:

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <div id="videos">
    <video id="localVideo" autoplay muted></video>
    <video id="remoteVideo" autoplay></video>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>
  
</body>

</html>

Thay thế js/main.js bằng nội dung của step-05/js/main.js.

Chạy máy chủ Node.js

Nếu không theo dõi lớp học lập trình này từ thư mục công việc, thì bạn có thể phải cài đặt các phần phụ thuộc cho thư mục step-05 hoặc thư mục đang làm việc. Chạy lệnh sau từ thư mục đang làm việc của bạn:

npm install

Sau khi cài đặt, nếu máy chủ Node.js không chạy, hãy khởi động bằng cách gọi lệnh sau trong thư mục work:

node index.js

Đảm bảo bạn đang sử dụng phiên bản index.js từ bước trước đó nhằm triển khai Socket.IO. Để biết thêm thông tin về Nút và Socket IO, hãy xem phần "Thiết lập dịch vụ tín hiệu để trao đổi thông báo".

Trên trình duyệt, hãy mở localhost:8080.

Mở lại localhost:8080 trong một thẻ hoặc cửa sổ mới. Một thành phần video sẽ hiển thị luồng cục bộ từ getUserMedia() và thành phần còn lại sẽ hiển thị "điều khiển từ xa" video được phát qua kết nối RTCPeer.

Xem nhật ký trong bảng điều khiển trình duyệt.

Điểm thưởng

  1. Ứng dụng này chỉ hỗ trợ trò chuyện video trực tiếp. Bạn có thể thay đổi thiết kế như thế nào để nhiều người có thể chia sẻ cùng một phòng trò chuyện video?
  2. Ví dụ này được mã hoá cứng tên phòng foo. Cách tốt nhất để hiển thị các tên phòng khác là gì?
  3. Người dùng sẽ chia sẻ tên phòng theo cách nào? Hãy thử tạo một giải pháp thay thế cho việc dùng chung tên phòng.
  4. Làm thế nào để thay đổi ứng dụng

Kiến thức bạn học được

Trong bước này, bạn đã tìm hiểu cách:

  • Chạy dịch vụ tín hiệu WebRTC bằng Socket.IO chạy trên Node.js.
  • Sử dụng dịch vụ đó để trao đổi siêu dữ liệu WebRTC giữa các ứng dụng ngang hàng.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-05.

Mẹo

  • Bạn có thể xem số liệu thống kê và dữ liệu gỡ lỗi WebRTC trên chrome://webrtc-internals.
  • Bạn có thể sử dụng test.webrtc.org để kiểm tra môi trường địa phương cũng như kiểm tra camera và micrô của mình.
  • Nếu bạn gặp khó khăn khi lưu vào bộ nhớ đệm, hãy thử những cách sau:
  • Làm mới hoàn toàn bằng cách nhấn giữ ctrl rồi nhấp vào nút Tải lại
  • Khởi động lại trình duyệt
  • Chạy npm cache clean từ dòng lệnh.

Tiếp theo

Tìm hiểu cách chụp ảnh, tải dữ liệu hình ảnh và chia sẻ dữ liệu đó giữa những người đồng nghiệp từ xa.

9. Chụp và chia sẻ ảnh qua kênh dữ liệu

Kiến thức bạn sẽ học được

Trong bước này, bạn sẽ tìm hiểu cách:

  • Chụp ảnh và lấy dữ liệu từ ảnh bằng thành phần canvas.
  • Trao đổi dữ liệu hình ảnh với người dùng từ xa.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-06.

Cách hoạt động

Trước đây, bạn đã tìm hiểu cách trao đổi tin nhắn văn bản bằng RTCDataChannel.

Bước này giúp bạn có thể chia sẻ toàn bộ các tệp: trong ví dụ này là ảnh được chụp qua getUserMedia().

Sau đây là các phần cốt lõi của bước này:

  1. Thiết lập kênh dữ liệu. Xin lưu ý rằng ở bước này, bạn không thêm luồng nội dung nghe nhìn nào vào kết nối ngang hàng.
  2. Ghi lại luồng video webcam của người dùng bằng getUserMedia():
var video = document.getElementById('video');

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}
  1. Khi người dùng nhấp vào nút Chụp, hãy lấy ảnh chụp nhanh (khung hình video) từ luồng video và hiển thị ảnh đó trong phần tử canvas:
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}
  1. Khi người dùng nhấp vào nút Send (Gửi), hãy chuyển đổi hình ảnh thành byte và gửi qua một kênh dữ liệu:
function sendPhoto() {
  // Split data channel message in chunks of this byte length.
  var CHUNK_LEN = 64000;
  var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
    len = img.data.byteLength,
    n = len / CHUNK_LEN | 0;

  console.log('Sending a total of ' + len + ' byte(s)');
  dataChannel.send(len);

  // split the photo and send in chunks of about 64KB
  for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN,
      end = (i + 1) * CHUNK_LEN;
    console.log(start + ' - ' + (end - 1));
    dataChannel.send(img.data.subarray(start, end));
  }

  // send the reminder, if any
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}
  1. Phía nhận chuyển đổi các byte thông báo của kênh dữ liệu trở lại một hình ảnh và hiển thị hình ảnh đó cho người dùng:
function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      console.log('Done. Rendering photo.');
      renderPhoto(buf);
    }
  };
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

Lấy mã

Thay thế nội dung của thư mục work bằng nội dung của step-06. Bây giờ, tệp index.html của bạn trong công việc sẽ có dạng như sau**:**

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>

  <h2>
    <span>Room URL: </span><span id="url">...</span>
  </h2>

  <div id="videoCanvas">
    <video id="camera" autoplay></video>
    <canvas id="photo"></canvas>
  </div>

  <div id="buttons">
    <button id="snap">Snap</button><span> then </span><button id="send">Send</button>
    <span> or </span>
    <button id="snapAndSend">Snap &amp; Send</button>
  </div>

  <div id="incoming">
    <h2>Incoming photos</h2>
    <div id="trail"></div>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

Nếu không theo dõi lớp học lập trình này từ thư mục công việc, thì bạn có thể phải cài đặt các phần phụ thuộc cho thư mục step-06 hoặc thư mục đang làm việc. Chỉ cần chạy lệnh sau từ thư mục đang làm việc của bạn:

npm install

Sau khi cài đặt, nếu máy chủ Node.js không chạy, hãy khởi động bằng cách gọi lệnh sau từ thư mục work (công việc):

node index.js

Đảm bảo bạn đang sử dụng phiên bản index.js triển khai Socket.IO và nhớ khởi động lại máy chủ Node.js nếu thực hiện thay đổi. Để biết thêm thông tin về Nút và Socket IO, hãy xem phần "Thiết lập dịch vụ tín hiệu để trao đổi thông báo".

Nếu cần, hãy nhấp vào nút Allow (Cho phép) để cho phép ứng dụng dùng webcam của bạn.

Ứng dụng sẽ tạo một mã phòng ngẫu nhiên và thêm mã đó vào URL. Mở URL từ thanh địa chỉ trong thẻ hoặc cửa sổ trình duyệt mới.

Nhấp vào nút Chụp & Gửi rồi xem phần Cuộc gọi đến trong thẻ khác ở cuối trang. Ứng dụng này chuyển ảnh giữa các thẻ.

Bạn sẽ thấy như sau:

911b40f36ba6ba8.png.

Điểm thưởng

  1. Làm cách nào để thay đổi mã để có thể chia sẻ bất kỳ loại tệp nào?

Tìm hiểu thêm

Kiến thức bạn học được

  • Cách chụp ảnh và lấy dữ liệu từ ảnh bằng thành phần canvas.
  • Cách trao đổi dữ liệu đó với người dùng từ xa.

Phiên bản hoàn chỉnh của bước này nằm trong thư mục step-06.

10. Xin chúc mừng

Bạn đã xây dựng một ứng dụng để phát trực tuyến video theo thời gian thực và trao đổi dữ liệu!

Kiến thức bạn học được

Trong lớp học lập trình này, bạn đã tìm hiểu cách:

  • Tải video từ webcam của bạn.
  • Phát video bằng RTCPeerConnection.
  • Truyền dữ liệu bằng RTCDataChannel.
  • Thiết lập một dịch vụ tín hiệu để trao đổi tin nhắn.
  • Kết hợp tín hiệu và kết nối ngang hàng.
  • Chụp và chia sẻ ảnh qua kênh dữ liệu.

Các bước tiếp theo

Tìm hiểu thêm

  • Một loạt tài nguyên để bắt đầu với WebRTC có sẵn từ webrtc.org.