Aktifkan komunikasi real-time dengan WebRTC

1. Sebelum memulai

Codelab ini mengajarkan cara membuat aplikasi untuk mendapatkan video dan mengambil snapshot dengan webcam, serta membagikannya secara peer-to-peer dengan WebRTC. Anda juga akan mempelajari cara menggunakan WebRTC API inti dan menyiapkan server pesan dengan Node.js.

Prasyarat

  • Pengetahuan dasar tentang HTML, CSS, dan JavaScript

Yang akan Anda build

  • Dapatkan video dari webcam Anda.
  • Streaming video dengan RTCPeerConnection.
  • Streaming data dengan RTCDataChannel.
  • Siapkan layanan sinyal untuk bertukar pesan.
  • Menggabungkan sinyal dan koneksi pembanding.
  • Mengambil foto dan menggunakan saluran data untuk membagikannya.

Yang Anda butuhkan

  • Chrome 47 atau yang lebih baru
  • Server Web untuk Chrome atau server web pilihan Anda
  • Editor teks pilihan Anda
  • Node.js

2. Mendapatkan kode sampel

Mendownload kode

  1. Jika Anda sudah terbiasa dengan Git, jalankan perintah ini untuk meng-clone kode untuk codelab ini dari GitHub:
git clone https://github.com/googlecodelabs/webrtc-web

Atau, klik link ini untuk mendownload file ZIP kode:

  1. Buka file zip yang didownload untuk mengekstrak folder project bernama webrtc-web-master, yang berisi satu folder untuk setiap langkah codelab ini dan semua resource yang Anda butuhkan.

Semua kode Anda berfungsi di direktori bernama work.

Folder step-nn berisi versi yang sudah selesai untuk setiap langkah codelab ini. Referensi tersebut tersedia untuk referensi.

Instal dan verifikasi server web

Meskipun Anda bebas menggunakan server web sendiri, codelab ini dirancang untuk berfungsi dengan baik dengan Server Web untuk Chrome.

  1. Jika Anda tidak memiliki Server Web untuk Chrome, klik link ini untuk menginstalnya dari Chrome Web Store:

d0a4649b4920cf3.png

  1. Klik Tambahkan ke Chrome, yang akan menginstal Server Web untuk Chrome dan membuka aplikasi Google secara otomatis di tab baru.
  2. Klik Server Web:

27fce4494f641883.png

Dialog akan muncul, yang memungkinkan Anda mengonfigurasi server web lokal:

a300381a486b9e22.png

  1. Klik Pilih Folder.
  2. Pilih folder work yang telah Anda buat.

Pada URL Server Web, Anda akan melihat URL tempat Anda dapat melihat pekerjaan yang sedang berlangsung di

Chrome.

  1. Di bagian Opsi (mungkin perlu dimulai ulang), centang kotak Tampilkan index.html secara otomatis.
  2. Alihkan Server Web: Dimulai dua kali untuk menghentikan dan memulai ulang server.

f23cafb3993dfac1.png

  1. Klik URL pada URL Server Web untuk melihat pekerjaan Anda di browser web.

Anda akan melihat halaman yang terlihat seperti ini, yang sesuai dengan work/index.html:

18a705cb6ccc5181.png

Jelas, aplikasi ini belum melakukan sesuatu yang menarik. Hanya kerangka minimal untuk memastikan bahwa server web Anda bekerja dengan baik. Anda akan menambahkan fungsi dan fitur tata letak pada langkah berikutnya.

3. Streaming video dari webcam

Versi lengkap langkah ini ada di folder step-01.

Tambahkan tanda hubung HTML

Salin kode ini dan tempelkan ke file index.html di direktori work Anda untuk menambahkan elemen video dan script:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

  <video autoplay playsinline></video>

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

</body>

</html>

Menambahkan sedikit JavaScript

Salin kode ini dan tempelkan ke file main.js di folder js Anda:

'use strict';

// In this codelab, you  only stream video, not audio (video: true).
const mediaStreamConstraints = {
  video: true,
};

// The video element where the stream is displayed
const localVideo = document.querySelector('video');

// The local stream that's displayed on the video
let localStream;

