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.