Pular para conteúdo

Controle de Videochamada em Modo Embed

Visão Geral

A plataforma Videochamada.com.br oferece suporte para integração de videochamadas em aplicações de terceiros através de iframe, com controle completo via API JavaScript usando postMessage. Este modo permite ocultar os controles visuais da interface e gerenciar todas as funcionalidades através de comandos externos.

Configuração Inicial

1. Desabilitar Controles Visuais

Para ocultar a barra de controles da videochamada, configure a opção enableCallControlBar como false nas configurações do projeto através do dashboard administrativo.

Quando esta flag está desabilitada: - Todos os botões de controle são ocultados - A videochamada continua funcionando normalmente - O controle é feito exclusivamente via API JavaScript

2. Incorporar o Iframe

<iframe
  id="videocall-iframe"
  src="https://videochamada.com/call/CALL_ID?username=NomeUsuario"
  width="800"
  height="600"
  allow="camera; microphone; display-capture"
  frameborder="0">
</iframe>

Permissões necessárias: - camera - Acesso à câmera - microphone - Acesso ao microfone - display-capture - Compartilhamento de tela

API de Comandos

Enviando Comandos para o Iframe

Use postMessage para enviar comandos para a videochamada:

const iframe = document.getElementById('neft-videocall');

// Formato do comando
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'ACTION_NAME',
  data: {} // Dados opcionais
}, '*');

Lista Completa de Comandos

Comando Descrição Parâmetros
toggleAudio Liga/desliga microfone -
toggleVideo Liga/desliga câmera -
toggleScreenShare Inicia/para compartilhamento de tela -
openChat Abre o painel de chat -
closeChat Fecha o painel de chat -
openSettings Abre dialog de configurações de dispositivos -
openLayoutDialog Abre dialog de seleção de layout -
toggleConnectionStatus Mostra/oculta status de conexão -
toggleRaiseHand Levanta/baixa a mão -
changeLayout Altera layout diretamente { layout: 'mosaic' \| 'sidebar' }
getStatus Solicita status atual -
endCall Encerra a chamada -

Exemplos de Comandos

Controles de Áudio e Vídeo

// Ligar/desligar microfone
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'toggleAudio'
}, '*');

// Ligar/desligar câmera
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'toggleVideo'
}, '*');

// Compartilhar/parar tela
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'toggleScreenShare'
}, '*');

Controles de Chat

// Abrir chat
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'openChat'
}, '*');

// Fechar chat
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'closeChat'
}, '*');

Configurações e Interface

// Abrir dialog de configurações de dispositivos
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'openSettings'
}, '*');

// Abrir dialog de seleção de layout
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'openLayoutDialog'
}, '*');

// Alternar visibilidade do status de conexão
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'toggleConnectionStatus'
}, '*');

// Solicitar status atual (sem executar ações)
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'getStatus'
}, '*');

Interação

// Levantar/baixar mão
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'toggleRaiseHand'
}, '*');

Layout

// Mudar para layout mosaico (direto, sem dialog)
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'changeLayout',
  data: { layout: 'mosaic' }
}, '*');

// Mudar para layout sidebar (direto, sem dialog)
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'changeLayout',
  data: { layout: 'sidebar' }
}, '*');

Controle de Chamada

// Encerrar chamada
iframe.contentWindow.postMessage({
  type: 'videocall-command',
  action: 'endCall'
}, '*');

Recebendo Eventos e Status

Listener de Eventos

Configure um listener para receber atualizações da videochamada:

window.addEventListener('message', (event) => {
  // Verificar origem por segurança (recomendado)
  // if (event.origin !== 'https://videochamada.com') return;

  if (event.data.type === 'videocall-status') {
    // Status completo da chamada
    console.log('Status:', event.data.status);
  }

  if (event.data.type === 'videocall-event') {
    // Evento específico
    console.log('Evento:', event.data.event, event.data.data);
  }
});

Estrutura do Status

O objeto de status contém:

{
  audioEnabled: boolean,           // Microfone ativo
  videoEnabled: boolean,           // Câmera ativa
  screenShareEnabled: boolean,     // Compartilhamento ativo
  chatOpen: boolean,               // Chat aberto
  handRaised: boolean,             // Mão levantada
  layoutMode: string,              // 'mosaic' ou 'sidebar'
  participantCount: number,        // Número de participantes
  isRecording: boolean,            // Gravação ativa
  connectionStatusVisible: boolean // Status de conexão visível
}

Eventos Específicos

Eventos emitidos quando ações ocorrem:

// Áudio alternado
{
  type: 'videocall-event',
  event: 'audioToggled',
  data: { enabled: true/false }
}

// Vídeo alternado
{
  type: 'videocall-event',
  event: 'videoToggled',
  data: { enabled: true/false }
}

