Komunikasi real time dengan WebRTC

1. Pengantar

WebRTC adalah project open source untuk memungkinkan 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 Chrome di desktop dan Android. WebRTC juga tersedia untuk aplikasi native di iOS dan Android.

Apa itu sinyal?

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

Apa itu STUN dan TURN?

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

Apakah WebRTC aman?

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

2. Ringkasan

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

Yang akan Anda pelajari

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

Yang Anda butuhkan

  • Chrome 47 atau yang lebih baru
  • Server Web untuk 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 terbiasa menggunakan git, Anda dapat mendownload kode untuk codelab ini dari GitHub dengan meng-clone kode tersebut:

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

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

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 referensi yang Anda perlukan.

Anda akan mengerjakan semua pekerjaan coding di direktori bernama work.

Folder step-nn berisi versi yang sudah selesai 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 Anda belum menginstal aplikasi tersebut, Anda dapat menginstalnya dari Chrome Web Store.

6ddeb4aee53c0f0e.pngS

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

1d2b4aa977ab7e24.pngS

Klik ikon Web Server:

27fce4494f641883.pngS

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

Screenshot 2016-02-18 pukul 11.48.14 AM.png

Klik tombol CHOOSE Folder, lalu pilih folder work yang baru saja Anda buat. Dengan begitu, Anda dapat melihat pekerjaan yang sedang berlangsung di Chrome melalui URL yang ditandai dalam dialog Server Web di bagian URL Server Web.

Di bagian Options, centang kotak di samping Otomatis tampilkan index.html seperti yang ditunjukkan di bawah:

Screenshot 2016-02-18 pukul 11.56.30 AM.png

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

Screenshot 2016-02-18 pukul 12.22.18 PM.png

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

18a705cb6ccc5181.pngS

Jelas, aplikasi ini belum melakukan hal yang menarik — sejauh ini, ini hanyalah kerangka minimal yang kami gunakan untuk memastikan server web Anda berfungsi dengan baik. Anda akan menambahkan fungsi dan fitur tata letak di langkah berikutnya.

4. Streaming video dari webcam

Yang akan Anda pelajari

Pada langkah ini, Anda akan mengetahui cara:

  • Dapatkan streaming video dari webcam.
  • Memanipulasi pemutaran streaming.
  • Menggunakan CSS dan SVG untuk memanipulasi video.

Versi lengkap langkah ini ada di folder step-01.

Tanda hubung 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 sepotong JavaScript

Tambahkan baris berikut ke main.js di folder js Anda:

'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 dan Anda akan melihat halaman seperti ini (menampilkan tampilan dari webcam!):

9297048e43ed0f3d.pngS

Cara kerjanya

