WebRTC
ブラウザ間でP2P(または SFU 経由)の音声・動画・データをやり取りする仕組み。 ビデオ会議・ライブ配信・低遅延ゲーム・ファイル直接転送・画面共有まで。
3 つの主要 API
- getUserMedia — カメラ・マイクの取得
- RTCPeerConnection — P2P 接続の確立と通信
- RTCDataChannel — 任意データ(テキスト・バイナリ)の送受信
カメラ・マイクの取得
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720, facingMode: "user" },
audio: { echoCancellation: true, noiseSuppression: true },
})
document.querySelector("#preview").srcObject = stream
// 停止
stream.getTracks().forEach((t) => t.stop())
デバイス一覧
const devices = await navigator.mediaDevices.enumerateDevices()
const cams = devices.filter((d) => d.kind === "videoinput")
const mics = devices.filter((d) => d.kind === "audioinput")
await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: cams[1].deviceId } },
})
画面共有
const screen = await navigator.mediaDevices.getDisplayMedia({
video: { frameRate: 30 },
audio: true, // 一部 OS のみ
})
P2P 接続の流れ
- シグナリングサーバ(WebSocket 等)で 2 ピアを繋げる場を提供
- 呼ぶ側が
RTCPeerConnectionを作成 → Offer SDP を生成 - シグナリング経由で Offer を相手へ送信
- 相手が
RTCPeerConnectionを作って Offer を受け取り → Answer SDP を返す - 双方が ICE candidates を交換
- P2P 接続確立 → メディア / データの送受信
シンプルな実装
const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{
urls: "turn:turn.example.com:3478",
username: "user",
credential: "pass",
},
],
})
// ローカルストリーム追加
stream.getTracks().forEach((t) => pc.addTrack(t, stream))
// 相手のストリーム
pc.ontrack = (e) => {
remoteVideo.srcObject = e.streams[0]
}
// ICE candidate を相手に送る
pc.onicecandidate = (e) => {
if (e.candidate) signaling.send({ type: "ice", candidate: e.candidate })
}
// Offer を作成
const offer = await pc.createOffer()
await pc.setLocalDescription(offer)
signaling.send({ type: "offer", sdp: offer })
// 相手から Answer / ICE が来たら
signaling.onMessage(async (msg) => {
if (msg.type === "answer") {
await pc.setRemoteDescription(msg.sdp)
} else if (msg.type === "ice") {
await pc.addIceCandidate(msg.candidate)
}
})
STUN / TURN
- STUN — 自分のグローバル IP / Port を知るためのサーバ。NAT 越えに使う
- TURN — STUN で抜けない時に中継するサーバ。費用がかかるが必須
- Google の STUN(
stun:stun.l.google.com:19302)が無料で使える - TURN は自前運用(Coturn)かSaaS(Twilio / Xirsys)
シグナリング
WebRTC 自体にシグナリング機能は無い。WebSocket / Firebase / Pusher 等で自前で繋ぐ。 単純な部屋制御 + メッセージ転送だけで OK。
RTCDataChannel
テキスト・バイナリの双方向通信。P2P で WebSocket 並みの API。
// 呼ぶ側
const dc = pc.createDataChannel("chat", { ordered: true })
dc.onopen = () => dc.send("hello")
dc.onmessage = (e) => console.log(e.data)
// 受ける側
pc.ondatachannel = (e) => {
const ch = e.channel
ch.onmessage = (e) => console.log(e.data)
}
オプション
ordered: true— 順序保証(既定)maxRetransmits: 0— 再送なし(UDP 風)maxPacketLifeTime: 1000— ms 単位の寿命maxRetransmitsとmaxPacketLifeTimeは同時指定不可
SFU と MCU
多人数会議は純粋 P2P では破綻(10 人で 90 ストリーム)。中継サーバを使う:
- SFU(Selective Forwarding Unit) — 中継のみ。デコードしない。定番
- MCU(Multipoint Control Unit) — 全ストリームを合成して 1 本に。重い
OSS SFU: mediasoup / Janus / LiveKit / Pion。SaaS: Daily / Agora / Twilio Video / 100ms。
主要なライブラリ・SaaS
| 名前 | 特徴 |
|---|---|
| simple-peer | ピア通信を薄くラップ |
| PeerJS | シグナリングサーバ込み |
| LiveKit | OSS の SFU + Cloud。大規模会議 |
| Daily | Video API SaaS |
| Agora | SaaS。グローバル展開強い |
| 100ms | SaaS。エンタープライズ |
| Twilio Video | SaaS。古典 |
| mediasoup | OSS SFU。Node.js |
| Pion | Go 製の WebRTC 実装 |
セキュリティ
- 必ず HTTPS(getUserMedia は HTTPS 必須)
- SDP / ICE は暗号化される(DTLS / SRTP)
- シグナリング経路は認証必須(誰でも接続可だと荒らされる)
- TURN サーバにはcredential必須。短命のトークン発行
- ピアの IP がバレる(プライバシー)。VPN / TURN 強制で隠せる
権限とパーミッション
- カメラ・マイクはユーザー許可ダイアログ必須
- 初回拒否されたら復活困難(設定アプリで戻す必要)
- 権限を求める前に「なぜ必要か」を画面に明示
navigator.permissions.query({ name: "camera" })で状態確認
ローカル開発の注意
- HTTPS が必要。
localhostなら HTTP でも OK - 2 ブラウザを別ウィンドウで開いてテスト
- 同一マシンの NAT 越えに失敗する場合は
?ip=...等で外部から
典型用途
- ビデオ会議(Zoom / Google Meet)
- ライブ配信のスタジオ
- P2P ファイル転送(ShareDrop など)
- 低遅延ゲーム(DataChannel 不順序モード)
- 遠隔操作・コーディング協業
WHIP / WHEP
WebRTC を放送風に使う新標準。WHIP(送信)/ WHEP(受信)。 OBS や HLS / RTMP の代替として注目されている(低遅延)。
学び方
- webrtc.org 公式のサンプル
- WebRTC for the Curious(無料書籍)
- サンプルコードを動かすのが圧倒的に早い
- 本格的な多人数会議はライブラリ / SaaS に頼るのが現実的