Elysia Tools

Parcourez outils, exemples, explications visuelles et guides thématiques dans un espace clair.

Catégories populaires

Dossiers

Langue
ZHESPTRUFRDEEN

Elysia Tools

Navigation mobile

Technologies web

Exemples de Communication Temps Réel WebRTC

Exemples complets WebRTC pour communication audio/vidéo P2P, canaux de données, partage d'écran et serveur de signalisation

Exemples

Entrées de cette collection

Code sourceIntermédiaire

Implémentation Appel Vidéo P2P

Configuration complète appel vidéo WebRTC avec accès caméra et audio

Difficulté

7/10

Temps estimé

30 min

Étiquettes

webrtc, p2p, video, audio, peer-connection, media-stream

plaintext
// WebRTC Peer-to-Peer Video Call Implementation
// Complete setup for video and audio communication

class WebRTCPeerConnection {
  constructor(config = {}) {
    this.localVideo = document.getElementById('localVideo');
    this.remoteVideo = document.getElementById('remoteVideo');

    this.pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        {
          urls: 'turn:turn.example.com:3478',
          username: 'user',
          credential: 'password'
        }
      ],
      ...config
    });

    this.setupPeerConnection();
    this.setupMediaStreams();
  }

  async setupMediaStreams() {
    try {
      // Get local media stream with video and audio
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          width: { ideal: 1280 },
          height: { ideal: 720 },
          facingMode: 'user'
        },
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true
        }
      });

      this.localVideo.srcObject = stream;

      // Add tracks to peer connection
      stream.getTracks().forEach(track => {
        this.pc.addTrack(track, stream);
      });

    } catch (error) {
      console.error('Error accessing media devices:', error);
    }
  }

  setupPeerConnection() {
    // Handle incoming tracks
    this.pc.ontrack = (event) => {
      if (event.streams && event.streams[0]) {
        this.remoteVideo.srcObject = event.streams[0];
      }
    };

    // Handle ICE candidates
    this.pc.onicecandidate = (event) => {
      if (event.candidate) {
        // Send candidate to remote peer via signaling server
        this.sendSignalingMessage({
          type: 'ice-candidate',
          candidate: event.candidate
        });
      }
    };

    // Handle connection state changes
    this.pc.onconnectionstatechange = () => {
      console.log('Connection state:', this.pc.connectionState);

      if (this.pc.connectionState === 'connected') {
        console.log('WebRTC connection established!');
      }
    };

    // Handle ICE connection state changes
    this.pc.oniceconnectionstatechange = () => {
      console.log('ICE connection state:', this.pc.iceConnectionState);
    };
  }

  async createOffer() {
    try {
      const offer = await this.pc.createOffer({
        offerToReceiveAudio: true,
        offerToReceiveVideo: true
      });

      await this.pc.setLocalDescription(offer);

      // Send offer to remote peer
      this.sendSignalingMessage({
        type: 'offer',
        offer: offer
      });

    } catch (error) {
      console.error('Error creating offer:', error);
    }
  }

  async handleOffer(offer) {
    try {
      await this.pc.setRemoteDescription(offer);

      const answer = await this.pc.createAnswer();
      await this.pc.setLocalDescription(answer);

      // Send answer back
      this.sendSignalingMessage({
        type: 'answer',
        answer: answer
      });

    } catch (error) {
      console.error('Error handling offer:', error);
    }
  }

  async handleAnswer(answer) {
    try {
      await this.pc.setRemoteDescription(answer);
    } catch (error) {
      console.error('Error handling answer:', error);
    }
  }

  async handleIceCandidate(candidate) {
    try {
      await this.pc.addIceCandidate(candidate);
    } catch (error) {
      console.error('Error adding ICE candidate:', error);
    }
  }

  sendSignalingMessage(message) {
    // Implement WebSocket or signaling server communication
    console.log('Sending signaling message:', message);
  }

  // Media controls
  toggleAudio() {
    const audioTrack = this.localVideo.srcObject.getAudioTracks()[0];
    if (audioTrack) {
      audioTrack.enabled = !audioTrack.enabled;
    }
  }

  toggleVideo() {
    const videoTrack = this.localVideo.srcObject.getVideoTracks()[0];
    if (videoTrack) {
      videoTrack.enabled = !videoTrack.enabled;
    }
  }

  // Hang up the call
  hangup() {
    if (this.pc) {
      this.pc.close();
      this.pc = null;
    }

    // Stop local media
    if (this.localVideo.srcObject) {
      this.localVideo.srcObject.getTracks().forEach(track => track.stop());
      this.localVideo.srcObject = null;
    }

    this.remoteVideo.srcObject = null;
  }
}

// Usage example
const webrtcConnection = new WebRTCPeerConnection();

// Start the call
webrtcConnection.createOffer();
format
JavaScript
apis
["RTCPeerConnection","MediaDevices","MediaStream"]
features
["ice-handling","media-stream","connection-management"]
browsers
["Chrome","Firefox","Safari","Edge"]
Code sourceIntermédiaire

Communication Canal Données WebRTC

Implémentation de canaux de données pour messagerie et transfert de fichiers en temps réel

Difficulté

6/10

Temps estimé

25 min

Étiquettes

data-channel, real-time, messaging, file-transfer, p2p

plaintext
// WebRTC Data Channel Implementation
// Real-time data communication between peers

class WebRTCDataChannel {
  constructor(peerConnection) {
    this.pc = peerConnection;
    this.dataChannel = null;
    this.messageCallbacks = new Map();

    this.setupDataChannel();
  }

  setupDataChannel() {
    // Create data channel for reliable messaging
    this.dataChannel = this.pc.createDataChannel('chat', {
      ordered: true,
      maxRetransmits: 3,
      protocol: 'json'
    });

    this.setupDataChannelHandlers();
  }

