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.