Komunikasi real time dengan WebRTC

1. Pengantar

WebRTC adalah project open source untuk mengaktifkan komunikasi audio, video, dan data secara real-time di Web dan aplikasi native.

WebRTC memiliki beberapa API JavaScript — klik link untuk melihat demo.

Di mana saya dapat menggunakan WebRTC?

Di Firefox, Opera, dan di Chrome di desktop dan Android. WebRTC juga tersedia untuk aplikasi native di iOS dan Android.

Apa itu pensinyalan?

WebRTC menggunakan RTCPeerConnection untuk mengomunikasikan data streaming antar-browser, tetapi juga memerlukan mekanisme untuk mengoordinasikan komunikasi dan mengirim pesan kontrol, sebuah proses yang dikenal sebagai pensinyalan. Metode dan protokol pensinyalan tidak ditentukan oleh WebRTC. Dalam codelab ini, Anda akan menggunakan Socket.IO untuk mengirim pesan, tetapi ada banyak alternatif.

Apa itu STUN dan TURN?

WebRTC dirancang untuk bekerja secara peer-to-peer, sehingga pengguna dapat terhubung melalui rute yang paling langsung. Namun, WebRTC dibuat untuk mengatasi jaringan dunia nyata: aplikasi klien perlu melintasi gateway NAT dan firewall, dan jaringan peer-to-peer memerlukan penggantian jika koneksi langsung gagal. Sebagai bagian dari proses ini, WebRTC API menggunakan server STUN untuk mendapatkan alamat IP komputer Anda, dan server TURN untuk berfungsi sebagai server relay jika komunikasi peer-to-peer gagal. (WebRTC di dunia nyata menjelaskan secara lebih mendetail.)

Apakah WebRTC aman?

Enkripsi wajib dilakukan untuk semua komponen WebRTC, dan JavaScript API-nya hanya dapat digunakan dari origin yang aman (HTTPS atau localhost). Mekanisme pensinyalan tidak ditentukan oleh standar WebRTC, jadi Anda harus memastikan untuk menggunakan protokol yang aman.

2. Ringkasan

Buat aplikasi untuk mendapatkan video dan mengambil snapshot dengan webcam Anda, lalu bagikan secara peer-to-peer melalui WebRTC. Selama proses ini, Anda akan mempelajari cara menggunakan WebRTC API inti dan menyiapkan server pesan menggunakan Node.js.

Yang akan Anda pelajari

  • Mendapatkan video dari webcam
  • Streaming video dengan RTCPeerConnection
  • Streaming data dengan RTCDataChannel
  • Menyiapkan layanan sinyal untuk bertukar pesan
  • Menggabungkan koneksi peer dan pensinyalan
  • Mengambil foto dan membagikannya melalui saluran data

Yang Anda butuhkan

  • Chrome 47 atau yang lebih baru
  • Web Server for Chrome, atau gunakan server web pilihan Anda sendiri.
  • Kode contoh
  • Editor teks
  • Pengetahuan dasar tentang HTML, CSS, dan JavaScript

3. Mendapatkan kode contoh

Mendownload kode

Jika sudah terbiasa dengan git, Anda dapat mendownload kode untuk codelab ini dari GitHub dengan meng-clone-nya:

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

Atau, klik tombol berikut untuk mendownload file .zip kode:

Buka file zip yang didownload. Tindakan ini akan mengekstrak folder project (adaptive-web-media) yang berisi satu folder untuk setiap langkah codelab ini, beserta semua materi yang akan Anda butuhkan.

Anda akan melakukan semua pekerjaan coding di direktori bernama work.

Folder step-nn berisi versi akhir untuk setiap langkah codelab ini. Folder tersebut disediakan sebagai referensi

Menginstal dan memverifikasi server web

Meskipun Anda bebas menggunakan server web Anda sendiri, codelab ini dirancang agar berfungsi dengan baik dengan Server Web Chrome. Jika aplikasi tersebut belum diinstal, Anda dapat menginstalnya dari Chrome Web Store.

6ddeb4aee53c0f0e.png

Setelah menginstal aplikasi Web Server for Chrome, klik pintasan Aplikasi Chrome dari panel bookmark, halaman Tab Baru, atau dari Peluncur Aplikasi:

1d2b4aa977ab7e24.png

Klik ikon Server Web:

27fce4494f641883.png

Selanjutnya, Anda akan melihat dialog ini, yang memungkinkan Anda mengonfigurasi server web lokal:

