cat ~/notes/janus-webrtc-sfu.md
janus buat video call 1:1: kenapa gw taro sfu di tengah
[en]janus buat video call 1:1: kenapa gw taro sfu di tengah
Video call 1:1 bisa nyambungin dua client secara langsung. Punya gw tetap lewat server.
Alasannya bukan kualitas video atau ukuran room. Sistemnya butuh recording server-side, kontrol call dari satu tempat, dan jalur media yg tetap jalan di network aneh.
janus ngurus jalur medianya. Backend gw yg mutusin satu call boleh ngapain. Janus yg mindahin dan ngerekam paket.
Pemisahan ini enak dipakai, tapi artinya gw juga harus ngurus TURN, UDP networking, recording pipeline, sama tagihan egress.
Janus itu sebenarnya apa?
Janus adalah WebRTC server general-purpose. Dia bukan produk video call lengkap dan ga pegang application state. Yg dia kasih adalah sekumpulan plugin buat pola media yg beda:
- videoroom buat audio dan video multiparty, ini yg gw pakai
- streaming buat broadcast satu sumber ke banyak penonton
- SIP, NoSIP, textroom, recordplay, dan beberapa lainnya
App pakai HTTP atau WebSocket buat signaling dan admin API terpisah buat management. Medianya jalan sebagai RTP lewat UDP.
Mental model yg kepake adalah programmable media pipe.
Backend gw mutusin siapa boleh nelepon, bikin room, dan pegang seluruh call lifecycle. Janus nerima stream, forward paket, dan nulis recording.
Kenapa dua orang perlu server di tengah?
Buat dua peserta, peer-to-peer biasa jadi baseline paling masuk akal. Jalur medianya pendek dan egress server hampir nol.
Tiga topologi umum ini bikin trade-off-nya lebih jelas:
- mesh / P2P nyambungin peserta secara langsung. Buat 1:1 ini efisien, tapi ga ada satu jalur media buat recording server-side atau enforce call dari server.
- MCU decode semua stream, mix hasilnya, encode ulang, lalu kirim balik. Bandwidth client kecil, tapi ongkos CPU server gede. Buat dua orang ini overkill.
- SFU nerima tiap stream lalu forward tanpa decode atau encode ulang. Ini model yg dipakai plugin videoroom punya janus.
Gw pilih SFU karena server memang harus tetap ada di jalur media. Recording sama kontrol call bikin ini jadi system requirement, bukan optimasi scaling.
Dua plane dalam satu call
Satu call punya signaling plane dan media plane yg terpisah:
Signaling plane bawa control traffic. Client connect ke backend gw lewat HTTPS atau WSS. Backend authorize call, bikin room di janus, masukin dua peserta, relay SDP offer dan answer, lalu tukeran ICE candidate.
Room dibikin per call. Backend yg pegang state transition, notification, sama lifecycle sisanya. Janus ga perlu tau makna apa pun di level produk.
Media plane bawa RTP lewat UDP. Begitu signaling selesai, mayoritas client ngirim audio sama video langsung ke janus.
Sebagian network pakai symmetric NAT atau ngeblok jalur langsung. Buat client kayak gini, coturn jadi TURN relay. Tanpa TURN, sebagian call bakal gagal connect walaupun signaling-nya sukses.
Karena semua stream udah lewat janus, recording pakai jalur media yg sama. Janus nulis tiap stream ke disk. Worker nge-mux file-nya jadi MP4, encrypt hasilnya, lalu upload ke object storage.
Ga perlu recorder client-side atau jalur media kedua yg harus dijaga tetap sinkron.
Bagian operasionalnya
Host networking ngubah service discovery
SFU butuh range port UDP yg gede buat RTP. Range itu ga enak ditaro di belakang overlay network container, jadi janus jalan langsung di host network di luar orchestrator.
Efeknya kena ke name resolution. Janus ga bisa resolve nama service yg cuma ada di overlay network.
Waktu webhook update state berhenti masuk, target-nya ternyata nama service internal yg ga bisa di-resolve dari host.
Ngubah target ke 127.0.0.1 langsung benerin jalurnya.
ICE-lite butuh NAT mapping eksplisit
Server-nya punya public IP stabil dan ga perlu gather candidate sendiri secara dinamis.
Janus jalan dalam mode ICE-lite dengan nat_1_1_mapping yg nunjuk ke external IP itu.
Tanpa mapping tersebut, client bisa dapet candidate yg salah ngejelasin alamat host.
TURN ga bisa ngumpet di belakang HTTP proxy
Signaling API bisa ditaro di belakang CDN proxy. TURN engga.
Media WebRTC pakai UDP yg ga bisa dibawa reverse proxy HTTP biasa. DNS record TURN harus resolve langsung ke relay host.
Recording janus belum jadi video
Janus ngerekam satu file mentah buat tiap media stream dalam formatnya sendiri. File itu belum bisa langsung diputer.
Worker terpisah pakai ffmpeg buat nge-mux semuanya jadi MP4. Tanpa batas thread, ffmpeg bakal pakai semua CPU core yg ada, jadi worker-nya punya limit eksplisit supaya ga ganggu call aktif.
Recording itu pipeline, bukan checkbox.
Egress adalah bagian dari arsitektur
Setiap byte yg masuk ke SFU bakal keluar lagi. Cloud provider nagih outgoing traffic itu sebagai egress.
Bentuk kasar ongkosnya:
concurrent call x menit x bitrate x 2
Tiap peserta ngirim stream ke janus, lalu janus forward ke peserta lain. P2P ga punya traffic server-side ini karena medianya ga lewat SFU.
Kontrol ongkos paling kuat adalah hard cap bitrate per room. Gw batasin audio sama video dari server supaya client ga bisa nego lewat limit. Nurunin video cap langsung nurunin egress secara proporsional, sementara beda kualitas di resolusi call lebih kecil daripada beda bandwidth-nya.
Pilihan lain adalah routing call yg ga butuh media server-side lewat jalur P2P. Egress jadi hampir nol, tapi recording harus pindah ke client.
Topologi yg benar tergantung requirement mana yg bikin server harus tetap ada di tengah.
Bakal gw pilih janus lagi?
Iya, selama recording dan call control butuh jalur media server-side.
Janus tetap fokus di WebRTC dan ngebiarin product logic hidup di app. Kompleksitas aslinya ada di sekelilingnya: host networking, NAT mapping, TURN, recording, sama egress.
Kalo mulai lagi, lima hal itu bakal gw modelin sebelum call production pertama. Plugin videoroom-nya sendiri justru bagian yg paling lurus.