// Handle success and add the MediaStream to the video element
function gotLocalMediaStream(mediaStream) {
  localStream = mediaStream;
  localVideo.srcObject = mediaStream;
}

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

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

Coba

Buka file index.html di browser dan Anda akan melihat sesuatu seperti ini, tetapi dengan tampilan dari webcam Anda, tentu saja:

9297048e43ed0f3d.png

Cara kerjanya

Setelah panggilan getUserMedia(), browser meminta izin untuk mengakses kamera Anda jika permintaan tersebut merupakan permintaan pertama untuk akses kamera bagi asal saat ini.

Jika berhasil, MediaStream akan ditampilkan, yang dapat digunakan oleh elemen media melalui atribut srcObject:

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


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

Argumen constraints memungkinkan Anda menentukan media yang akan didapatkan. Dalam contoh ini, media hanya menampilkan video karena audio dinonaktifkan secara default:

const mediaStreamConstraints = {
  video: true,
};

Anda dapat menggunakan batasan untuk persyaratan tambahan, seperti resolusi video:

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

Spesifikasi MediaTrackConstraints mencantumkan semua jenis batasan potensial, meskipun tidak semua opsi didukung oleh semua browser. Jika resolusi yang diminta tidak didukung oleh kamera yang sedang dipilih, getUserMedia() akan ditolak dengan OverconstrainedError dan Anda akan diminta untuk memberikan izin agar dapat mengakses kamera.

Jika getUserMedia() berhasil, streaming video dari webcam akan ditetapkan sebagai sumber elemen video:

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

Cetak poin bonus

  • Objek localStream yang diteruskan ke getUserMedia() berada dalam cakupan global, sehingga Anda dapat memeriksanya dari konsol browser. Buka konsol, ketik stream,, lalu tekan Enter (Return di Mac). Untuk melihat konsol di Chrome, tekan Control+Shift+J (atau Command+Option+J di Mac).
  • Apa yang ditampilkan oleh localStream.getVideoTracks()?
  • Panggil localStream.getVideoTracks()[0].stop().
  • Lihat objek batasan. Apa yang terjadi jika Anda mengubahnya menjadi {audio: true, video: true}?
  • Berapa ukuran elemen video? Bagaimana cara mendapatkan ukuran alami video dari JavaScript, bukan ukuran layar? Gunakan Alat Developer Google Chrome untuk memeriksanya.
  • Tambahkan filter CSS ke elemen video, seperti ini:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • Tambahkan filter SVG, seperti ini:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

Tips

Praktik terbaik

Pastikan elemen video Anda tidak meluap dari penampungnya. Codelab ini menambahkan width dan max-width untuk menyetel ukuran yang diinginkan dan ukuran maksimum video. Browser Anda akan menghitung tinggi secara otomatis.

video {
  max-width: 100%;
  width: 320px;
}

4. Streaming video dengan RTCPeerConnection API

Versi lengkap langkah ini ada di folder step-2.

Menambahkan elemen video dan tombol kontrol

Dalam file index.html, ganti elemen video tunggal dengan dua elemen video dan tiga button:

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

Satu elemen video menampilkan streaming dari getUserMedia() dan elemen lainnya menampilkan video yang sama yang di-streaming melalui RTCPeerconnection. (Dalam aplikasi dunia nyata, satu elemen video akan menampilkan aliran data lokal dan yang lainnya akan menampilkan aliran jarak jauh.)

Menambahkan shim adapter.js

Salin elemen skrip ini dan tempel di atas elemen skrip untuk main.js:

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

File index.html Anda seharusnya terlihat seperti ini sekarang:

<!DOCTYPE html>
<html>

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

<body>
  <h1>Real-time 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>

Instal kode RTCPeerConnection

Ganti main.js dengan versi di folder step-02.

Lakukan panggilan telepon

  1. Buka file index.html.
  2. Klik Mulai untuk mendapatkan video dari webcam.
  3. Klik Panggil untuk melakukan koneksi pembanding

Anda akan melihat video yang sama dari webcam di kedua elemen video.

  1. Lihat konsol browser untuk melihat logging WebRTC.

Cara kerjanya

Langkah ini melakukan banyak hal.