// Compartilhamento de tela alternado
{
  type: 'videocall-event',
  event: 'screenShareToggled',
  data: { enabled: true/false }
}

// Participante entrou
{
  type: 'videocall-event',
  event: 'participantJoined',
  data: { participantId: string, name: string }
}

// Participante saiu
{
  type: 'videocall-event',
  event: 'participantLeft',
  data: { participantId: string }
}

// Chamada encerrada
{
  type: 'videocall-event',
  event: 'callEnded',
  data: { reason: string }
}

Exemplo Completo

<!DOCTYPE html>
<html>
<head>
  <title>Integração Videochamada</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      margin: 0;
      padding: 20px;
      background: #f5f5f5;
    }

    .video-container {
      background: #000;
      border-radius: 8px;
      overflow: hidden;
      margin-bottom: 20px;
    }

    .controls {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-bottom: 20px;
    }

    button {
      padding: 10px 20px;
      border: none;
      border-radius: 5px;
      background: #007bff;
      color: white;
      cursor: pointer;
      font-size: 16px;
      transition: background 0.2s;
    }

    button:hover {
      background: #0056b3;
    }

    button:active {
      transform: scale(0.98);
    }

    button.danger {
      background: #dc3545;
    }

    button.danger:hover {
      background: #c82333;
    }

    #status {
      background: white;
      padding: 15px;
      border-radius: 5px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }

    #status p {
      margin: 5px 0;
      font-size: 14px;
    }

    .status-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 10px;
    }
  </style>
</head>
<body>
  <h1>Controle de Videochamada</h1>

  <div class="video-container">
    <iframe
      id="videocall-frame"
      src="https://videochamada.com/call/123?username=Usuario"
      width="100%"
      height="600"
      allow="camera; microphone; display-capture"
      frameborder="0">
    </iframe>
  </div>

  <div class="controls">
    <button onclick="toggleAudio()">🎤 Microfone</button>
    <button onclick="toggleVideo()">📹 Câmera</button>
    <button onclick="toggleScreen()">🖥️ Tela</button>
    <button onclick="openSettings()">⚙️ Configurações</button>
    <button onclick="toggleChat()">💬 Chat</button>
    <button onclick="raiseHand()">✋ Mão</button>
    <button onclick="changeLayoutMode()">🎨 Layout</button>
    <button onclick="toggleConnectionStatus()">📡 Status Conexão</button>
    <button onclick="getStatus()">📊 Atualizar Status</button>
    <button class="danger" onclick="endCall()">📞 Encerrar</button>
  </div>

  <div id="status">
    <h3>Status da Chamada</h3>
    <div class="status-grid"></div>
  </div>

  <script>
    const iframe = document.getElementById('neft-call');
    let currentStatus = {};

    // Escutar mensagens do iframe
    window.addEventListener('message', (event) => {
      // Verificar origem por segurança
      // if (event.origin !== 'https://videochamada.com') return;

      if (event.data.type === 'videocall-status') {
        currentStatus = event.data.status;
        updateUI();
      }

      if (event.data.type === 'videocall-event') {
        console.log(`[Evento] ${event.data.event}:`, event.data.data);
      }
    });

    // Funções de controle
    function sendCommand(action, data = null) {
      iframe.contentWindow.postMessage({
        type: 'videocall-command',
        action: action,
        data: data,
        timestamp: Date.now()
      }, '*');
    }

    function toggleAudio() {
      sendCommand('toggleAudio');
    }

    function toggleVideo() {
      sendCommand('toggleVideo');
    }

    function toggleScreen() {
      sendCommand('toggleScreenShare');
    }

    function openSettings() {
      sendCommand('openSettings');
    }

    function toggleChat() {
      if (currentStatus.chatOpen) {
        sendCommand('closeChat');
      } else {
        sendCommand('openChat');
      }
    }

    function raiseHand() {
      sendCommand('toggleRaiseHand');
    }

    function changeLayoutMode() {
      // Alterna entre mosaico e sidebar
      const newLayout = currentStatus.layoutMode === 'mosaic' ? 'sidebar' : 'mosaic';
      sendCommand('changeLayout', { layout: newLayout });
    }

    function toggleConnectionStatus() {
      sendCommand('toggleConnectionStatus');
    }

    function getStatus() {
      sendCommand('getStatus');
    }

    function endCall() {
      if (confirm('Deseja encerrar a chamada?')) {
        sendCommand('endCall');
      }
    }

    function updateUI() {
      const statusGrid = document.querySelector('.status-grid');
      statusGrid.innerHTML = `
        <p>🎤 Microfone: ${currentStatus.audioEnabled ? '✅ Ativo' : '❌ Mudo'}</p>
        <p>📹 Câmera: ${currentStatus.videoEnabled ? '✅ Ligada' : '❌ Desligada'}</p>
        <p>🖥️ Tela: ${currentStatus.screenShareEnabled ? '✅ Compartilhando' : '❌ Não compartilhando'}</p>
        <p>💬 Chat: ${currentStatus.chatOpen ? '✅ Aberto' : '❌ Fechado'}</p>
        <p>✋ Mão: ${currentStatus.handRaised ? '✅ Levantada' : '❌ Baixada'}</p>
        <p>🎨 Layout: ${currentStatus.layoutMode === 'sidebar' ? '📑 Barra lateral' : '⚏ Mosaico'}</p>
        <p>👥 Participantes: ${currentStatus.participantCount || 0}</p>
        <p>🔴 Gravação: ${currentStatus.isRecording ? '✅ Gravando' : '❌ Não gravando'}</p>
        <p>📡 Status: ${currentStatus.connectionStatusVisible ? '✅ Visível' : '❌ Oculto'}</p>
      `;
    }

    // Solicitar status inicial após 1 segundo
    setTimeout(() => {
      sendCommand('getStatus');
    }, 1000);
  </script>
