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.
Caminho recomendado: SDK JavaScript
Prefira o SDK JavaScript @videochamada/embed: ele monta o iframe e faz
toda a comunicação postMessage descrita nesta página por você, com métodos e eventos prontos.
Use esta página apenas se quiser implementar o protocolo manualmente.
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://sua-organizacao.videochamada.com.br/chamada/CALL_ID?username=NomeUsuario"
width="800"
height="600"
allow="camera; microphone; display-capture"
frameborder="0">
</iframe>
Permissões necessárias:
camera- Acesso à câmeramicrophone- Acesso ao microfonedisplay-capture- Compartilhamento de tela
API de Comandos
Enviando Comandos para o Iframe
Use postMessage para enviar comandos para a videochamada:
const iframe = document.getElementById('videocall-iframe');
// Origem da chamada — sempre use-a como targetOrigin, nunca '*'
const CALL_ORIGIN = 'https://sua-organizacao.videochamada.com.br';
// Formato do comando
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'ACTION_NAME',
data: {} // Dados opcionais
}, CALL_ORIGIN);
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 | - |
setTheme |
Aplica tokens de tema (white-label) na interface da chamada | { background, accent, ... } — veja os tokens disponíveis |
endCall |
Encerra a chamada | - |
Exemplos de Comandos
Controles de Áudio e Vídeo
// Ligar/desligar microfone
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'toggleAudio'
}, CALL_ORIGIN);
// Ligar/desligar câmera
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'toggleVideo'
}, CALL_ORIGIN);
// Compartilhar/parar tela
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'toggleScreenShare'
}, CALL_ORIGIN);
Controles de Chat
// Abrir chat
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'openChat'
}, CALL_ORIGIN);
// Fechar chat
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'closeChat'
}, CALL_ORIGIN);
Configurações e Interface
// Abrir dialog de configurações de dispositivos
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'openSettings'
}, CALL_ORIGIN);
// Abrir dialog de seleção de layout
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'openLayoutDialog'
}, CALL_ORIGIN);
// Alternar visibilidade do status de conexão
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'toggleConnectionStatus'
}, CALL_ORIGIN);
// Solicitar status atual (sem executar ações)
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'getStatus'
}, CALL_ORIGIN);
Interação
// Levantar/baixar mão
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'toggleRaiseHand'
}, CALL_ORIGIN);
Layout
// Mudar para layout mosaico (direto, sem dialog)
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'changeLayout',
data: { layout: 'mosaic' }
}, CALL_ORIGIN);
// Mudar para layout sidebar (direto, sem dialog)
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'changeLayout',
data: { layout: 'sidebar' }
}, CALL_ORIGIN);
Controle de Chamada
// Encerrar chamada
iframe.contentWindow.postMessage({
type: 'videocall-command',
action: 'endCall'
}, CALL_ORIGIN);
Recebendo Eventos e Status
Listener de Eventos
Configure um listener para receber atualizações da videochamada:
const CALL_ORIGIN = 'https://sua-organizacao.videochamada.com.br';
window.addEventListener('message', (event) => {
// Ignorar mensagens que não venham da chamada (segurança)
if (event.origin !== CALL_ORIGIN) 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 }
}
// Participante entrou sem microfone disponível
{
type: 'videocall-event',
event: 'audioUnavailable',
data: { microphoneCount: number, attempt: number }
}
// Participante confirmou a entrada sem áudio
{
type: 'videocall-event',
event: 'joinedWithoutAudio',
data: { attempt: number }
}
Campo timestamp
Todas as mensagens videocall-status e videocall-event enviadas pela chamada
incluem também um campo timestamp (epoch em milissegundos).
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://sua-organizacao.videochamada.com.br/chamada/CALL_ID?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('videocall-frame');
const CALL_ORIGIN = 'https://sua-organizacao.videochamada.com.br';
let currentStatus = {};
// Escutar mensagens do iframe
window.addEventListener('message', (event) => {
// Ignorar mensagens que não venham da chamada (segurança)
if (event.origin !== CALL_ORIGIN) 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()
}, CALL_ORIGIN);
}
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:
// o subdomínio da sua organização e, se configurado, o seu domínio personalizado
const trustedOrigins = [
'https://sua-organizacao.videochamada.com.br',
'https://chamadas.suaempresa.com.br' // domínio personalizado (opcional)
];
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
const CALL_ORIGIN = 'https://sua-organizacao.videochamada.com.br';
class VideochamadaController {
constructor(iframeId) {
this.iframe = document.getElementById(iframeId);
this.status = {};
this.setupListeners();
}
setupListeners() {
window.addEventListener('message', (event) => {
// Ignorar mensagens que não venham da chamada (segurança)
if (event.origin !== CALL_ORIGIN) return;
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
}, CALL_ORIGIN);
}
// 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';
const CALL_ORIGIN = 'https://sua-organizacao.videochamada.com.br';
function VideochamadaEmbed({ callId, username }) {
const iframeRef = useRef(null);
const [status, setStatus] = useState({});
useEffect(() => {
const handleMessage = (event) => {
// Ignorar mensagens que não venham da chamada (segurança)
if (event.origin !== CALL_ORIGIN) return;
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
}, CALL_ORIGIN);
}
};
return (
<div>
<iframe
ref={iframeRef}
src={`https://sua-organizacao.videochamada.com.br/chamada/${callId}?username=${encodeURIComponent(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
- Chat em Mobile/Iframe: Em dispositivos móveis, o painel de chat abre em modo overlay sobre o vídeo. Use os comandos
openChatecloseChatpara controlar programaticamente
Notas sobre Mobile
Ao integrar em dispositivos móveis:
- Em telas pequenas, o painel de chat abre automaticamente em modo overlay, sobreposto ao vídeo
- Use os comandos
openChat/closeChatpara controlar o estado do chat programaticamente - O status
chatOpenno objeto de status indica se o chat está aberto - Para uma melhor experiência mobile, considere criar controles customizados na sua aplicação que utilizem a API de comandos
Suporte
Para dúvidas sobre a integração, entre em contato com o suporte técnico ou consulte a documentação completa da API.