  setupDataChannelHandlers() {
    this.dataChannel.onopen = () => {
      console.log('Data channel opened');
      this.onChannelOpen();
    };

    this.dataChannel.onmessage = (event) => {
      this.handleMessage(event.data);
    };

    this.dataChannel.onclose = () => {
      console.log('Data channel closed');
      this.onChannelClose();
    };

    this.dataChannel.onerror = (error) => {
      console.error('Data channel error:', error);
      this.onChannelError(error);
    };
  }

  handleMessage(data) {
    try {
      const message = JSON.parse(data);

      switch (message.type) {
        case 'chat':
          this.onChatMessage(message);
          break;
        case 'file':
          this.onFileTransfer(message);
          break;
        case 'typing':
          this.onTypingIndicator(message);
          break;
        case 'cursor':
          this.onCursorUpdate(message);
          break;
        default:
          console.log('Unknown message type:', message.type);
      }

      // Call custom callbacks
      if (this.messageCallbacks.has(message.type)) {
        this.messageCallbacks.get(message.type)(message);
      }

    } catch (error) {
      console.error('Error parsing message:', error);
    }
  }

  // Send different types of messages
  sendChatMessage(text, sender = 'Anonymous') {
    const message = {
      type: 'chat',
      text: text,
      sender: sender,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendFile(file, onProgress) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = (event) => {
        const message = {
          type: 'file',
          name: file.name,
          size: file.size,
          mimeType: file.type,
          data: event.target.result
        };

        this.sendMessage(message);
        resolve(message);
      };

      reader.onerror = (error) => reject(error);
      reader.readAsArrayBuffer(file);
    });
  }

  sendTypingIndicator(isTyping) {
    const message = {
      type: 'typing',
      isTyping: isTyping,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendCursorUpdate(x, y) {
    const message = {
      type: 'cursor',
      x: x,
      y: y,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendCustomMessage(type, data) {
    const message = {
      type: type,
      data: data,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendMessage(message) {
    if (this.dataChannel && this.dataChannel.readyState === 'open') {
      this.dataChannel.send(JSON.stringify(message));
    } else {
      console.error('Data channel is not open');
    }
  }

  // Event handlers (can be overridden)
  onChannelOpen() {
    console.log('Data channel ready for communication');
  }

  onChannelClose() {
    console.log('Data channel communication ended');
  }

  onChannelError(error) {
    console.error('Data channel communication error:', error);
  }

  onChatMessage(message) {
    console.log('Chat message received:', message);
  }

  onFileTransfer(message) {
    console.log('File transfer received:', message);
  }

  onTypingIndicator(message) {
    console.log('Typing indicator:', message);
  }

  onCursorUpdate(message) {
    console.log('Cursor update:', message);
  }

  // Register custom message handlers
  onMessageType(type, callback) {
    this.messageCallbacks.set(type, callback);
  }

  // Data channel status
  isOpen() {
    return this.dataChannel && this.dataChannel.readyState === 'open';
  }

  close() {
    if (this.dataChannel) {
      this.dataChannel.close();
      this.dataChannel = null;
    }
  }
}

// File transfer helper class
class FileTransferHandler {
  constructor(dataChannel) {
    this.dataChannel = dataChannel;
    this.receivingFiles = new Map();
  }

  async receiveFile(fileInfo, onProgress) {
    const chunks = [];
    let receivedBytes = 0;

    return new Promise((resolve, reject) => {
      const handleChunk = (chunk) => {
        chunks.push(chunk);
        receivedBytes += chunk.byteLength;

        if (onProgress) {
          onProgress(receivedBytes, fileInfo.size);
        }

        if (receivedBytes >= fileInfo.size) {
          const blob = new Blob(chunks, { type: fileInfo.type });
          const file = new File([blob], fileInfo.name, { type: fileInfo.type });
          resolve(file);
        }
      };

      this.dataChannel.onMessageType('file-chunk', (message) => {
        if (message.fileId === fileInfo.id) {
          handleChunk(message.data);
        }
      });
    });
  }

  async sendFile(file, onProgress) {
    const chunkSize = 16384; // 16KB chunks
    const fileId = Date.now().toString();

    // Send file metadata
    this.dataChannel.sendCustomMessage('file-info', {
      id: fileId,
      name: file.name,
      size: file.size,
      type: file.type
    });

    // Send file in chunks
    const reader = new FileReader();
    let offset = 0;

    const sendNextChunk = () => {
      const chunk = file.slice(offset, offset + chunkSize);
      reader.readAsArrayBuffer(chunk);
    };

    reader.onload = (event) => {
      this.dataChannel.sendCustomMessage('file-chunk', {
        fileId: fileId,
        data: event.target.result
      });

      offset += chunkSize;

      if (onProgress) {
        onProgress(offset, file.size);
      }

      if (offset < file.size) {
        setTimeout(sendNextChunk, 10); // Small delay to prevent blocking
      }
    };

    sendNextChunk();
  }
}

// Usage example
const webrtcDataChannel = new WebRTCDataChannel(peerConnection);

// Set up message handlers
webrtcDataChannel.onMessageType('game-state', (message) => {
  updateGameDisplay(message.data);
});

// Send messages
webrtcDataChannel.sendChatMessage('Hello from peer!');
webrtcDataChannel.sendTypingIndicator(true);
format
JavaScript
apis
["RTCDataChannel","FileReader","Blob"]
features
["reliable-messaging","file-transfer","custom-protocols"]
protocols
["JSON","Binary"]
Code sourceIntermédiaire

Implémentation Partage Écran

Configuration complète de partage d'écran avec support des onglets et audio

Difficulté

6/10

Temps estimé

20 min

Étiquettes

screen-share, desktop-capture, tab-sharing, picture-in-picture, recording

plaintext
// WebRTC Screen Sharing Implementation
// Screen capture, tab sharing, and audio support

class ScreenShareManager {
  constructor(peerConnection) {
    this.pc = peerConnection;
    this.screenStream = null;
    this.localVideo = document.getElementById('localScreen');
    this.isSharing = false;
  }

  async startScreenShare(options = {}) {
    try {
      // Get screen share stream
      this.screenStream = await navigator.mediaDevices.getDisplayMedia({
        video: {
          cursor: options.cursor || 'always',
          displaySurface: options.displaySurface || 'monitor'
        },
        audio: options.audio || false,
        preferCurrentTab: options.preferCurrentTab || false
      });

      // Replace local video with screen share
      this.localVideo.srcObject = this.screenStream;

      // Add or replace video tracks in peer connection
      const videoTracks = this.screenStream.getVideoTracks();
      const sender = this.pc.getSenders().find(s =>
        s.track && s.track.kind === 'video'
      );

      if (sender && videoTracks.length > 0) {
        // Replace existing video track
        await sender.replaceTrack(videoTracks[0]);
      } else if (videoTracks.length > 0) {
        // Add new video track
        this.pc.addTrack(videoTracks[0], this.screenStream);
      }

      // Add audio tracks if available
      const audioTracks = this.screenStream.getAudioTracks();
      audioTracks.forEach(track => {
        const audioSender = this.pc.getSenders().find(s =>
          s.track && s.track.kind === 'audio'
        );

        if (audioSender) {
          audioSender.replaceTrack(track);
        } else {
          this.pc.addTrack(track, this.screenStream);
        }
      });

      this.isSharing = true;

      // Listen for stream end
      this.screenStream.getVideoTracks()[0].onended = () => {
        this.stopScreenShare();
      };

      return this.screenStream;

    } catch (error) {
      console.error('Error starting screen share:', error);
      throw error;
    }
  }

  async startTabShare() {
    try {
      // Use Chrome extension API for tab sharing (if available)
      if (chrome && chrome.desktopCapture) {
        return new Promise((resolve, reject) => {
          chrome.desktopCapture.chooseDesktopMedia(
            ['screen', 'window', 'tab'],
            (streamId) => {
              if (streamId) {
                navigator.mediaDevices.getUserMedia({
                  video: {
                    mandatory: {
                      chromeMediaSource: 'desktop',
                      chromeMediaSourceId: streamId
                    }
                  }
                }).then(resolve).catch(reject);
              } else {
                reject(new Error('No stream selected'));
              }
            }
          );
        });
      } else {
        // Fallback to standard screen capture with tab preference
        return this.startScreenShare({
          preferCurrentTab: true,
          displaySurface: 'browser'
        });
      }
    } catch (error) {
      console.error('Error starting tab share:', error);
      throw error;
    }
  }

  async stopScreenShare() {
    try {
      // Stop all tracks in screen stream
      if (this.screenStream) {
        this.screenStream.getTracks().forEach(track => track.stop());
        this.screenStream = null;
      }

      // Clear local video
      this.localVideo.srcObject = null;

      // Try to restore camera stream if available
      await this.restoreCameraStream();

      this.isSharing = false;

    } catch (error) {
      console.error('Error stopping screen share:', error);
    }
  }

  async restoreCameraStream() {
    try {
      const cameraStream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true
      });

      this.localVideo.srcObject = cameraStream;

      // Replace tracks in peer connection
      const videoTracks = cameraStream.getVideoTracks();
      const sender = this.pc.getSenders().find(s =>
        s.track && s.track.kind === 'video'
      );

      if (sender && videoTracks.length > 0) {
        await sender.replaceTrack(videoTracks[0]);
      }

      // Add audio tracks
      const audioTracks = cameraStream.getAudioTracks();
      audioTracks.forEach(track => {
        const audioSender = this.pc.getSenders().find(s =>
          s.track && s.track.kind === 'audio'
        );

        if (audioSender) {
          audioSender.replaceTrack(track);
        }
      });

    } catch (error) {
      console.error('Error restoring camera stream:', error);
    }
  }

  async toggleScreenShare() {
    if (this.isSharing) {
      await this.stopScreenShare();
    } else {
      await this.startScreenShare();
    }
  }

  // Get available screen sources
  async getScreenSources() {
    try {
      if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
        const devices = await navigator.mediaDevices.enumerateDevices();
        return devices.filter(device => device.kind === 'videoinput');
      }
      return [];
    } catch (error) {
      console.error('Error getting screen sources:', error);
      return [];
    }
  }

  // Check screen sharing capabilities
  getCapabilities() {
    return {
      screenCapture: !!(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia),
      tabCapture: !!(chrome && chrome.desktopCapture),
      audioCapture: !!(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia),
      cursorSupport: true,
      multipleSources: true
    };
  }

  // Add picture-in-picture support
  async enablePictureInPicture() {
    try {
      if (this.localVideo && 'requestPictureInPicture' in this.localVideo) {
        await this.localVideo.requestPictureInPicture();
      }
    } catch (error) {
      console.error('Error enabling picture-in-picture:', error);
    }
  }

  disablePictureInPicture() {
    if (this.localVideo && 'exitPictureInPicture' in this.localVideo) {
      this.localVideo.exitPictureInPicture();
    }
  }

  // Recording functionality
  startRecording() {
    if (!this.screenStream) return null;

    const chunks = [];
    const mediaRecorder = new MediaRecorder(this.screenStream, {
      mimeType: MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
        ? 'video/webm;codecs=vp9'
        : 'video/webm'
    });

    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0) {
        chunks.push(event.data);
      }
    };

    mediaRecorder.onstop = () => {
      const blob = new Blob(chunks, { type: 'video/webm' });
      this.onRecordingComplete(blob);
    };

    mediaRecorder.start();
    return mediaRecorder;
  }

  onRecordingComplete(blob) {
    // Handle recording completion
    console.log('Screen share recording completed:', blob);

    // Create download link
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `screen-share-${Date.now()}.webm`;
    a.click();
    URL.revokeObjectURL(url);
  }

  // Get current status
  getStatus() {
    return {
      isSharing: this.isSharing,
      hasVideo: !!(this.screenStream && this.screenStream.getVideoTracks().length),
      hasAudio: !!(this.screenStream && this.screenStream.getAudioTracks().length),
      trackCount: this.screenStream ? this.screenStream.getTracks().length : 0
    };
  }
}