</body>
</html>

Segurança

Verificação de Origem

Sempre verifique a origem das mensagens recebidas:

window.addEventListener('message', (event) => {
  // Verificar se a mensagem vem do domínio esperado
  const trustedOrigins = [
    'https://videochamada.com',
    'https://app.videochamada.com'
  ];

  if (!trustedOrigins.includes(event.origin)) {
    console.warn('Mensagem de origem não confiável:', event.origin);
    return;
  }

  // Processar mensagem...
});

Validação de Comandos

O iframe valida todos os comandos recebidos e ignora ações inválidas ou não autorizadas.

Casos de Uso Avançados

Controle Programático

class VideochamadaController {
  constructor(iframeId) {
    this.iframe = document.getElementById(iframeId);
    this.status = {};
    this.setupListeners();
  }

  setupListeners() {
    window.addEventListener('message', (event) => {
      if (event.data.type === 'videocall-status') {
        this.status = event.data.status;
        this.onStatusUpdate(this.status);
      }
    });
  }

  sendCommand(action, data = null) {
    this.iframe.contentWindow.postMessage({
      type: 'videocall-command',
      action: action,
      data: data
    }, '*');
  }

  // Métodos de conveniência
  mute() {
    if (this.status.audioEnabled) {
      this.sendCommand('toggleAudio');
    }
  }

  unmute() {
    if (!this.status.audioEnabled) {
      this.sendCommand('toggleAudio');
    }
  }

  startScreenShare() {
    if (!this.status.screenShareEnabled) {
      this.sendCommand('toggleScreenShare');
    }
  }

  stopScreenShare() {
    if (this.status.screenShareEnabled) {
      this.sendCommand('toggleScreenShare');
    }
  }

  onStatusUpdate(status) {
    // Override este método para reagir a mudanças
    console.log('Status atualizado:', status);
  }
}

// Uso
const controller = new VideochamadaController('videocall-frame');
controller.mute();
controller.startScreenShare();

Integração com Frameworks

React

import React, { useEffect, useRef, useState } from 'react';

function VideochamadaEmbed({ callId, username }) {
  const iframeRef = useRef(null);
  const [status, setStatus] = useState({});

  useEffect(() => {
    const handleMessage = (event) => {
      if (event.data.type === 'videocall-status') {
        setStatus(event.data.status);
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);

  const sendCommand = (action, data = null) => {
    if (iframeRef.current) {
      iframeRef.current.contentWindow.postMessage({
        type: 'videocall-command',
        action,
        data
      }, '*');
    }
  };

  return (
    <div>
      <iframe
        ref={iframeRef}
        src={`https://videochamada.com/call/${callId}?username=${username}`}
        width="100%"
        height="600"
        allow="camera; microphone; display-capture"
      />

      <div className="controls">
        <button onClick={() => sendCommand('toggleAudio')}>
          {status.audioEnabled ? 'Mutar' : 'Desmutar'}
        </button>
        <button onClick={() => sendCommand('toggleVideo')}>
          {status.videoEnabled ? 'Desligar Câmera' : 'Ligar Câmera'}
        </button>
      </div>
    </div>
  );
}

Limitações

  • O controle via API funciona independentemente da configuração enableCallControlBar
  • Algumas funcionalidades podem estar desabilitadas no projeto (ex: chat, gravação)
  • O usuário ainda precisa conceder permissões de câmera/microfone ao navegador
  • Dialogs (configurações, layout) são modais e bloqueiam interação até serem fechados

Suporte

Para dúvidas sobre a integração, entre em contato com o suporte técnico ou consulte a documentação completa da API.