WebRTC menggunakan RTCPeerConnection API untuk menyiapkan koneksi ke streaming video antara klien WebRTC, yang dikenal sebagai pembanding.

Pada contoh ini, kedua objek RTCPeerConnection ada di halaman yang sama: pc1 dan pc2.

Penyiapan panggilan antar-peering WebRTC melibatkan tiga tugas:

  1. Buat RTCPeerConnection untuk setiap akhir panggilan dan, di setiap akhir panggilan, tambahkan streaming lokal dari getUserMedia().
  2. Mendapatkan dan membagikan informasi jaringan.

Potensi endpoint koneksi disebut sebagai kandidat ICE.

  1. Dapatkan dan bagikan deskripsi lokal dan jarak jauh.

Metadata tentang media lokal berada dalam format Protokol Deskripsi Sesi (SDP).

Bayangkan Alice dan Bob ingin menggunakan RTCPeerConnection untuk menyiapkan video chat.

Pertama, Alice dan Bob bertukar informasi jaringan. Ekspresi menemukan kandidat mengacu pada proses menemukan antarmuka jaringan dan port menggunakan framework ICE.

  1. Alice membuat objek RTCPeerConnection dengan pengendali onicecandidate (addEventListener('icecandidate')).

Ini sesuai dengan kode berikut dari main.js:

let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  1. Alice memanggil getUserMedia() dan menambahkan streaming yang diteruskan ke sana:
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. Pengendali onicecandidate dari langkah pertama dipanggil saat kandidat jaringan tersedia.
  2. Alice mengirimkan data kandidat yang diserialisasi ke Bob.

