mcpField
W
webrtc

webrtc

github.com/claude-dev-suite開発46204 回インストール2 日前に更新
リポジトリ

webrtc について

WebRTCのピア接続、シグナリング、データチャネル、画面共有の実装例をまとめたスキルです。グループ通話向けのLiveKitと、接続の信頼性を確認する本番チェック項目も扱います。

主な機能

  1. 01ピア接続の設定
  2. 02Socket.IOシグナリング
  3. 03データチャネルでのメッセージ交換
  4. 04画面共有
  5. 05LiveKitのグループ通話例
  6. 06本番環境の信頼性チェック

活用シーン

  1. 011対1のWebRTC通話を構築する
  2. 02データチャネルでメッセージを交換する
  3. 03通話中に画面を共有する
  4. 04SFUを使ったグループ通話を計画する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill webrtc

インストール後、Claude Code で/webrtcを入力して読み込まれたか確認します。

詳細

WebRTC

ピア接続のセットアップ

const config: RTCConfiguration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'turn:turn.example.com', username: 'user', credential: 'pass' },
  ],
};

const pc = new RTCPeerConnection(config);

// Get local media
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach((track) => pc.addTrack(track, stream));

// Display remote stream
pc.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};

// ICE candidates — send to remote peer via signaling
pc.onicecandidate = (event) => {
  if (event.candidate) {
    signalingChannel.send({ type: 'ice-candidate', candidate: event.candidate });
  }
};

シグナリング(Socket.IO 経由)

// Caller
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
socket.emit('signal', { type: 'offer', sdp: offer });

// Callee — on receiving offer
socket.on('signal', async (msg) => {
  if (msg.type === 'offer') {
    await pc.setRemoteDescription(msg.sdp);
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);
    socket.emit('signal', { type: 'answer', sdp: answer });
  } else if (msg.type === 'answer') {
    await pc.setRemoteDescription(msg.sdp);
  } else if (msg.type === 'ice-candidate') {
    await pc.addIceCandidate(msg.candidate);
  }
});

データチャネル

const dataChannel = pc.createDataChannel('chat', { ordered: true });

dataChannel.onopen = () => dataChannel.send('Hello!');
dataChannel.onmessage = (e) => console.log('Received:', e.data);

// Remote side
pc.ondatachannel = (event) => {
  const channel = event.channel;
  channel.onmessage = (e) => console.log('Received:', e.data);
};

画面共有

const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: { cursor: 'always' },
  audio: true,
});

const videoTrack = screenStream.getVideoTracks()[0];
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
await sender?.replaceTrack(videoTrack);

videoTrack.onended = () => {
  // User stopped sharing — switch back to camera
  const cameraTrack = localStream.getVideoTracks()[0];
  sender?.replaceTrack(cameraTrack);
};

LiveKit(SFU — グループ通話に推奨)

import { Room, RoomEvent } from 'livekit-client';

const room = new Room();
await room.connect(LIVEKIT_URL, accessToken);

await room.localParticipant.enableCameraAndMicrophone();

room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  const element = track.attach();
  document.getElementById('remote-videos')!.appendChild(element);
});

room.on(RoomEvent.ParticipantDisconnected, (participant) => {
  console.log(`${participant.identity} left`);
});

アンチパターン

アンチパターン対処方法
TURN サーバーがないNAT 越えのため、常に TURN を設定します
グループ通話(4 人超)で P2P を使用するSFU(LiveKit、mediasoup)を使用します
getUserMedia のエラーハンドリングがないNotAllowedError、NotFoundError を処理します
暗号化されていないチャネルでシグナリングするWSS(WebSocket Secure)を使用します
接続状態を監視しないconnectionstatechange イベントをリッスンします

本番環境のチェックリスト

  • TURN サーバーをデプロイして設定する
  • グループビデオ(参加者 3 人超)には SFU を使用する
  • メディアへのアクセスが拒否された場合のフォールバック UI
  • 接続品質の監視
  • 帯域幅の適応(シマルキャスト)
  • 必要に応じて録画機能を用意する

コメント