Screen Shot 2016-02-18 at 11.48.14 AM.png

Klik tombol PILIH FOLDER, lalu pilih folder work yang baru saja Anda buat. Tindakan ini akan memungkinkan Anda melihat pekerjaan yang sedang berlangsung di Chrome melalui URL yang ditandai dalam dialog Server Web di bagian URL Server Web.

Di bagian Opsi, centang kotak di samping Tampilkan index.html secara otomatis seperti yang ditunjukkan di bawah:

Screen Shot 2016-02-18 at 11.56.30 AM.png

Kemudian, hentikan dan mulai ulang server dengan menggeser tombol berlabel Server Web: DIMULAI ke kiri, lalu kembali ke kanan.

Screen Shot 2016-02-18 at 12.22.18 PM.png

Sekarang, buka situs kerja Anda di browser web dengan mengklik URL Server Web yang ditandai. Anda akan melihat halaman yang terlihat seperti ini, yang sesuai dengan work/index.html:

18a705cb6ccc5181.png

Jelas, aplikasi ini belum melakukan sesuatu yang menarik. Sejauh ini, aplikasi ini hanyalah kerangka minimal yang kita gunakan untuk memastikan server web Anda berfungsi dengan baik. Anda akan menambahkan fungsi dan fitur tata letak pada langkah-langkah berikutnya.

4. Streaming video dari webcam

Yang akan Anda pelajari

Di langkah ini, Anda akan mengetahui cara:

  • Mendapatkan streaming video dari webcam Anda.
  • Memanipulasi pemutaran streaming.
  • Gunakan CSS dan SVG untuk memanipulasi video.

Versi lengkap langkah ini ada di folder step-01.

Sedikit HTML...

Tambahkan elemen video dan elemen script ke index.html di direktori work Anda:

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

...dan sedikit JavaScript

Tambahkan kode berikut ke main.js di folder 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);

Cobalah

Buka index.html di browser Anda dan Anda akan melihat sesuatu seperti ini (yang menampilkan tampilan dari webcam Anda, tentu saja!):

9297048e43ed0f3d.png

Cara kerjanya

