Tentang codelab ini
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
- 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:
- 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.
- Jika Anda tidak memiliki Server Web untuk Chrome, klik link ini untuk menginstalnya dari Chrome Web Store:
- Klik Tambahkan ke Chrome, yang akan menginstal Server Web untuk Chrome dan membuka aplikasi Google secara otomatis di tab baru.
- Klik Server Web:
Dialog akan muncul, yang memungkinkan Anda mengonfigurasi server web lokal:
- Klik Pilih Folder.
- 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.
- Di bagian Opsi (mungkin perlu dimulai ulang), centang kotak Tampilkan index.html secara otomatis.
- Alihkan Server Web: Dimulai dua kali untuk menghentikan dan memulai ulang server.
- 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
:
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:
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 kegetUserMedia()
berada dalam cakupan global, sehingga Anda dapat memeriksanya dari konsol browser. Buka konsol, ketikstream,
, lalu tekanEnter
(Return
di Mac). Untuk melihat konsol di Chrome, tekanControl+Shift+J
(atauCommand+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
- Jangan lupa atribut
autoplay
pada elemenvideo
. Tanpa itu, Anda hanya melihat satu frame! - Ada banyak opsi lainnya untuk batasan
getUserMedia()
. Untuk contoh lainnya, lihat Batasan & statistik dan Batasan & statistik lainnya di sampel WebRTC.
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
- Buka file
index.html
. - Klik Mulai untuk mendapatkan video dari webcam.
- Klik Panggil untuk melakukan 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 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:
- Buat
RTCPeerConnection
untuk setiap akhir panggilan dan, di setiap akhir panggilan, tambahkan streaming lokal darigetUserMedia()
. - Mendapatkan dan membagikan informasi jaringan.
Potensi endpoint koneksi disebut sebagai kandidat ICE.
- 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.
- Alice membuat objek
RTCPeerConnection
dengan pengendalionicecandidate (addEventListener('icecandidate'))
.
Ini sesuai dengan kode berikut dari main.js
:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
'iceconnectionstatechange', handleConnectionChange);
- 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.');
- Pengendali
onicecandidate
dari langkah pertama dipanggil saat kandidat jaringan tersedia. - 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.
- 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.
- 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);
- Jika berhasil, Alice menyetel deskripsi lokal menggunakan
setLocalDescription()
lalu mengirimkan deskripsi sesi ini kepada Bob melalui saluran pemberian sinyal. - Budi menetapkan deskripsi yang dikirim Alice sebagai deskripsi jarak jauh dengan
setRemoteDescription()
. - Budi menjalankan metode
RTCPeerConnection
createAnswer()
dan meneruskan deskripsi jarak jauh yang dia dapatkan dari Alice sehingga sesi lokal dihasilkan dan kompatibel dengan milik Alice. - Promise
createAnswer()
meneruskanRTCSessionDescription
, yang ditetapkan Bob sebagai deskripsi lokal, dan dikirim ke Alice. - 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
- Buka chrome://webrtc-internals.
Halaman ini menyediakan statistik WebRTC dan data debug. (Anda dapat menemukan daftar lengkap URL Chrome di chrome://about.)
- Gayakan halaman dengan CSS:
- Menempatkan video secara berdampingan.
- Membuat tombol memiliki lebar yang sama dengan teks yang lebih besar.
- Pastikan tata letak berfungsi di perangkat seluler.
- Dari konsol Chrome Developer Tools, lihat
localStream
,localPeerConnection
, danremotePeerConnection
. - 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
- Ganti
main.js
dengan kontenstep-03/js/main.js
.
- Coba lakukan streaming data antar-rekan:
- Buka
index.html
. - Klik Start untuk menyiapkan koneksi pembanding.
- Masukkan teks dalam
textarea
di sebelah kiri. - 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
- 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? - Gunakan CSS untuk meningkatkan tata letak halaman dan menambahkan atribut placeholder ke
dataChannelReceive
textarea
. - 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
- 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).
- 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>
- Di tingkat teratas direktori
work
Anda, buat file bernamapackage.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.
- Untuk menginstal dependensi, seperti
/socket.io/socket.io.js
, jalankan perintah berikut dari terminal command line di direktoriwork
Anda:
npm install
Anda akan melihat log penginstalan yang diakhiri dengan hal berikut:
Seperti yang dapat Anda lihat, npm
menginstal dependensi yang ditentukan dalam package.json
.
- Buat file baru
index.js
di tingkat teratas direktoriwork
Anda (bukan direktorijs
), 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, 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
.
- Buka tab baru untuk membuka http://localhost:8080 lagi, lalu masukkan nama ruangan yang sama lagi.
- Buka tab baru lainnya, buka http://localhost:8080 lagi, lalu masukkan nama ruangan yang sama lagi.
- 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
- 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>
- Ganti
js/main.js
dengan kontenstep-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.
- Jalankan perintah berikut dari direktori kerja Anda:
npm install
- 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.
- Dari browser, buka http://localhost:8080.
- 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
.
- 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:
- Tekan
Control
lalu klik Muat ulang halaman ini. - Mulai ulang browser.
- 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:
- Membuat saluran data.
Anda tidak menambahkan streaming media apa pun ke koneksi pembanding pada langkah ini.
- 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);
});
}
- 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);
}
- 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
- Ganti konten folder
work
dengan kontenstep-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 & 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 tidak mengikuti codelab ini dari direktori
work
, Anda mungkin perlu menginstal dependensi untuk folderstep-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, 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.
- Jika perlu, klik Izinkan untuk mengizinkan aplikasi menggunakan webcam.
Aplikasi ini membuat ID kamar acak dan menambahkan ID tersebut ke URL.
- Buka URL dari kolom URL di tab atau jendela browser baru.
- 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:
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!