Dalam aplikasi yang sebenarnya, proses ini, yang dikenal sebagai sinyal, berlangsung melalui layanan pesan. Anda akan mempelajari cara melakukannya di langkah berikutnya. Tentu saja, dalam langkah ini, kedua objek RTCPeerConnection berada di halaman yang sama dan dapat berkomunikasi secara langsung tanpa memerlukan fitur pesan eksternal.

  1. Saat Bob mendapatkan pesan kandidat dari Alice, dia memanggil addIceCandidate() untuk menambahkan kandidat tersebut ke deskripsi pembanding jarak jauh:
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}.`);
  }
}

Rekan WebRTC juga perlu menemukan dan bertukar informasi media audio dan video lokal dan jarak jauh, seperti kemampuan resolusi dan codec. Sinyal untuk bertukar informasi konfigurasi media dilanjutkan dengan pertukaran blob metadata, yang dikenal sebagai penawaran dan jawaban, menggunakan format SDP.

  1. Alice menjalankan metode RTCPeerConnection createOffer().

Promise yang ditampilkan memberikan deskripsi sesi lokal RTCSessionDescription—Alice:

trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. Jika berhasil, Alice menyetel deskripsi lokal menggunakan setLocalDescription() lalu mengirimkan deskripsi sesi ini kepada Bob melalui saluran pemberian sinyal.
  2. Budi menetapkan deskripsi yang dikirim Alice sebagai deskripsi jarak jauh dengan setRemoteDescription().
  3. Budi menjalankan metode RTCPeerConnection createAnswer() dan meneruskan deskripsi jarak jauh yang dia dapatkan dari Alice sehingga sesi lokal dihasilkan dan kompatibel dengan milik Alice.
  4. Promise createAnswer() meneruskan RTCSessionDescription, yang ditetapkan Bob sebagai deskripsi lokal, dan dikirim ke Alice.
  5. Ketika mendapatkan deskripsi sesi Bobi, dia menetapkannya sebagai deskripsi jarak jauh dengan 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);
}

Cetak poin bonus

  1. Buka chrome://webrtc-internals.

Halaman ini menyediakan statistik WebRTC dan data debug. (Anda dapat menemukan daftar lengkap URL Chrome di chrome://about.)

  1. Gayakan halaman dengan CSS:
  2. Menempatkan video secara berdampingan.
  3. Membuat tombol memiliki lebar yang sama dengan teks yang lebih besar.
  4. Pastikan tata letak berfungsi di perangkat seluler.
  5. Dari konsol Chrome Developer Tools, lihat localStream, localPeerConnection, dan remotePeerConnection.
  6. Dari konsol, lihat localPeerConnectionpc1.localDescription.

Seperti apa format SDP?

Tips

  • Untuk informasi selengkapnya tentang shim adapter.js, lihat repositori GitHub adapter.js.
  • Lihat AppRTC dan kodenya, aplikasi kanonis project WebRTC untuk panggilan WebRTC. Waktu penyiapan panggilan kurang dari 500 md.

Praktik terbaik

Agar kode Anda siap menghadapi masa depan, gunakan API berbasis Promise yang baru dan aktifkan kompatibilitas dengan browser yang tidak mendukungnya dengan adapter.js.

5. Menggunakan saluran data untuk bertukar data

Versi lengkap langkah ini ada di folder step-03.

Perbarui HTML Anda

Untuk langkah ini, Anda menggunakan saluran data WebRTC untuk mengirim teks antara dua elemen textarea di halaman yang sama. Itu tidak terlalu berguna, namun menunjukkan bagaimana WebRTC dapat digunakan untuk berbagi data, serta melakukan streaming video.

Hapus elemen video dan button dari index.html, dan ganti dengan HTML berikut:

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

Satu textarea untuk memasukkan teks, sedangkan yang lainnya untuk menampilkan teks sebagai yang di-streaming di antara pembanding.

File index.html Anda seharusnya terlihat seperti ini sekarang:

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>

Mengupdate JavaScript

  1. Ganti main.js dengan konten step-03/js/main.js.
  1. Coba lakukan streaming data antar-rekan:
  2. Buka index.html.
  3. Klik Start untuk menyiapkan koneksi pembanding.
  4. Masukkan teks dalam textarea di sebelah kiri.
  5. Klik Kirim untuk mentransfer teks menggunakan saluran data WebRTC.

Cara kerjanya

Kode ini menggunakan RTCPeerConnection dan RTCDataChannel untuk mengaktifkan pertukaran SMS.

Sebagian besar kode dalam langkah ini sama seperti contoh RTCPeerConnection. Fungsi sendData() dan createConnection() memiliki sebagian besar kode baru:

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);
}

Sintaksis RTCDataChannel sengaja mirip dengan WebSocket dengan metode send() dan peristiwa message.

Perhatikan penggunaan dataConstraint. Saluran data dapat dikonfigurasi untuk mengaktifkan berbagai jenis berbagi data, seperti memprioritaskan pengiriman yang andal daripada performa.

Cetak poin bonus

  1. Dengan SCTP, protokol yang digunakan oleh saluran data WebRTC, pengiriman data yang andal dan diurutkan aktif secara default. Kapan RTCDataChannel perlu menyediakan pengiriman data yang andal dan kapan performa menjadi lebih penting, meskipun itu berarti kehilangan beberapa data?
  2. Gunakan CSS untuk meningkatkan tata letak halaman dan menambahkan atribut placeholder ke dataChannelReceive textarea.
  3. Uji halaman di perangkat seluler.

Cari tahu selengkapnya

6. Menyiapkan layanan sinyal untuk bertukar pesan

Anda telah mempelajari cara bertukar data antar-siswa di halaman yang sama, tetapi bagaimana cara melakukannya di antara komputer yang berbeda? Pertama, Anda perlu menyiapkan saluran pensinyalan untuk bertukar pesan metadata.

Versi lengkap langkah ini ada di folder step-04.

Tentang aplikasi

WebRTC menggunakan JavaScript API sisi klien, tetapi untuk penggunaan di dunia nyata juga memerlukan server pemberian sinyal (messaging), serta server STUN dan turn. Anda dapat mengetahui informasi selengkapnya di sini.

Pada langkah ini, Anda akan membuat server sinyal Node.js sederhana menggunakan modul Socket.IO Node.js dan library JavaScript untuk messaging.

Dalam contoh ini, server (aplikasi Node.js) diimplementasikan di index.js dan klien yang menjalankannya di aplikasi tersebut (aplikasi web) diimplementasikan di index.html.

Aplikasi Node.js pada langkah ini memiliki dua tugas.

Pertama, pesan tersebut bertindak sebagai relai pesan:

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

Kedua, ruang chat video WebRTC dikelola:

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);
}

Aplikasi WebRTC sederhana Anda mengizinkan maksimum dua pembanding untuk berbagi ruang.

HTML dan JavaScript

  1. Perbarui index.html agar terlihat seperti ini:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time communication with WebRTC</h1>

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

</html>

Anda tidak akan melihat apa pun di halaman dalam langkah ini. Semua pencatatan log dilakukan ke konsol browser. Untuk melihat konsol di Chrome, tekan Control+Shift+J (atau Command+Option+J di Mac).

  1. Ganti js/main.js dengan yang berikut:
'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);
});

Menyiapkan file Socket.IO untuk dijalankan di Node.js

Dalam file HTML, Anda mungkin telah melihat bahwa Anda menggunakan file Socket.IO:

<script src="/socket.io/socket.io.js"></script>
  1. Di tingkat teratas direktori work Anda, buat file bernama package.json dengan konten berikut:
{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

Ini adalah manifes aplikasi yang memberi tahu Node Package Manager (npm) project apa

dependensi untuk diinstal.

  1. Untuk menginstal dependensi, seperti /socket.io/socket.io.js, jalankan perintah berikut dari terminal command line di direktori work Anda:
npm install

Anda akan melihat log penginstalan yang diakhiri dengan hal berikut:

3ab06b7bcc7664b9.png

Seperti yang dapat Anda lihat, npm menginstal dependensi yang ditentukan dalam package.json.

  1. Buat file baru index.js di tingkat teratas direktori work Anda (bukan direktori js), lalu tambahkan kode berikut:
'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);
        }
      });
    }
  });

});
  1. Dari terminal command line, jalankan perintah berikut di direktori work:
node index.js
  1. Dari browser, buka http://localhost:8080.

Setiap kali membuka URL ini, Anda akan diminta untuk memasukkan nama ruang.

Untuk bergabung ke ruang yang sama, masukkan nama ruang yang sama setiap kali, seperti foo.

  1. Buka tab baru untuk membuka http://localhost:8080 lagi, lalu masukkan nama ruangan yang sama lagi.
  2. Buka tab baru lainnya, buka http://localhost:8080 lagi, lalu masukkan nama ruangan yang sama lagi.
  3. Periksa konsol di setiap tab.

Anda akan melihat logging dari JavaScript.

Cetak poin bonus

  • Mekanisme pesan alternatif apa yang mungkin dimungkinkan? Masalah apa yang mungkin Anda temui menggunakan WebSocket murni?
  • Masalah apa yang mungkin terlibat dalam penskalaan aplikasi ini? Dapatkah Anda mengembangkan metode untuk menguji ribuan atau jutaan permintaan ruang secara serentak?
  • Aplikasi ini menggunakan perintah JavaScript untuk mendapatkan nama ruang. Cari tahu cara mendapatkan nama kamar dari URL. Misalnya, http://localhost:8080/foo akan memberikan nama ruang foo.

Cari tahu selengkapnya

7. Menggabungkan sinyal dan koneksi pembanding

Versi lengkap langkah ini ada di folder step-05.

Mengganti HTML dan JavaScript

  1. Ganti konten index.html dengan konten berikut:
<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>
  1. Ganti js/main.js dengan konten step-05/js/main.js.

Menjalankan server Node.js

Jika tidak mengikuti codelab ini dari direktori work, Anda mungkin perlu menginstal dependensi untuk folder step-05 atau folder kerja saat ini.

  1. Jalankan perintah berikut dari direktori kerja Anda:
npm install
  1. Setelah diinstal, jika server Node.js Anda tidak berjalan, mulailah dengan menjalankan perintah berikut di direktori work:
node index.js

Pastikan Anda menggunakan versi index.js dari langkah sebelumnya yang mengimplementasikan Socket.IO. Untuk informasi selengkapnya tentang Node dan IO Socket, tinjau bagian Menyiapkan layanan sinyal untuk bertukar pesan.

  1. Dari browser, buka http://localhost:8080.
  2. Buka tab baru dan pilih http://localhost:8080 lagi.

Satu elemen video menampilkan streaming lokal dari getUserMedia() dan elemen lainnya menampilkan video jarak jauh yang di-streaming melalui RTCPeerconnection.

  1. Lihat logging di konsol browser.

Skor b****onus poin

  • Aplikasi ini hanya mendukung chat video one-to-one. Bagaimana Anda mengubah desain untuk memungkinkan lebih dari satu orang berbagi ruang chat video yang sama?
  • Contoh ini memiliki nama ruangan foo yang di-hardcode. Apa cara terbaik untuk mengaktifkan nama ruangan lainnya?
  • Bagaimana pengguna akan membagikan nama ruang? Coba buat alternatif untuk berbagi nama ruang.
  • Bagaimana cara mengubah aplikasi?

Tips

  • Temukan statistik WebRTC dan debug data di chrome://webrtc-internals.
  • Gunakan Pemecah Masalah WebRTC untuk memeriksa lingkungan lokal Anda, serta menguji kamera dan mikrofon Anda.
  • Jika Anda mengalami masalah aneh dalam penyimpanan ke cache, coba langkah berikut:
  1. Tekan Control lalu klik Muat ulang halaman ini.
  2. Mulai ulang browser.
  3. Jalankan npm cache clean dari command line.

8 Mengambil foto dan membagikannya melalui saluran data

Versi lengkap langkah ini ada di folder step-06.

Cara kerjanya

Sebelumnya, Anda telah mempelajari cara bertukar SMS menggunakan RTCDataChannel. Langkah ini memungkinkan untuk berbagi seluruh file. Dalam contoh ini, foto diambil dengan getUserMedia().

Bagian inti dari langkah ini adalah sebagai berikut:

  1. Membuat saluran data.

Anda tidak menambahkan streaming media apa pun ke koneksi pembanding pada langkah ini.

  1. Rekam streaming video webcam Anda dengan 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. Klik Snap untuk mendapatkan snapshot (frame video) dari streaming video dan menampilkannya di elemen 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. Klik Kirim untuk mengonversi gambar ke byte dan mengirimkannya melalui saluran data:
function sendPhoto() {
  // Split the 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 approximately 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 applicable.
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}

Sisi penerima mengonversi byte pesan saluran data menjadi gambar dan menampilkan gambar kepada pengguna:

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');
  // The trail is the element that holds 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);
}

Mendapatkan kode

  1. Ganti konten folder work dengan konten step-06.

File index.html Anda di work akan terlihat seperti ini sekarang**:**

<!DOCTYPE html>
<html>

<head>

  <title>Real-time communication with WebRTC</title>

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

</head>

<body>

  <h1>Real-time 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>
  1. Jika tidak mengikuti codelab ini dari direktori work, Anda mungkin perlu menginstal dependensi untuk folder step-06 atau folder kerja saat ini. Cukup jalankan perintah berikut dari direktori kerja Anda:
npm install
  1. Setelah diinstal, jika server Node.js Anda tidak berjalan, mulailah dengan menjalankan perintah berikut dari direktori work Anda:
node index.js
    Make sure that you're using the version of `index.js` that implements Socket.IO and 

jangan lupa untuk memulai ulang server Node.js jika Anda membuat perubahan.

Untuk informasi selengkapnya tentang Node dan Socket.IO, tinjau bagian Menyiapkan sinyal

layanan untuk bertukar pesan.

  1. Jika perlu, klik Izinkan untuk mengizinkan aplikasi menggunakan webcam.

Aplikasi ini membuat ID kamar acak dan menambahkan ID tersebut ke URL.

  1. Buka URL dari kolom URL di tab atau jendela browser baru.
  2. Klik Snap & Send, lalu lihat Incoming photos di tab lainnya di bagian bawah halaman.

Aplikasi ini mentransfer foto antar-tab.

Anda akan melihat sesuatu seperti ini:

911b40f36ba6ba8.png

Cetak poin bonus

Bagaimana cara mengubah kode untuk memungkinkan berbagi jenis file apa pun?

Cari tahu selengkapnya

9. Selamat

Anda telah membuat aplikasi untuk streaming video dan pertukaran data real-time!

Pelajari lebih lanjut