Setelah panggilan getUserMedia(), browser akan meminta izin dari pengguna untuk mengakses kamera mereka (jika ini adalah pertama kalinya akses kamera diminta untuk origin 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 diambil. Dalam contoh ini, hanya 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 yang mungkin, meskipun tidak semua opsi didukung oleh semua browser. Jika resolusi yang diminta tidak didukung oleh kamera yang saat ini dipilih, getUserMedia() akan ditolak dengan OverconstrainedError dan pengguna tidak akan diminta untuk memberikan izin akses ke kamera mereka.

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

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

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 Return. (Untuk melihat konsol di Chrome, tekan Ctrl-Shift-J, atau Command-Option-J jika Anda menggunakan Mac.)
  • Apa yang ditampilkan localStream.getVideoTracks()?
  • Coba telepon localStream.getVideoTracks()[0].stop().
  • Lihat objek batasan: apa yang terjadi saat Anda mengubahnya menjadi {audio: true, video: true}?
  • Berapa ukuran elemen video? Bagaimana cara mendapatkan ukuran alami video dari JavaScript, bukan ukuran tampilan? Gunakan Chrome Dev Tools untuk memeriksa.
  • Coba tambahkan filter CSS ke elemen video. Contoh:
video {
  filter: blur(4px) invert(1) opacity(0.5);
}
  • Coba tambahkan filter SVG. Contoh:
video {
   filter: hue-rotate(180deg) saturate(200%);
 }

Yang telah Anda pelajari

Di langkah ini, Anda telah mempelajari cara:

  • Mendapatkan video dari webcam Anda.
  • Menetapkan batasan media.
  • Mengubah elemen video.

Versi lengkap langkah ini ada di folder step-01.

Tips

Praktik terbaik

  • Pastikan elemen video Anda tidak meluap dari penampungnya. Kami telah menambahkan width dan max-width untuk menetapkan ukuran pilihan dan ukuran maksimum untuk video. Browser akan menghitung tinggi secara otomatis:
video {
  max-width: 100%;
  width: 320px;
}

Berikutnya

Anda memiliki video, tetapi bagaimana cara melakukan streaming? Cari tahu di langkah berikutnya.

5. Streaming video dengan RTCPeerConnection

Yang akan Anda pelajari

Di langkah ini, Anda akan mengetahui cara:

  • Abstraksikan perbedaan browser dengan shim WebRTC, adapter.js.
  • Gunakan RTCPeerConnection API untuk melakukan streaming video.
  • Mengontrol pengambilan dan streaming media.

Versi lengkap langkah ini ada di folder step-2.

Apa itu RTCPeerConnection?

RTCPeerConnection adalah API untuk melakukan panggilan WebRTC guna melakukan streaming video dan audio, serta bertukar data.

Contoh ini menyiapkan koneksi antara dua objek RTCPeerConnection (dikenal sebagai peer) di halaman yang sama.

Tidak terlalu berguna dalam praktik, tetapi bagus untuk memahami cara kerja RTCPeerConnection.

Menambahkan elemen video dan tombol kontrol

Di index.html, ganti satu elemen video dengan dua elemen video dan tiga tombol:

<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 akan menampilkan streaming dari getUserMedia()dan elemen video lainnya akan menampilkan video yang sama yang di-streaming melalui RTCPeerconnection. (Dalam penerapan di dunia nyata, satu elemen video akan menampilkan aliran lokal dan elemen video lainnya akan menampilkan aliran jarak jauh.)

Tambahkan shim adapter.js

Tambahkan link ke adapter.js versi saat ini di atas link ke main.js:

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

Index.html sekarang akan terlihat seperti ini:

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

Menginstal kode RTCPeerConnection

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

Lakukan panggilan

Buka index.html, klik tombol Start untuk mendapatkan video dari webcam Anda, lalu klik Call untuk membuat koneksi peer. Anda akan melihat video yang sama (dari webcam Anda) di kedua elemen video. Lihat konsol browser untuk melihat logging WebRTC.

Cara kerjanya

Langkah ini melakukan banyak hal...

WebRTC menggunakan RTCPeerConnection API untuk menyiapkan koneksi guna melakukan streaming video antar-klien WebRTC, yang dikenal sebagai peer.

Dalam contoh ini, dua objek RTCPeerConnection berada di halaman yang sama: pc1 dan pc2. Tidak terlalu berguna, tetapi bagus untuk menunjukkan cara kerja API.

Menyiapkan panggilan antara peer WebRTC melibatkan tiga tugas:

  • Buat RTCPeerConnection untuk setiap akhir panggilan dan, di setiap akhir, tambahkan stream lokal dari getUserMedia().
  • Mendapatkan dan membagikan informasi jaringan: endpoint koneksi potensial dikenal sebagai kandidat ICE.
  • Mendapatkan dan membagikan deskripsi lokal dan jarak jauh: metadata tentang media lokal dalam format SDP.

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

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

  1. Alice membuat objek RTCPeerConnection dengan handler 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 aliran 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. Handler onicecandidate dari langkah 1 dipanggil saat kandidat jaringan tersedia.
  2. Alice mengirim data kandidat yang diserialisasi ke Bob. Dalam aplikasi yang sebenarnya, proses ini (dikenal sebagai pensinyalan) terjadi melalui layanan pesan – Anda akan mempelajari cara melakukannya di langkah selanjutnya. Tentu saja, pada langkah ini, dua objek RTCPeerConnection berada di halaman yang sama dan dapat berkomunikasi secara langsung tanpa perlu mengirim pesan eksternal.
  3. Saat Bob menerima pesan kandidat dari Alice, dia akan memanggil addIceCandidate(), untuk menambahkan kandidat ke deskripsi peer 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}.`);
  }
}

Peer WebRTC juga perlu menemukan dan menukar informasi media audio dan video lokal dan jarak jauh, seperti resolusi dan kemampuan codec. Pensinyalan untuk bertukar informasi konfigurasi media dilakukan dengan bertukar blob metadata, yang dikenal sebagai penawaran dan jawaban, menggunakan format Session Description Protocol, yang dikenal sebagai SDP:

  1. Alice menjalankan metode createOffer() RTCPeerConnection. Janji yang ditampilkan memberikan RTCSessionDescription: Deskripsi sesi lokal Alice:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. Jika berhasil, Alice akan menyetel deskripsi lokal menggunakan setLocalDescription(), lalu mengirimkan deskripsi sesi ini ke Bob melalui saluran sinyal mereka.
  2. Bob menetapkan deskripsi yang dikirim Alice kepadanya sebagai deskripsi jarak jauh menggunakan setRemoteDescription().
  3. Bob menjalankan metode createAnswer() RTCPeerConnection, meneruskan deskripsi jarak jauh yang dia dapatkan dari Alice, sehingga sesi lokal yang kompatibel dengan sesi Alice dapat dibuat. Janji createAnswer() meneruskan RTCSessionDescription: Bob menetapkannya sebagai deskripsi lokal dan mengirimkannya ke Alice.
  4. Saat Alice mendapatkan deskripsi sesi Bob, 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);
}
  1. Ping!

Poin bonus

  1. Lihat chrome://webrtc-internals. Hal ini memberikan data statistik dan proses debug WebRTC. (Daftar lengkap URL Chrome ada di chrome://about.)
  2. Menyesuaikan gaya halaman dengan CSS:
  • Letakkan video secara berdampingan.
  • Buat tombol dengan lebar yang sama, dengan teks yang lebih besar.
  • Pastikan tata letak berfungsi di perangkat seluler.
  1. Dari konsol Chrome Dev Tools, lihat localStream, localPeerConnection, dan remotePeerConnection.
  2. Dari konsol, lihat localPeerConnectionpc1.localDescription. Seperti apa format SDP?

Yang telah Anda pelajari

Di langkah ini, Anda telah mempelajari cara:

  • Abstraksikan perbedaan browser dengan shim WebRTC, adapter.js.
  • Gunakan RTCPeerConnection API untuk melakukan streaming video.
  • Mengontrol pengambilan dan streaming media.
  • Membagikan informasi media dan jaringan antar-peer untuk mengaktifkan panggilan WebRTC.

Versi lengkap langkah ini ada di folder step-2.

Tips

  • Ada banyak hal yang perlu dipelajari di langkah ini. Untuk menemukan referensi lain yang menjelaskan RTCPeerConnection secara lebih mendetail, lihat webrtc.org. Halaman ini menyertakan saran untuk framework JavaScript — jika Anda ingin menggunakan WebRTC, tetapi tidak ingin berurusan dengan API.
  • Cari tahu selengkapnya tentang shim adapter.js dari repo GitHub adapter.js.
  • Ingin melihat tampilan aplikasi video chat terbaik di dunia? Lihat AppRTC, aplikasi kanonis project WebRTC untuk panggilan WebRTC: app, code. Waktu penyiapan panggilan kurang dari 500 md.

Praktik terbaik

  • Untuk memastikan kode Anda siap untuk masa mendatang, gunakan API berbasis Promise yang baru dan aktifkan kompatibilitas dengan browser yang tidak mendukungnya menggunakan adapter.js.

Berikutnya

Langkah ini menunjukkan cara menggunakan WebRTC untuk melakukan streaming video antar-peer, tetapi codelab ini juga membahas data.

Pada langkah berikutnya, cari tahu cara melakukan streaming data arbitrer menggunakan RTCDataChannel.

6. Menggunakan RTCDataChannel untuk bertukar data

Yang akan Anda pelajari

  • Cara bertukar data antara endpoint (peer) WebRTC.

Versi lengkap langkah ini ada di folder step-03.

Perbarui HTML Anda

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

Hapus elemen video dan tombol 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 akan digunakan untuk memasukkan teks, dan textarea lainnya akan menampilkan teks yang di-streaming antar-peer.

index.html sekarang akan terlihat seperti ini:

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

Memperbarui JavaScript

Ganti main.js dengan konten step-03/js/main.js.

Coba streaming data antar-peer: buka index.html, tekan Start untuk menyiapkan koneksi peer, masukkan beberapa teks di textarea di sebelah kiri, lalu klik Send untuk mentransfer teks menggunakan saluran data WebRTC.

Cara kerjanya

Kode ini menggunakan RTCPeerConnection dan RTCDataChannel untuk memungkinkan pertukaran pesan teks.

Sebagian besar kode dalam langkah ini sama dengan 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 dibuat mirip dengan WebSocket, dengan metode send() dan peristiwa message.

Perhatikan penggunaan dataConstraint. Channel data dapat dikonfigurasi untuk mengaktifkan berbagai jenis berbagi data — misalnya, memprioritaskan pengiriman yang andal daripada performa. Anda dapat menemukan informasi selengkapnya tentang opsi di Mozilla Developer Network.

Poin bonus

  1. Dengan SCTP, protokol yang digunakan oleh saluran data WebRTC, pengiriman data yang andal dan berurutan diaktifkan secara default. Kapan RTCDataChannel perlu menyediakan pengiriman data yang andal, dan kapan performa mungkin lebih penting — meskipun berarti kehilangan beberapa data?
  2. Gunakan CSS untuk meningkatkan tata letak halaman, dan tambahkan atribut placeholder ke area teks "dataChannelReceive".
  3. Uji halaman di perangkat seluler.

Yang telah Anda pelajari

Di langkah ini, Anda telah mempelajari cara:

  • Buat koneksi antara dua peer WebRTC.
  • Bertukar data teks antar-peer.

Versi lengkap langkah ini ada di folder step-03.

Cari tahu lebih lanjut

Berikutnya

Anda telah mempelajari cara bertukar data antar-peer di halaman yang sama, tetapi bagaimana cara melakukannya antar-mesin yang berbeda? Pertama, Anda perlu menyiapkan saluran sinyal untuk bertukar pesan metadata. Cari tahu caranya di langkah berikutnya.

7. Menyiapkan layanan sinyal untuk bertukar pesan

Yang akan Anda pelajari

Pada langkah ini, Anda akan mengetahui cara:

  • Gunakan npm untuk menginstal dependensi project seperti yang ditentukan dalam package.json
  • Jalankan server Node.js dan gunakan node-static untuk menyajikan file statis.
  • Menyiapkan layanan pesan di Node.js menggunakan Socket.IO.
  • Gunakan fitur tersebut untuk membuat 'ruang' dan bertukar pesan.

Versi lengkap langkah ini ada di folder step-04.

Konsep

Untuk menyiapkan dan mempertahankan panggilan WebRTC, klien (peer) WebRTC perlu bertukar metadata:

  • Informasi kandidat (jaringan).
  • Pesan penawaran dan jawaban yang memberikan informasi tentang media, seperti resolusi dan codec.

Dengan kata lain, pertukaran metadata diperlukan sebelum streaming audio, video, atau data peer-to-peer dapat dilakukan. Proses ini disebut pensinyalan.

Pada langkah-langkah sebelumnya, objek RTCPeerConnection pengirim dan penerima berada di halaman yang sama, sehingga 'pensinyalan' hanyalah masalah meneruskan metadata antar-objek.

Dalam aplikasi dunia nyata, RTCPeerConnection pengirim dan penerima berjalan di halaman web pada perangkat yang berbeda, dan Anda memerlukan cara agar keduanya dapat berkomunikasi metadata.

Untuk melakukannya, Anda menggunakan server sinyal: server yang dapat meneruskan pesan antara klien WebRTC (peer). Pesan sebenarnya adalah teks biasa: objek JavaScript yang diubah menjadi string.

Prasyarat: Instal Node.js

Untuk menjalankan langkah-langkah berikutnya dalam codelab ini (folder step-04 hingga step-06), Anda harus menjalankan server di localhost menggunakan Node.js.

Anda dapat mendownload dan menginstal Node.js dari link ini atau melalui pengelola paket pilihan Anda.

Setelah diinstal, Anda dapat mengimpor dependensi yang diperlukan untuk langkah berikutnya (menjalankan npm install), serta menjalankan server localhost kecil untuk menjalankan codelab (menjalankan node index.js). Perintah ini akan ditunjukkan nanti, saat diperlukan.

Tentang aplikasi

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

Pada langkah ini, Anda akan membangun server sinyal Node.js sederhana, menggunakan modul Node.js Socket.IO dan library JavaScript untuk mengirim pesan. Pengalaman dengan Node.js dan Socket.IO akan berguna, tetapi tidak penting; komponen pesan sangat sederhana.

Dalam contoh ini, server (aplikasi Node.js) diterapkan di index.js, dan klien yang berjalan di server (aplikasi web) diterapkan di index.html.

Aplikasi Node.js pada langkah ini memiliki dua tugas.

Pertama, ia bertindak sebagai penerus pesan:

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

Kedua, aplikasi ini mengelola ‘ruang' chat 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);
}

Aplikasi WebRTC sederhana kami akan mengizinkan maksimal dua peer untuk berbagi ruang.

HTML & JavaScript

Perbarui index.html agar terlihat seperti ini:

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

Anda tidak akan melihat apa pun di halaman pada langkah ini: semua logging dilakukan ke konsol browser. (Untuk melihat konsol di Chrome, tekan Ctrl-Shift-J, atau Command-Option-J jika Anda menggunakan Mac.)

Ganti js/main.js dengan kode 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 Socket.IO untuk berjalan di Node.js

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

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

Di tingkat teratas direktori work, buat file bernama package.json dengan isi 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) dependensi project mana yang akan diinstal.

Untuk menginstal dependensi (seperti /socket.io/socket.io.js), jalankan perintah berikut dari terminal command line, di direktori work:

npm install

Anda akan melihat log penginstalan yang diakhiri dengan sesuatu seperti ini:

3ab06b7bcc7664b9.png

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

Buat file index.js baru di tingkat teratas direktori work (bukan di 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);
        }
      });
    }
  });

});

Dari terminal command line, jalankan perintah berikut di direktori work:

node index.js

Dari browser Anda, buka localhost:8080.

Setiap kali Anda membuka URL ini, Anda akan diminta untuk memasukkan nama ruang. Untuk bergabung ke ruangan yang sama, pilih nama ruangan yang sama setiap kali, seperti 'foo'.

Buka halaman tab baru, lalu buka localhost:8080 lagi. Pilih nama ruangan yang sama.

Buka localhost:8080 di tab atau jendela ketiga. Pilih nama ruangan yang sama lagi.

Periksa konsol di setiap tab: Anda akan melihat logging dari JavaScript di atas.

Poin bonus

  1. Apa mekanisme pesan alternatif yang mungkin dapat digunakan? Masalah apa yang mungkin Anda alami saat menggunakan WebSocket 'murni'?
  2. Masalah apa yang mungkin terjadi saat menskalakan aplikasi ini? Dapatkah Anda mengembangkan metode untuk menguji ribuan atau jutaan permintaan ruang serentak?
  3. Aplikasi ini menggunakan perintah JavaScript untuk mendapatkan nama ruang. Cari cara untuk mendapatkan nama ruang dari URL. Misalnya, localhost:8080/foo akan memberikan nama ruang foo.

Yang telah Anda pelajari

Di langkah ini, Anda telah mempelajari cara:

  • Gunakan npm untuk menginstal dependensi project seperti yang ditentukan dalam package.json
  • Jalankan server Node.js untuk menyajikan file statis.
  • Siapkan layanan pesan di Node.js menggunakan socket.io.
  • Gunakan fitur tersebut untuk membuat 'ruang' dan bertukar pesan.

Versi lengkap langkah ini ada di folder step-04.

Cari tahu lebih lanjut

Berikutnya

Cari tahu cara menggunakan pensinyalan untuk memungkinkan dua pengguna membuat koneksi peer.

8. Menggabungkan koneksi peer dan pensinyalan

Yang akan Anda pelajari

Di langkah ini, Anda akan mengetahui cara:

  • Menjalankan layanan sinyal WebRTC menggunakan Socket.IO yang berjalan di Node.js
  • Gunakan layanan tersebut untuk bertukar metadata WebRTC antar-peer.

Versi lengkap langkah ini ada di folder step-05.

Mengganti HTML dan JavaScript

Ganti konten index.html dengan konten berikut:

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

Ganti js/main.js dengan konten step-05/js/main.js.

Jalankan server Node.js

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

npm install

Setelah diinstal, jika server Node.js Anda tidak berjalan, mulai dengan memanggil perintah berikut di direktori work:

node index.js

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

Dari browser Anda, buka localhost:8080.

Buka localhost:8080 lagi, di tab atau jendela baru. Satu elemen video akan menampilkan streaming lokal dari getUserMedia()dan elemen video lainnya akan menampilkan video 'jarak jauh' yang di-streaming melalui RTCPeerconnection.

Melihat logging di konsol browser.

Poin bonus

  1. Aplikasi ini hanya mendukung chat video personal. Bagaimana Anda mengubah desain agar lebih dari satu orang dapat berbagi ruang video chat yang sama?
  2. Contoh memiliki nama ruangan foo yang dikodekan secara permanen. Apa cara terbaik untuk mengaktifkan nama ruangan lainnya?
  3. Bagaimana cara pengguna membagikan nama ruang? Mencoba membuat alternatif untuk berbagi nama ruang.
  4. Bagaimana Anda dapat mengubah aplikasi

Yang telah Anda pelajari

Di langkah ini, Anda telah mempelajari cara:

  • Jalankan layanan pensinyalan WebRTC menggunakan Socket.IO yang berjalan di Node.js.
  • Gunakan layanan tersebut untuk bertukar metadata WebRTC antar-peer.

Versi lengkap langkah ini ada di folder step-05.

Tips

  • Statistik dan data proses debug WebRTC tersedia dari chrome://webrtc-internals.
  • test.webrtc.org dapat digunakan untuk memeriksa lingkungan lokal Anda dan menguji kamera serta mikrofon Anda.
  • Jika Anda mengalami masalah aneh terkait penyimpanan dalam cache, coba lakukan hal berikut:
  • Lakukan muat ulang paksa dengan menahan ctrl dan mengklik tombol Muat Ulang
  • Mulai ulang browser
  • Jalankan npm cache clean dari command line.

Berikutnya

Cari tahu cara mengambil foto, mendapatkan data gambar, dan membagikannya di antara peer jarak jauh.

9. Mengambil foto dan membagikannya melalui saluran data

Yang akan Anda pelajari

Di langkah ini, Anda akan mempelajari cara:

  • Ambil foto dan dapatkan datanya menggunakan elemen kanvas.
  • Bertukar data gambar dengan pengguna jarak jauh.

Versi lengkap langkah ini ada di folder step-06.

Cara kerjanya

Sebelumnya, Anda telah mempelajari cara bertukar pesan teks menggunakan RTCDataChannel.

Langkah ini memungkinkan Anda membagikan seluruh file: dalam contoh ini, foto yang diambil melalui getUserMedia().

Bagian inti dari langkah ini adalah sebagai berikut:

  1. Buat saluran data. Perhatikan bahwa Anda tidak menambahkan aliran media apa pun ke koneksi peer dalam langkah ini.
  2. Merekam streaming video webcam pengguna 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. Saat pengguna mengklik tombol Snap, ambil snapshot (frame video) dari streaming video dan tampilkan dalam 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. Saat pengguna mengklik tombol Kirim, konversi gambar menjadi byte dan kirim melalui saluran data:
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. Sisi penerima mengonversi byte pesan saluran data kembali menjadi gambar dan menampilkan gambar tersebut 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');
  // 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);
}

Mendapatkan kode

Ganti konten folder work Anda dengan konten step-06. File index.html Anda di work sekarang akan terlihat seperti ini**:**

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

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

Setelah diinstal, jika server Node.js Anda tidak berjalan, mulai dengan memanggil perintah berikut dari direktori work Anda:

node index.js

Pastikan Anda menggunakan versi index.js yang menerapkan Socket.IO, dan jangan lupa untuk memulai ulang server Node.js jika Anda melakukan perubahan. Untuk mengetahui informasi selengkapnya tentang Node dan Socket IO, tinjau bagian "Siapkan layanan sinyal untuk bertukar pesan".

Jika perlu, klik tombol Izinkan untuk mengizinkan aplikasi menggunakan webcam Anda.

Aplikasi akan membuat ID ruang acak dan menambahkan ID tersebut ke URL. Buka URL dari kolom URL di tab atau jendela browser baru.

Klik tombol Snap & Send, lalu lihat area Masuk di tab lain di bagian bawah halaman. Aplikasi mentransfer foto antar-tab.

Anda akan melihat yang seperti ini:

911b40f36ba6ba8.png

Poin bonus

  1. Bagaimana cara mengubah kode agar memungkinkan berbagi jenis file apa pun?

Cari tahu lebih lanjut

Yang telah Anda pelajari

  • Cara mengambil foto dan mendapatkan data dari foto tersebut menggunakan elemen kanvas.
  • Cara menukar data tersebut dengan pengguna jarak jauh.

Versi lengkap langkah ini ada di folder step-06.

10. Selamat

Anda telah membuat aplikasi untuk melakukan streaming video dan pertukaran data secara real-time.

Yang telah Anda pelajari

Dalam codelab ini, Anda telah mempelajari cara:

  • Mendapatkan video dari webcam Anda.
  • Streaming video dengan RTCPeerConnection.
  • Streaming data dengan RTCDataChannel.
  • Siapkan layanan pensinyalan untuk bertukar pesan.
  • Menggabungkan koneksi dan pensinyalan peer.
  • Ambil foto dan bagikan melalui saluran data.

Langkah berikutnya

Pelajari lebih lanjut

  • Berbagai referensi untuk mulai menggunakan WebRTC tersedia di webrtc.org.