WebRTC Basics — Realtime

WebRTC WebRTC (Web Real-Time Communication) memungkinkan komunikasi peer-to-peer langsung di browser: video call, audio call, screen sharing, file transfer…

WebRTC (Web Real-Time Communication) adalah API browser untuk koneksi langsung antara dua browser (peer-to-peer), untuk audio dan video, atau data apa pun lewat data channel. Semua yang lewat dienkripsi. Bayangin Bu Rini di meja kasir dan Mas Eko di mesin cuci mau saling lempar catatan langsung. Sebelum bisa, mereka perlu bertukar "alamat dan cara bicara" lewat kantor (server). Begitu masing-masing tahu cara menjangkau yang lain, kantornya gak ikut campur lagi.

🧪 Tebakan cepat: Tablet Bu Rini dan tablet Mas Eko sudah tersambung lewat data channel WebRTC, dengan server Socket.IO laundry sebagai perantara awalnya. Lalu koneksi Socket.IO Bu Rini diputus. Sampaikah pesan berikutnya dari Bu Rini ke tablet Mas Eko? Jawabannya ada di bagian "Signaling lewat Socket.IO yang sudah ada".

Dua peer dan satu data channel

Setiap ujung punya satu RTCPeerConnection. Yang memulai (Bu Rini) membuat data channel, membuat offer, dan yang menerima (Mas Eko) membalas answer. Offer dan answer berbentuk SDP (Session Description Protocol): teks yang menjelaskan apa yang mau dikirim dan kunci enkripsinya.

const rini = new RTCPeerConnection()
const eko = new RTCPeerConnection()
// di halaman yang sama, "kirim ke peer lain" cukup panggil fungsinya langsung
rini.onicecandidate = (e) => e.candidate && eko.addIceCandidate(e.candidate)
eko.onicecandidate = (e) => e.candidate && rini.addIceCandidate(e.candidate)

const channel = rini.createDataChannel('papan')
channel.onopen = () => channel.send(JSON.stringify({ id: 12, status: 'Siap diambil' }))
eko.ondatachannel = (e) => { e.channel.onmessage = (m) => console.log('eko dapat', m.data) }

rini.createOffer()
  .then((offer) => rini.setLocalDescription(offer).then(() => eko.setRemoteDescription(offer)))
  .then(() => eko.createAnswer())
  .then((answer) => eko.setLocalDescription(answer).then(() => rini.setRemoteDescription(answer)))

Dijalankan di Chromium 147 (dan juga di Firefox 146 serta WebKit 26.4):

   24 ms offer type offer sdp bytes 458
   29 ms rini candidate candidate:4100380318 1 udp 2113937151 54fed8ec-...-7dc9ef9b0b62.local 63524 typ host ...
   80 ms rini connectionState connected
   81 ms rini channel open, readyState open maxMessageSize 262144
   82 ms eko got {"id":12,"status":"Siap diambil"}

ICE, NAT, STUN, dan TURN

ICE candidate adalah satu alamat yang mungkin bisa dipakai peer lain untuk menjangkaumu (IP, port, protokol). ICE mencoba pasangan-pasangan kandidat sampai ada yang tembus.

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.example.com:3478' },
    { urls: 'turn:turn.example.com:3478', username: 'laundry', credential: 'rahasia-sementara' },
  ],
})

Signaling lewat Socket.IO yang sudah ada

WebRTC sengaja gak menentukan cara mengantar offer, answer, dan kandidat. Itu disebut signaling, dan kamu yang membuatnya. Server Socket.IO laundry sudah ada, jadi cukup diteruskan:

// server: cuma meneruskan, gak membaca isinya
io.on('connection', (socket) => {
  socket.join('rtc:papan')
  socket.to('rtc:papan').emit('peer-siap')
  socket.on('signal', (data) => socket.to('rtc:papan').emit('signal', data))
})

// kedua tablet
pc.onicecandidate = (e) => { if (e.candidate) socket.emit('signal', { candidate: e.candidate }) }
socket.on('signal', async (d) => {
  if (d.sdp) {
    await pc.setRemoteDescription(d.sdp)
    if (d.sdp.type === 'offer') {
      await pc.setLocalDescription(await pc.createAnswer())
      socket.emit('signal', { sdp: pc.localDescription })
    }
  } else if (d.candidate) await pc.addIceCandidate(d.candidate)
})
// tablet Bu Rini, saat menerima 'peer-siap':
// await pc.setLocalDescription(await pc.createOffer()); socket.emit('signal', { sdp: pc.localDescription })

Dicoba dengan dua halaman (Chromium 147 dengan Chromium 147, juga Chromium 147 dengan Firefox 146) dan server socket.io 4.8.4: server meneruskan 6 sinyal (offer, answer, 2 + 2 kandidat), total 1.942 byte JSON. Lalu socket.disconnect() di tablet Bu Rini, dan pesan berikutnya lewat data channel tetap sampai di tablet Mas Eko. Itu jawaban tebakannya: sampai. Server cuma dibutuhkan untuk berkenalan.

Kamera dan mikrofon

navigator.mediaDevices.getUserMedia({ video: true, audio: true }) meminta izin kamera dan mikrofon, lalu track-nya dipasang dengan pc.addTrack(). Fitur ini cuma ada di secure context (HTTPS atau localhost). Dicoba di ketiga browser lewat http://192.168.x.x: navigator.mediaDevices bernilai undefined, sedangkan RTCPeerConnection tetap ada. Video call dengan pelanggan di luar cakupan pelajaran ini.

Kapan WebRTC alat yang salah

WebRTC cocok untuk yang langsung antar dua orang dan berat atau sensitif latensi: panggilan suara dan video, berbagi layar, atau mengirim file besar antar tablet di Wi-Fi yang sama tanpa membebani server.

⚠️ Jebakan yang sering ketemu

TL;DR: WebRTC menyambungkan dua browser langsung untuk media atau data channel, terenkripsi. RTCPeerConnection di tiap ujung, createDataChannel lalu offer dan answer berupa SDP, dan ICE candidate dari dua arah. Pengantar offer, answer, dan kandidat (signaling) kamu buat sendiri, misalnya lewat room Socket.IO; setelah tersambung, data gak lewat server lagi. Di jaringan lokal cukup host candidate; lewat internet butuh STUN untuk alamat publik, dan TURN (berbayar per GB) kalau jalur langsung gak tembus. Kamera dan mikrofon butuh secure context dan izin. Untuk kabar status yang harus tersimpan dan disiarkan ke banyak HP, tetap pakai server.

Yang akan kamu pelajari