// UI Integration Example
class ScreenShareUI {
  constructor(screenShareManager) {
    this.screenShare = screenShareManager;
    this.setupUI();
  }

  setupUI() {
    // Create control buttons
    this.createShareButton();
    this.createTabShareButton();
    this.createStopButton();
    this.createRecordButton();

    // Create status display
    this.createStatusDisplay();
  }

  createShareButton() {
    const button = document.createElement('button');
    button.textContent = 'Share Screen';
    button.onclick = async () => {
      try {
        await this.screenShare.startScreenShare();
        this.updateStatus();
      } catch (error) {
        this.showError('Failed to start screen share: ' + error.message);
      }
    };
    document.body.appendChild(button);
  }

  createTabShareButton() {
    const button = document.createElement('button');
    button.textContent = 'Share Tab';
    button.onclick = async () => {
      try {
        await this.screenShare.startTabShare();
        this.updateStatus();
      } catch (error) {
        this.showError('Failed to start tab share: ' + error.message);
      }
    };
    document.body.appendChild(button);
  }

  createStopButton() {
    const button = document.createElement('button');
    button.textContent = 'Stop Sharing';
    button.onclick = async () => {
      await this.screenShare.stopScreenShare();
      this.updateStatus();
    };
    document.body.appendChild(button);
  }

