Elysia Tools

Explore ferramentas, exemplos, explicações visuais e guias temáticos em um espaço claro.

Categorias populares

Temas

Idioma
ZHESPTRUFRDEEN

Elysia Tools

Navegação

Tecnologias Web

Exemplos de Comunicação em Tempo Real WebRTC

Exemplos completos WebRTC para comunicação áudio/vídeo P2P, canais de dados, compartilhamento de tela e servidor de sinalização

Exemplos

Entradas desta coleção

CódigoIntermediário

Implementação de Chamada de Vídeo P2P

Configuração completa de chamada de vídeo WebRTC com acesso a câmera e áudio

Dificuldade

7/10

Tempo estimado

30 min

Etiquetas

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"]
CódigoIntermediário

Comunicação Canal de Dados WebRTC

Implementação de canais de dados para mensagens e transferência de arquivos em tempo real

Dificuldade

6/10

Tempo estimado

25 min

Etiquetas

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"]
CódigoIntermediário

Implementação Compartilhamento de Tela

Configuração completa de compartilhamento de tela com suporte para abas e áudio

Dificuldade

6/10

Tempo estimado

20 min

Etiquetas

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"]
CódigoComplexo

Servidor de Sinalização WebRTC

Servidor WebSocket para estabelecimento de conexões P2P e negociação de mídia

Dificuldade

8/10

Tempo estimado

35 min

Etiquetas

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"]
CódigoComplexo

Implementação Servidor de Mídia SFU

Arquitetura SFU para videoconferências escaláveis com múltiplos participantes

Dificuldade

9/10

Tempo estimado

45 min

Etiquetas

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"]

Ferramentas

Ferramentas frequentemente usadas com este exemplo

Relacionado

Coleções de exemplos relacionadas

Relacionado

Temas relacionados

Guia de uso da ferramenta

Contexto para entender esta coleção de exemplos

Título

Exemplos de Comunicação em Tempo Real WebRTC

Descrição

Exemplos completos WebRTC para comunicação áudio/vídeo P2P, canais de dados, compartilhamento de tela e servidor de sinalização

Visão geral

Exemplos completos WebRTC para comunicação áudio/vídeo P2P, canais de dados, compartilhamento de tela e servidor de sinalização

Quando usar

  • Use this sample set when you want reference inputs for Exemplos de Comunicação em Tempo Real WebRTC.
  • Use it to preview output formats and compare related sample variants.

Como funciona

  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.

Casos de uso

  • Implementação de Chamada de Vídeo P2P
  • Comunicação Canal de Dados WebRTC
  • Implementação Compartilhamento de Tela
  • Servidor de Sinalização WebRTC
  • Implementação Servidor de Mídia SFU

Perguntas frequentes

What is included in Exemplos de Comunicação em Tempo Real WebRTC?

This page includes curated sample entries, related tools, and neighboring sample collections for Exemplos de Comunicação em Tempo Real WebRTC.

Tema

Fluxos de coordenadas, tempo e órbita astronômica

Converta coordenadas celestes, fixe a escala temporal de observação e resolva posições ou órbitas com limites de precisão explícitos.

Tema

Ferramentas de pitch, tempo, loops e reprodução reversa de áudio

Ajuste pitch, tempo, velocidade, batidas, loops, reversão e stutter em um remix de áudio com intenção clara.

Tema

Depuracao de Observabilidade com Logs, Traces, Webhooks e Payloads JSON

Analise logs, decodifique traces, inspecione evidencias webhook/API, explore payloads JSON e extraia padroes recorrentes antes do resumo de incidente.

Tema

Contagem de células, viabilidade e acompanhamento do crescimento

Converta contagens brutas dos quadrados de uma câmara de Neubauer em células por mililitro, calcule a viabilidade por azul de tripan junto às concentrações de vivas e mortas, converta a densidade entre os dialetos que os protocolos realmente usam e derive o tempo de duplicação de duas contagens em fase exponencial.

Início
Ferramentas3201
Exemplos593
Visualizações465
Visualizadores de arquivos103
Temas233
Etiquetas333
Imagens, áudio e vídeo657

Ferramentas de processamento, conversão e otimização de imagens, áudio e vídeo

Matemática, datas e finanças644

Calculadoras, ferramentas numéricas, lógica de datas, estatística e finanças

Design e cor297

Ferramentas de cor, layout, gráficos, estilo visual e apoio ao design

Texto e escrita209

Ferramentas de escrita, limpeza, formatação, extração e análise de texto

Desenvolvimento e Web193

Utilitários para desenvolvedores, rede, depuração web e automação

Conversão e codificação175

Ferramentas de conversão de formatos, arquivos, unidades, arquivos compactados e codificação

Triagem de fontes FLAC/WAV e entrega de evidências
Revisão e entrega de um componente de sorteio auto-hospedado
Revisão de lote de códigos Stripe e pré-verificação da importação
Revisão da seleção técnica de fotos e entrega de candidatas à edição
Estruturar uma captura de interface, revisar e entregar ao desenvolvimento

© 2026 Elysia Tools. Todos os direitos reservados.

DocumentaçãoPreçosSobreTermos de serviçoPolítica de privacidadeCookiesContato

Data Analysis

Detector de anomalias em series temporais

Envia dados de serie temporal em CSV ou JSON, detecta anomalias com Z-Score e IQR e retorna um relatorio com grafico

Data Analysis

Analisador de previsao e sazonalidade

Projeta periodos futuros a partir de series temporais CSV ou JSON e mostra tendencia, sazonalidade e residuo

Document Tools

Gerador PDF de treino fitness

Gera um PDF imprimivel de treino a partir de JSON ou CSV com tabelas, caixas de progresso e QR opcionais

Data Processing

Fusionador de JSON

Combina múltiplos arquivos JSON com várias estratégias (fusão profunda, sobrescrever, etc.)

Security

Gerador JWT

Gerar tokens JWT com claims e algoritmos personalizados

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

Dashboard de Vendas PDF

Cria um relatorio visual PDF com graficos, dados e conclusoes

Mídia

Exemplos de Códigos QR

Imagens de código QR de exemplo para testar ferramentas de leitura e geração de código QR

Documentos

Exemplos PDF

Exemplos PDF gerados por ferramentas de 2026-02-01 a 2026-02-10

DevOps

Exemplos de Análise de Logs ELK Stack

Exemplos completos de ELK Stack para agregação, processamento e visualização de logs em sistemas distribuídos

C++

Exemplos de Serialização Windows C++

Exemplos de serialização de dados Windows C++ incluindo serialização/deserialização JSON e análise XML

Mídia

Exemplos SVG

Exemplos de Gráficos Vetoriais Escaláveis (SVG) demonstrando vários recursos e técnicas SVG

Tecnologias Web

Aplicações Web Progressivas (PWA)

Construa aplicações web progressivas modernas, confiáveis e envolventes com capacidades offline