Setelah panggilan getUserMedia(), browser akan meminta izin dari pengguna untuk mengakses kamera mereka (jika ini adalah pertama kalinya akses kamera diminta untuk 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, 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 potensial, 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 memberikan izin untuk mengakses kameranya.

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 jika 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 memeriksanya.
  • 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

Pada langkah ini, Anda telah mempelajari cara:

  • Dapatkan video dari webcam.
  • Tetapkan batasan media.
  • Aneh dengan elemen video.

Versi lengkap langkah ini ada di folder step-01.

Tips

Praktik terbaik

  • Pastikan elemen video Anda tidak melebihi penampungnya. Kami telah menambahkan width dan max-width guna menetapkan ukuran yang diinginkan 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

Pada langkah ini, Anda akan mengetahui cara:

  • Menghilangkan 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 untuk melakukan streaming video dan audio, serta bertukar data.

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

Tidak banyak penggunaan praktis, tetapi bagus untuk memahami cara kerja RTCPeerConnection.

Menambahkan elemen video dan tombol kontrol

Di index.html, ganti elemen video tunggal 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 lainnya akan menampilkan video yang sama yang di-streaming melalui RTCPeerconnection. (Dalam penerapan nyata, satu elemen video akan menampilkan streaming lokal dan elemen video lainnya adalah streaming jarak jauh.)

Menambahkan shim adaptor.js

Tambahkan link ke versi adapter.js saat ini di atas link ke adapter.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.

Telepon

Buka index.html, klik tombol Mulai untuk mendapatkan video dari webcam, lalu klik Call untuk membuat koneksi pembanding. Anda akan melihat video yang sama (dari webcam) 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 antara klien WebRTC, yang dikenal sebagai peer.

Dalam contoh ini, kedua objek RTCPeerConnection berada di halaman yang sama: pc1 dan pc2. Tidak banyak penggunaan praktis, tetapi bagus untuk mendemonstrasikan cara kerja API.

Ada tiga tugas untuk menyiapkan panggilan antara peer WebRTC:

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

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

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

  1. Alice membuat objek RTCPeerConnection dengan pengendali onicecandidate (addEventListener('icecandidate')). Hal ini sesuai dengan kode berikut dari main.js:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  1. Anita memanggil getUserMedia() dan menambahkan aliran data yang diteruskan ke:
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 1 akan dipanggil saat kandidat jaringan tersedia.
  2. Alice mengirimkan data kandidat yang diserialisasi kepada Bob. Di aplikasi yang sebenarnya, proses ini (dikenal sebagai sinyal) berlangsung melalui layanan pesan – Anda akan mempelajari cara melakukannya di langkah selanjutnya. Tentu saja, di langkah ini, kedua objek RTCPeerConnection berada di halaman yang sama dan dapat berkomunikasi langsung tanpa memerlukan pesan eksternal.
  3. Saat Bobi mendapatkan pesan kandidat dari Alia, dia memanggil addIceCandidate(), untuk menambahkan kandidat tersebut ke deskripsi rekan 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 mencari tahu dan bertukar informasi media audio serta video lokal dan jarak jauh, seperti kemampuan resolusi dan codec. Sinyal untuk bertukar informasi konfigurasi media dilakukan dengan mempertukarkan blob metadata, yang dikenal sebagai penawaran dan jawaban, menggunakan format Protokol Deskripsi Sesi, yang dikenal sebagai SDP:

  1. Alice menjalankan metode createOffer() RTCPeerConnection. Promise yang ditampilkan memberikan RTCSessionDescription: deskripsi sesi lokal Alice:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
  .then(createdOffer).catch(setSessionDescriptionError);
  1. Jika berhasil, Anita menetapkan deskripsi lokal menggunakan setLocalDescription(), lalu mengirimkan deskripsi sesi ini kepada Bobi melalui saluran pemberian sinyalnya.
  2. Bobi menetapkan deskripsi yang dikirim Alia sebagai deskripsi jarak jauh menggunakan setRemoteDescription().
  3. Bob menjalankan metode createAnswer() RTCPeerConnection, dengan meneruskan deskripsi jarak jauh yang ia dapatkan dari Alice, sehingga sesi lokal dapat dibuat yang kompatibel dengan miliknya. Promise createAnswer() meneruskan RTCSessionDescription: Bob menyetelnya sebagai deskripsi lokal dan mengirimkannya ke Alice.
  4. Saat Alice mendapatkan deskripsi sesi Bobi, dia menyetelnya 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. Tindakan ini memberikan statistik WebRTC dan data proses debug. (Daftar lengkap URL Chrome dapat dilihat di chrome://about.)
  2. Sesuaikan gaya halaman dengan CSS:
  • Letakkan video secara berdampingan.
  • Buat tombol-tombol memiliki 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

Pada langkah ini, Anda telah mempelajari cara:

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

Versi lengkap langkah ini ada di folder step-2.

Tips

  • Ada banyak hal yang harus dipelajari dalam 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 memproses API-nya.
  • Cari tahu lebih lanjut tentang shim adaptor.js dari repo GitHub adaptor.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

  • Agar kode Anda siap menghadapi masa depan, 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 WebRTC (peer).

Versi lengkap langkah ini ada di folder step-03.

Memperbarui HTML

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

Hapus elemen video dan tombol dari index.html lalu 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 area teks akan digunakan untuk memasukkan teks, area teks lainnya akan menampilkan teks sebagai streaming di antara 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>

Mengupdate JavaScript Anda

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

Coba data streaming antar-peer: buka index.html, tekan Start untuk menyiapkan koneksi pembanding, 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 mengaktifkan 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 mirip dengan WebSocket, dengan metode send() dan peristiwa message.

Perhatikan penggunaan dataConstraint. Saluran data dapat dikonfigurasi untuk memungkinkan 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 diurutkan diaktifkan secara default. Kapan RTCDataChannel perlu menyediakan pengiriman data yang andal, dan kapan kinerja menjadi lebih penting — bahkan jika itu berarti kehilangan beberapa data?
  2. Gunakan CSS untuk menyempurnakan tata letak halaman, dan tambahkan atribut placeholder ke "dataChannelReceive" {i>textarea<i}.
  3. Uji halaman di perangkat seluler.

Yang telah Anda pelajari

Pada langkah ini, Anda telah mempelajari cara:

  • Membuat koneksi antara dua peer WebRTC.
  • Bertukar data teks di antara pembanding.

Versi lengkap langkah ini ada di folder step-03.

Cari tahu selengkapnya

Berikutnya

Anda telah belajar cara bertukar data antara pembanding pada halaman yang sama, tetapi bagaimana Anda melakukannya di antara komputer yang berbeda? Pertama, Anda perlu menyiapkan channel pemberi sinyal untuk bertukar pesan metadata. Cari tahu caranya di langkah berikutnya.

7. Menyiapkan layanan pemberian 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 menyalurkan file statis.
  • Menyiapkan layanan pesan di Node.js menggunakan Socket.IO.
  • Gunakan kolom tersebut untuk membuat 'ruang' dan bertukar pesan.

Versi lengkap langkah ini ada di folder step-04.

Konsep

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

  • Informasi kandidat (jaringan).
  • Tawarkan dan jawab pesan 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 sinyal.

Pada langkah sebelumnya, objek RTCPeerConnection pengirim dan penerima berada di halaman yang sama, sehingga ‘sinyal’ hanya masalah meneruskan metadata antar-objek.

Dalam aplikasi dunia nyata, RTCPeerConnections pengirim dan penerima berjalan di laman web pada perangkat yang berbeda, dan Anda memerlukan cara bagi mereka untuk mengomunikasikan metadata.

Untuk itu, Anda menggunakan server pensinyalan: server yang dapat meneruskan pesan antara klien WebRTC (peer) Pesan sebenarnya adalah teks biasa: objek JavaScript dengan string.

Prasyarat: Instal Node.js

Untuk menjalankan langkah berikutnya dari codelab ini (folder step-04 ke 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, ketika diperlukan.

Tentang aplikasi

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

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

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

Aplikasi Node.js di langkah ini memiliki dua tugas.

Pertama, fungsi ini bertindak sebagai relai pesan:

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

Kedua, ia mengelola 'ruang' video chat 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 kita akan mengizinkan maksimum dua peer untuk berbagi ruang.

HTML & JavaScript

Perbarui index.html sehingga 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 telah 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 yang berikut ini:

'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 dijalankan di Node.js

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

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

Di tingkat atas 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 apa yang akan diinstal.

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 berakhir seperti ini:

3ab06b7bcc7664b9.pngS

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

Buat file baru index.js di tingkat teratas direktori kerja Anda (bukan di direktori js) dan 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, buka localhost:8080.

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

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

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

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

Poin bonus

  1. Mekanisme pesan alternatif apa yang mungkin dapat dilakukan? Masalah apa yang mungkin Anda temui saat menggunakan kata 'pure' WebSocket?
  2. Masalah apa yang mungkin terkait dengan penskalaan aplikasi ini? Dapatkah Anda mengembangkan metode untuk menguji ribuan atau jutaan permintaan ruang secara bersamaan?
  3. Aplikasi ini menggunakan perintah JavaScript untuk mendapatkan nama ruang. Cari tahu cara mendapatkan nama ruang dari URL. Misalnya localhost:8080/foo akan memberikan nama ruang foo.

Yang telah Anda pelajari

Pada langkah ini, Anda telah mempelajari cara:

  • Gunakan npm untuk menginstal dependensi project seperti yang ditentukan dalam package.json
  • Menjalankan server Node.js ke file statis server.
  • Menyiapkan layanan pesan di Node.js menggunakan socket.io.
  • Gunakan kolom tersebut untuk membuat 'ruang' dan bertukar pesan.

Versi lengkap langkah ini ada di folder step-04.

Cari tahu selengkapnya

Berikutnya

Cari tahu cara menggunakan sinyal untuk memungkinkan dua pengguna melakukan koneksi peer.

8. Menggabungkan koneksi dan pensinyalan peer

Yang akan Anda pelajari

Pada langkah ini, Anda akan mengetahui cara:

  • Menjalankan layanan pemberian sinyal WebRTC menggunakan Socket.IO yang berjalan di Node.js
  • Menggunakan layanan tersebut untuk bertukar metadata WebRTC di antara peer.

Versi lengkap langkah ini ada di folder step-05.

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

Menjalankan server Node.js

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

npm install

Setelah diinstal, jika server Node.js Anda tidak berjalan, mulailah 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 "Menyiapkan layanan pemberian sinyal untuk bertukar pesan".

Dari browser, buka localhost:8080.

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

Lihat logging di konsol browser.

Poin bonus

  1. Aplikasi ini hanya mendukung obrolan video empat mata. Bagaimana Anda dapat mengubah desain untuk memungkinkan lebih dari satu orang berbagi ruang {i>video chat<i} yang sama?
  2. Contoh tersebut memiliki nama ruang foo hard code. Apa cara terbaik untuk mengaktifkan nama ruang lainnya?
  3. Bagaimana cara pengguna membagikan nama ruang? Coba buat alternatif untuk nama ruang berbagi.
  4. Bagaimana cara Anda mengubah aplikasi

Yang telah Anda pelajari

Pada langkah ini, Anda telah mempelajari cara:

  • Menjalankan layanan pemberian sinyal WebRTC menggunakan Socket.IO yang berjalan di Node.js.
  • Menggunakan layanan tersebut untuk bertukar metadata WebRTC di antara peer.

Versi lengkap langkah ini ada di folder step-05.

Tips

  • Statistik WebRTC dan data debug tersedia dari chrome://webrtc-internals.
  • test.webrtc.org dapat digunakan untuk memeriksa lingkungan lokal serta menguji kamera dan mikrofon Anda.
  • Jika Anda mengalami masalah yang tidak biasa dengan penyimpanan dalam cache, coba langkah berikut ini:
  • Lakukan refresh 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 dengan rekan-rekan jarak jauh.

9. Mengambil foto dan membagikannya melalui saluran data

Yang akan Anda pelajari

Pada langkah ini, Anda akan mempelajari cara:

  • Mengambil foto dan mendapatkan data dari foto tersebut menggunakan elemen kanvas.
  • Bertukar data gambar dengan pengguna jarak jauh.

Versi lengkap langkah ini ada di folder step-06.

Cara kerjanya

Sebelumnya Anda belajar cara bertukar pesan teks menggunakan RTCDataChannel.

Langkah ini memungkinkan untuk 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 streaming media apa pun ke koneksi pembanding di langkah ini.
  2. Rekam 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, dapatkan 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 Send, konversikan gambar ke 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 kerja dengan konten langkah-06. File index.html di bagian kerja sekarang seharusnya 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 Anda saat ini. Cukup jalankan perintah berikut dari direktori kerja Anda:

npm install

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

node index.js

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

Jika perlu, klik tombol Allow untuk mengizinkan aplikasi menggunakan webcam.

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 Paskan & Kirim, lalu lihat area Masuk di tab lain di bagian bawah halaman. Aplikasi mentransfer foto antar-tab.

Anda akan melihat sesuatu seperti ini:

911b40f36ba6ba8.pngS

Poin bonus

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

Cari tahu selengkapnya

Yang telah Anda pelajari

  • Cara mengambil foto dan mendapatkan data dari foto menggunakan elemen kanvas.
  • Cara bertukar 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 secara realtime dan pertukaran data!

Yang telah Anda pelajari

Dalam codelab ini, Anda telah mempelajari cara:

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

Langkah berikutnya

Pelajari lebih lanjut

  • Berbagai referensi untuk memulai WebRTC tersedia di webrtc.org.