  createRecordButton() {
    const button = document.createElement('button');
    button.textContent = 'Start Recording';

    button.onclick = () => {
      if (!this.recording) {
        this.recording = this.screenShare.startRecording();
        button.textContent = 'Stop Recording';
      } else {
        this.recording.stop();
        this.recording = null;
        button.textContent = 'Start Recording';
      }
    };

    document.body.appendChild(button);
  }

  createStatusDisplay() {
    this.statusDiv = document.createElement('div');
    this.statusDiv.style.marginTop = '10px';
    this.updateStatus();
    document.body.appendChild(this.statusDiv);
  }

  updateStatus() {
    const status = this.screenShare.getStatus();
    this.statusDiv.innerHTML = `
      <div>Sharing: ${status.isSharing ? 'Yes' : 'No'}</div>
      <div>Video: ${status.hasVideo ? 'Yes' : 'No'}</div>
      <div>Audio: ${status.hasAudio ? 'Yes' : 'No'}</div>
      <div>Tracks: ${status.trackCount}</div>
    `;
  }

  showError(message) {
    alert(message);
  }
}

// Usage example
const screenShareManager = new ScreenShareManager(peerConnection);
const screenShareUI = new ScreenShareUI(screenShareManager);
format
JavaScript
apis
["getDisplayMedia","MediaRecorder","PictureInPicture"]
features
["screen-capture","tab-capture","audio-capture","recording"]
browsers
["Chrome","Firefox","Edge"]
Code sourceComplexe

Serveur Signalisation WebRTC

Serveur WebSocket pour établissement de connexions P2P et négociation médias

Difficulté

8/10

Temps estimé

35 min

Étiquettes

signaling, websocket, peer-connection, room-management, server

plaintext
// WebRTC Signaling Server Implementation
// Node.js WebSocket server for peer connection coordination

const WebSocket = require('ws');
const http = require('http');
const express = require('express');
const cors = require('cors');

class WebRTCSignalingServer {
  constructor(port = 8080) {
    this.port = port;
    this.app = express();
    this.server = http.createServer(this.app);
    this.wss = new WebSocket.Server({ server: this.server });

    this.rooms = new Map(); // room_id -> Set of peers
    this.peers = new Map(); // peer_id -> { ws, room_id, metadata }

    this.setupExpress();
    this.setupWebSocket();
  }

  setupExpress() {
    this.app.use(cors());
    this.app.use(express.json());

    // Static files for client
    this.app.use(express.static('public'));

    // API endpoints
    this.app.get('/api/rooms', (req, res) => {
      const rooms = Array.from(this.rooms.keys()).map(roomId => ({
        id: roomId,
        peerCount: this.rooms.get(roomId).size
      }));
      res.json(rooms);
    });

    this.app.post('/api/rooms', (req, res) => {
      const roomId = this.generateRoomId();
      this.rooms.set(roomId, new Set());
      res.json({ roomId });
    });

    this.app.get('/api/rooms/:roomId/peers', (req, res) => {
      const roomId = req.params.roomId;
      const roomPeers = this.rooms.get(roomId);

      if (!roomPeers) {
        return res.status(404).json({ error: 'Room not found' });
      }

      const peers = Array.from(roomPeers).map(peerId => ({
        id: peerId,
        metadata: this.peers.get(peerId)?.metadata
      }));

      res.json(peers);
    });
  }

  setupWebSocket() {
    this.wss.on('connection', (ws, req) => {
      const peerId = this.generatePeerId();

      console.log(`Peer connected: ${peerId}`);

      // Store peer connection
      this.peers.set(peerId, {
        ws: ws,
        room_id: null,
        metadata: {
          userAgent: req.headers['user-agent'],
          ip: req.socket.remoteAddress,
          connectedAt: Date.now()
        }
      });

      // Send peer ID to client
      this.sendToPeer(peerId, {
        type: 'peer-id',
        peerId: peerId
      });

      // Handle messages
      ws.on('message', (message) => {
        try {
          const data = JSON.parse(message);
          this.handleMessage(peerId, data);
        } catch (error) {
          console.error(`Invalid message from peer ${peerId}:`, error);
          this.sendError(peerId, 'Invalid message format');
        }
      });

      // Handle disconnection
      ws.on('close', () => {
        console.log(`Peer disconnected: ${peerId}`);
        this.handlePeerDisconnect(peerId);
      });

      // Handle errors
      ws.on('error', (error) => {
        console.error(`WebSocket error for peer ${peerId}:`, error);
      });
    });
  }

  handleMessage(peerId, message) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    switch (message.type) {
      case 'join-room':
        this.handleJoinRoom(peerId, message.roomId);
        break;

      case 'leave-room':
        this.handleLeaveRoom(peerId);
        break;

      case 'offer':
        this.handleRelayMessage(peerId, message, 'offer');
        break;

      case 'answer':
        this.handleRelayMessage(peerId, message, 'answer');
        break;

      case 'ice-candidate':
        this.handleRelayMessage(peerId, message, 'ice-candidate');
        break;

      case 'room-info':
        this.sendRoomInfo(peerId);
        break;

      case 'peer-list':
        this.sendPeerList(peerId);
        break;

      case 'direct-message':
        this.handleDirectMessage(peerId, message);
        break;

      default:
        console.log(`Unknown message type: ${message.type}`);
    }
  }

  handleJoinRoom(peerId, roomId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Leave current room if in one
    if (peer.room_id) {
      this.handleLeaveRoom(peerId);
    }

    // Join new room
    if (!this.rooms.has(roomId)) {
      this.rooms.set(roomId, new Set());
    }

    this.rooms.get(roomId).add(peerId);
    peer.room_id = roomId;

    console.log(`Peer ${peerId} joined room ${roomId}`);

    // Notify room about new peer
    this.broadcastToRoom(roomId, {
      type: 'peer-joined',
      peerId: peerId,
      metadata: peer.metadata
    }, peerId);

    // Send room info to new peer
    this.sendRoomInfo(peerId);
  }

  handleLeaveRoom(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer || !peer.room_id) return;

    const roomId = peer.room_id;
    this.rooms.get(roomId).delete(peerId);

    // Clean up empty rooms
    if (this.rooms.get(roomId).size === 0) {
      this.rooms.delete(roomId);
    }

    console.log(`Peer ${peerId} left room ${roomId}`);

    // Notify room about peer leaving
    this.broadcastToRoom(roomId, {
      type: 'peer-left',
      peerId: peerId
    });

    peer.room_id = null;
  }

  handleRelayMessage(senderId, message, messageType) {
    const peer = this.peers.get(senderId);
    if (!peer || !peer.room_id) return;

    // Add sender info to message
    message.senderId = senderId;

    // Relay to all peers in room except sender
    this.broadcastToRoom(peer.room_id, message, senderId);
  }

  handleDirectMessage(senderId, message) {
    const recipientId = message.recipientId;
    const recipient = this.peers.get(recipientId);

    if (recipient) {
      message.senderId = senderId;
      this.sendToPeer(recipientId, message);
    } else {
      this.sendError(senderId, `Recipient ${recipientId} not found`);
    }
  }

  handlePeerDisconnect(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Leave room if in one
    if (peer.room_id) {
      this.handleLeaveRoom(peerId);
    }

    // Clean up peer data
    this.peers.delete(peerId);
  }

  sendToPeer(peerId, message) {
    const peer = this.peers.get(peerId);
    if (peer && peer.ws.readyState === WebSocket.OPEN) {
      peer.ws.send(JSON.stringify(message));
    }
  }

  sendError(peerId, error) {
    this.sendToPeer(peerId, {
      type: 'error',
      error: error
    });
  }

  broadcastToRoom(roomId, message, excludePeerId = null) {
    const room = this.rooms.get(roomId);
    if (!room) return;

    room.forEach(peerId => {
      if (peerId !== excludePeerId) {
        this.sendToPeer(peerId, message);
      }
    });
  }

  sendRoomInfo(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer || !peer.room_id) return;

    const room = this.rooms.get(peer.room_id);
    const peers = Array.from(room || []).filter(id => id !== peerId);

    this.sendToPeer(peerId, {
      type: 'room-info',
      roomId: peer.room_id,
      peers: peers,
      peerCount: peers.length
    });
  }

  sendPeerList(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    const allPeers = Array.from(this.peers.keys())
      .filter(id => id !== peerId)
      .map(id => ({
        id: id,
        metadata: this.peers.get(id)?.metadata
      }));

    this.sendToPeer(peerId, {
      type: 'peer-list',
      peers: allPeers
    });
  }

  generatePeerId() {
    return `peer_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  generateRoomId() {
    return Math.random().toString(36).substr(2, 8).toUpperCase();
  }

  start() {
    this.server.listen(this.port, () => {
      console.log(`Signaling server running on port ${this.port}`);
    });
  }

  stop() {
    this.wss.close();
    this.server.close();
  }

  // Server statistics
  getStats() {
    return {
      totalPeers: this.peers.size,
      totalRooms: this.rooms.size,
      rooms: Array.from(this.rooms.entries()).map(([id, peers]) => ({
        id,
        peerCount: peers.size
      }))
    };
  }
}

// Room management extension
class RoomManager {
  constructor(signalingServer) {
    this.server = signalingServer;

    // Add room management endpoints
    this.server.app.delete('/api/rooms/:roomId', (req, res) => {
      const roomId = req.params.roomId;
      const room = this.server.rooms.get(roomId);

      if (!room) {
        return res.status(404).json({ error: 'Room not found' });
      }

      // Disconnect all peers in room
      room.forEach(peerId => {
        this.server.sendToPeer(peerId, {
          type: 'room-closed',
          roomId: roomId,
          reason: 'Room deleted by admin'
        });

        const peer = this.server.peers.get(peerId);
        if (peer) {
          peer.room_id = null;
        }
      });

      this.server.rooms.delete(roomId);
      res.json({ message: 'Room deleted' });
    });

    // Add room settings
    this.server.app.patch('/api/rooms/:roomId/settings', (req, res) => {
      const roomId = req.params.roomId;
      const settings = req.body;

      // Store room settings (implement room settings storage)
      console.log(`Room ${roomId} settings updated:`, settings);

      // Broadcast settings update to room
      this.server.broadcastToRoom(roomId, {
        type: 'room-settings-updated',
        roomId: roomId,
        settings: settings
      });

      res.json({ message: 'Settings updated' });
    });
  }
}

// Usage example
const signalingServer = new WebRTCSignalingServer(8080);
const roomManager = new RoomManager(signalingServer);

// Start server
signalingServer.start();

// Graceful shutdown
process.on('SIGINT', () => {
  console.log('Shutting down signaling server...');
  signalingServer.stop();
  process.exit(0);
});

// Health check endpoint
signalingServer.app.get('/health', (req, res) => {
  const stats = signalingServer.getStats();
  res.json({
    status: 'healthy',
    ...stats
  });
});
format
JavaScript
frameworks
["Node.js","WebSocket","Express"]
features
["room-management","peer-discovery","message-relay"]
protocols
["WebSocket","HTTP"]
Code sourceComplexe

Implémentation Serveur Médias SFU

Architecture SFU pour visioconférences évolutives avec plusieurs participants

Difficulté

9/10

Temps estimé

45 min

Étiquettes

sfu, media-server, scalable, video-conference, simulcast, quality-control

plaintext
// WebRTC SFU (Selective Forwarding Unit) Implementation
// Scalable media server for multi-participant conferences

const WebSocket = require('ws');
const { RTCPeerConnection, RTCSessionDescription } = require('wrtc');
const uuid = require('uuid');

class SFUMediaServer {
  constructor() {
    this.rooms = new Map(); // roomId -> Room instance
    this.peers = new Map(); // peerId -> Peer instance
    this.wss = new WebSocket.Server({ port: 3000 });

    this.setupWebSocket();
  }

  setupWebSocket() {
    this.wss.on('connection', (ws, req) => {
      console.log('New SFU client connected');

      ws.on('message', async (data) => {
        try {
          const message = JSON.parse(data);
          await this.handleMessage(ws, message);
        } catch (error) {
          console.error('Error handling message:', error);
        }
      });

      ws.on('close', () => {
        console.log('SFU client disconnected');
        this.handleDisconnect(ws);
      });
    });
  }

  async handleMessage(ws, message) {
    const { type, roomId, peerId, data } = message;

    switch (type) {
      case 'join-room':
        await this.handleJoinRoom(ws, roomId, peerId, data);
        break;

      case 'leave-room':
        this.handleLeaveRoom(peerId);
        break;

      case 'offer':
        await this.handleOffer(peerId, data);
        break;

      case 'ice-candidate':
        await this.handleIceCandidate(peerId, data);
        break;

      case 'request-tracks':
        await this.handleTrackRequest(peerId, data);
        break;

      case 'update-layout':
        this.handleLayoutUpdate(roomId, data);
        break;

      default:
        console.log(`Unknown message type: ${type}`);
    }
  }

  async handleJoinRoom(ws, roomId, peerId, metadata = {}) {
    try {
      // Create room if it doesn't exist
      if (!this.rooms.has(roomId)) {
        this.rooms.set(roomId, new SFURoom(roomId));
      }

      const room = this.rooms.get(roomId);

      // Create peer instance
      const peer = new SFUPeer(peerId, ws, room, metadata);
      this.peers.set(peerId, peer);

      // Add peer to room
      await room.addPeer(peer);

      console.log(`Peer ${peerId} joined room ${roomId}`);

    } catch (error) {
      console.error('Error joining room:', error);
      ws.send(JSON.stringify({
        type: 'error',
        error: error.message
      }));
    }
  }

  handleLeaveRoom(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    peer.leave();
    this.peers.delete(peerId);
  }

  async handleOffer(peerId, offerData) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleOffer(offerData);
  }

  async handleIceCandidate(peerId, candidateData) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleIceCandidate(candidateData);
  }

  async handleTrackRequest(peerId, request) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleTrackRequest(request);
  }

  handleLayoutUpdate(roomId, layout) {
    const room = this.rooms.get(roomId);
    if (!room) return;

    room.updateLayout(layout);
  }

  handleDisconnect(ws) {
    // Find and disconnect peer
    for (const [peerId, peer] of this.peers.entries()) {
      if (peer.ws === ws) {
        this.handleLeaveRoom(peerId);
        break;
      }
    }
  }
}

class SFURoom {
  constructor(roomId) {
    this.roomId = roomId;
    this.peers = new Map(); // peerId -> Peer instance
    this.tracks = new Map(); // trackId -> Track instance
    this.layout = 'grid'; // grid, speaker, custom
    this.maxVideoQuality = '1080p';
  }

  async addPeer(peer) {
    this.peers.set(peer.id, peer);

    // Send existing tracks to new peer
    for (const [trackId, track] of this.tracks.entries()) {
      await this.sendTrackToPeer(peer, track);
    }

    // Notify other peers about new peer
    this.broadcastToPeers({
      type: 'peer-joined',
      peerId: peer.id,
      metadata: peer.metadata
    }, peer.id);
  }

  removePeer(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Clean up peer's tracks
    for (const trackId of peer.trackIds) {
      this.tracks.delete(trackId);
    }

    // Remove peer from room
    this.peers.delete(peerId);

    // Notify other peers
    this.broadcastToPeers({
      type: 'peer-left',
      peerId: peerId
    });

    console.log(`Peer ${peerId} left room ${this.roomId}`);
  }

  async sendTrackToPeer(peer, track) {
    try {
      // Create transceiver for the track
      const transceiver = peer.pc.addTransceiver(track.track, {
        direction: 'sendonly'
      });

      // Store track mapping
      peer.trackMappings.set(track.id, {
        transceiver: transceiver,
        trackId: track.id
      });

      console.log(`Sent track ${track.id} to peer ${peer.id}`);

    } catch (error) {
      console.error(`Error sending track to peer ${peer.id}:`, error);
    }
  }

  addTrack(peerId, track) {
    const trackId = uuid.v4();
    const sfuTrack = new SFUTrack(trackId, peerId, track);

    this.tracks.set(trackId, sfuTrack);

    // Send track to all other peers
    this.broadcastToPeers({
      type: 'track-added',
      trackId: trackId,
      peerId: peerId,
      metadata: sfuTrack.metadata
    }, peerId);

    return sfuTrack;
  }

  removeTrack(trackId) {
    const track = this.tracks.get(trackId);
    if (!track) return;

    this.tracks.delete(trackId);

    // Notify all peers
    this.broadcastToPeers({
      type: 'track-removed',
      trackId: trackId
    });
  }

  updateLayout(layout) {
    this.layout = layout;

    // Recalculate track quality and simulcast layers
    this.optimizeTracks();

    // Notify peers about layout change
    this.broadcastToPeers({
      type: 'layout-updated',
      layout: layout
    });
  }

  optimizeTracks() {
    // Implement track optimization based on layout
    // Adjust quality levels, enable/disable simulcast layers
    console.log(`Optimizing tracks for layout: ${this.layout}`);
  }

  broadcastToPeers(message, excludePeerId = null) {
    for (const [peerId, peer] of this.peers.entries()) {
      if (peerId !== excludePeerId) {
        peer.send(message);
      }
    }
  }
}

class SFUPeer {
  constructor(id, ws, room, metadata = {}) {
    this.id = id;
    this.ws = ws;
    this.room = room;
    this.metadata = metadata;

    this.pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' }
      ]
    });

    this.trackIds = new Set();
    this.trackMappings = new Map();
    this.requestedTracks = new Set();

    this.setupPeerConnection();
    this.setupWebSocket();
  }

  setupPeerConnection() {
    // Handle incoming tracks
    this.pc.ontrack = (event) => {
      console.log(`Received track from peer ${this.id}:`, event.track);

      const trackId = uuid.v4();
      this.trackIds.add(trackId);

      // Add track to room
      this.room.addTrack(this.id, event.track);
    };

    // Handle ICE candidates
    this.pc.onicecandidate = (event) => {
      if (event.candidate) {
        this.send({
          type: 'ice-candidate',
          candidate: event.candidate
        });
      }
    };

    // Handle connection state
    this.pc.onconnectionstatechange = () => {
      console.log(`Peer ${this.id} connection state: ${this.pc.connectionState}`);
    };
  }

  setupWebSocket() {
    this.ws.on('close', () => {
      this.leave();
    });

    this.ws.on('error', (error) => {
      console.error(`WebSocket error for peer ${this.id}:`, error);
    });
  }

  async handleOffer(offerData) {
    try {
      const offer = new RTCSessionDescription(offerData);
      await this.pc.setRemoteDescription(offer);

      // Create answer
      const answer = await this.pc.createAnswer();
      await this.pc.setLocalDescription(answer);

      // Send answer back
      this.send({
        type: 'answer',
        answer: answer
      });

    } catch (error) {
      console.error(`Error handling offer for peer ${this.id}:`, error);
    }
  }

  async handleIceCandidate(candidateData) {
    try {
      const candidate = new RTCIceCandidate(candidateData);
      await this.pc.addIceCandidate(candidate);
    } catch (error) {
      console.error(`Error adding ICE candidate for peer ${this.id}:`, error);
    }
  }

  async handleTrackRequest(request) {
    const { trackId, quality } = request;

    if (!this.room.tracks.has(trackId)) {
      console.warn(`Track ${trackId} not found`);
      return;
    }

    // Adjust track quality based on request
    await this.adjustTrackQuality(trackId, quality);

    console.log(`Peer ${this.id} requested track ${trackId} with quality ${quality}`);
  }

  async adjustTrackQuality(trackId, quality) {
    const track = this.room.tracks.get(trackId);
    if (!track) return;

    // Implement quality adjustment logic
    // This could involve simulcast layer selection or bitrate adjustment
    console.log(`Adjusting track ${trackId} quality to ${quality}`);
  }

  send(message) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    }
  }

  leave() {
    console.log(`Peer ${this.id} leaving`);

    // Clean up peer connection
    if (this.pc) {
      this.pc.close();
    }

    // Remove from room
    this.room.removePeer(this.id);
  }
}

class SFUTrack {
  constructor(id, peerId, track) {
    this.id = id;
    this.peerId = peerId;
    this.track = track;
    this.metadata = {
      kind: track.kind,
      enabled: track.enabled,
      readyState: track.readyState
    };
    this.quality = 'auto'; // auto, high, medium, low
    this.simulcastLayers = [];
  }

  setQuality(quality) {
    this.quality = quality;
    // Implement actual quality adjustment
  }

  enable() {
    this.track.enabled = true;
    this.metadata.enabled = true;
  }

  disable() {
    this.track.enabled = false;
    this.metadata.enabled = false;
  }
}

// Usage example
const sfuServer = new SFUMediaServer();

console.log('SFU Media Server running on ws://localhost:3000');

// Add graceful shutdown
process.on('SIGINT', () => {
  console.log('Shutting down SFU server...');
  process.exit(0);
});

// Monitoring and statistics
class SFUMonitor {
  constructor(sfuServer) {
    this.sfu = sfuServer;

    // Add monitoring endpoint
    setInterval(() => {
      this.logStats();
    }, 30000); // Log stats every 30 seconds
  }

  logStats() {
    const stats = this.getStats();
    console.log('SFU Statistics:', stats);
  }

  getStats() {
    let totalPeers = 0;
    let totalTracks = 0;

    for (const room of this.sfu.rooms.values()) {
      totalPeers += room.peers.size;
      totalTracks += room.tracks.size;
    }

    return {
      rooms: this.sfu.rooms.size,
      peers: totalPeers,
      tracks: totalTracks,
      timestamp: new Date().toISOString()
    };
  }
}

const monitor = new SFUMonitor(sfuServer);
format
JavaScript
frameworks
["Node.js","WebSocket","WebRTC"]
features
["track-forwarding","quality-control","simulcast","room-management"]
protocols
["WebRTC","WebSocket"]

Outils

Outils souvent utilisés avec cet exemple

Associé

Collections d’exemples associées

Associé

Dossiers associés

Guide d'utilisation de l'outil

Contexte pour comprendre cette collection d’exemples

Titre

Exemples de Communication Temps Réel WebRTC

Description

Exemples complets WebRTC pour communication audio/vidéo P2P, canaux de données, partage d'écran et serveur de signalisation

Aperçu

Exemples complets WebRTC pour communication audio/vidéo P2P, canaux de données, partage d'écran et serveur de signalisation

Quand l’utiliser

  • Use this sample set when you want reference inputs for Exemples de Communication Temps Réel WebRTC.
  • Use it to preview output formats and compare related sample variants.

Fonctionnement

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Cas d’usage

  • Implémentation Appel Vidéo P2P
  • Communication Canal Données WebRTC
  • Implémentation Partage Écran
  • Serveur Signalisation WebRTC
  • Implémentation Serveur Médias SFU

FAQ

What is included in Exemples de Communication Temps Réel WebRTC?

This page includes curated sample entries, related tools, and neighboring sample collections for Exemples de Communication Temps Réel WebRTC.

Dossier

Déduire un JSON Schema depuis des exemples et des tableurs

Produisez un brouillon de JSON Schema relisible à partir d'exemples JSON représentatifs ou de lignes XLSX, puis examinez types, caractère obligatoire, valeurs nulles et anomalies avant toute adoption contractuelle.

Dossier

Capture autorisée de webhooks et contrôle de confiance du endpoint

Capturer et rejouer des requêtes webhook contrôlées, examiner les preuves de livraison et vérifier le certificat HTTPS du récepteur sans confondre certificat valide et confiance métier.

Dossier

Debogage d'Observabilite avec Logs, Traces, Webhooks et Charges JSON

Parsez les logs, decodez les traces, inspectez les preuves webhook/API, explorez les charges JSON et extrayez les motifs recurrents avant la note d'incident.

Dossier

Révision des notes de version pour le web, Slack et PDF

Vérifiez une version à partir de son journal des modifications, contrôlez la syntaxe et les références Markdown, puis préparez des notes cohérentes pour le web, Slack et PDF.

Accueil
Outils3201
Exemples593
Visualisations465
Visionneuses de fichiers103
Dossiers233
Étiquettes333
Images, audio et vidéo657

Outils de traitement, conversion et optimisation d’images, d’audio et de vidéo

Maths, dates et finance644

Calculateurs, outils numériques, logique de dates, statistiques et finance

Design et couleur297

Outils de couleur, mise en page, graphisme, style visuel et assistance design

Texte et rédaction209

Outils de rédaction, nettoyage, formatage, extraction et analyse de texte

Développement et Web193

Utilitaires développeur, réseau, débogage Web et assistants d’automatisation

Conversion et encodage175

Outils de conversion de formats, fichiers, archives, unités et encodages

Examen des sources FLAC/WAV et transmission des preuves
Revue et livraison d’un module de jeu promotionnel auto-hébergé
Relecture d'un lot de codes Stripe et contrôle préalable à l'importation
Tri technique des photos et transmission des candidates à la retouche
Structurer une capture d'interface, la relire et la transmettre au développement

© 2026 Elysia Tools. Tous droits réservés.

DocumentationTarifsÀ proposConditions d'utilisationPolitique de confidentialitéCookiesContact

Data Analysis

Detecteur d anomalies de series temporelles

Importe des donnees de series temporelles en CSV ou JSON, detecte les anomalies avec Z-Score et IQR et renvoie un rapport graphique

Data Analysis

Analyseur de prevision et de saisonnalite

Projette des periodes futures a partir de donnees CSV ou JSON et affiche tendance, saisonnalite et residus

Document Tools

Generateur PDF de programme fitness

Genere un PDF imprimable dentrainement depuis JSON ou CSV avec tableaux, cases de suivi et QR optionnels

Data Processing

Fusionneur JSON

Fusionne plusieurs fichiers JSON avec différentes stratégies (fusion profonde, écrasement, etc.)

Security

Générateur JWT

Générer des tokens JWT avec des revendications et algorithmes personnalisés

Format Conversion

Text to Excel Converter

Convert various text formats to Excel with customizable parsing and formatting options

Format Conversion

Text to JSON Converter

Convert various text formats to JSON with customizable parsing options

Document Tools

Tableau de Bord Ventes PDF

Cree un rapport visuel PDF avec graphiques, donnees et conclusions

Médias

Exemples de Codes QR

Images de codes QR d'exemple pour tester les outils de lecture et de génération de codes QR

Documents

Exemples PDF

Exemples PDF generes par les outils du 2026-02-01 au 2026-02-10

DevOps

Exemples d'Analyse de Logs ELK Stack

Exemples complets ELK Stack pour agrégation, traitement et visualisation de logs dans les systèmes distribués

C++

Exemples de Sérialisation Windows C++

Exemples de sérialisation de données Windows C++ incluant la sérialisation/désérialisation JSON et l'analyse XML

Médias

Exemples SVG

Exemples de graphiques vectoriels évolutifs (SVG) démontrant diverses fonctionnalités et techniques SVG

Technologies web

Applications Web Progressives (PWA)

Construisez des applications web progressives modernes, fiables et engageantes avec des capacités hors ligne