Gravação de videochamadas
Captura opcional, MP4 lado a lado, limites de processamento e integração no Atendro.
O AtendroZAP pode capturar os dois sentidos de uma videochamada e gerar um MP4 após seu encerramento. É uma opção do servidor, desligada por padrão, anunciada em capabilities.calls_video_recording. A gravação WAV existente continua disponível com seu contrato e ciclo de vida próprios.
O MP4 mostra atendente à esquerda e contato à direita, em 1280×360 a 25 fps, com vídeo H.264 e áudio AAC estéreo: voz do atendente à esquerda e do contato à direita. Proporções são preservadas com margens pretas. Câmera desligada deixa o espaço correspondente preto. O console exibe player e download na aba Histórico de chamadas, depois do processamento.
Habilitar no servidor
Esta configuração destina-se à homologação. A ativação em produção depende da validação com chamadas reais, orientação e consumo de recursos descrita ao final deste guia.
São necessários os módulos de chamadas, vídeo e gravação de áudio:
ATENDROZAP_CALLS_ENABLED=true
ATENDROZAP_CALLS_VIDEO_ENABLED=true
ATENDROZAP_CALLS_RECORDING_ENABLED=true
ATENDROZAP_CALLS_RECORDING_DIR=/call-recordings
ATENDROZAP_CALLS_RECORDING_RETENTION_DAYS=30
ATENDROZAP_CALLS_VIDEO_RECORDING_ENABLED=true
ATENDROZAP_CALLS_VIDEO_RECORDING_RETENTION_DAYS=7A ativação é global por worker: as videochamadas das instâncias calls existentes e das cadastradas depois passam a ser capturadas automaticamente. Não há habilitação individual por instância. Chamadas anteriores à ativação não ganham gravação retroativa.
Também são necessários os executáveis ffmpeg e ffprobe. A imagem padrão api permanece sem eles. O target opcional api-video inclui FFmpeg 8.1.2 e libx264; essa composição tem licença GPL-2.0-or-later e revisão própria. Não basta ligar a variável numa imagem que não contém os executáveis. Consulte a revisão de licença do repositório antes de distribuir a imagem.
Depois de finalizar o commit, o fluxo do repositório pode preparar as imagens no host autorizado:
deploy/ship.sh HOST REV api-videoHOST é o alias SSH autorizado e REV é a revisão Git revisada. Esse comando envia o código versionado e constrói as imagens da API e do console; não reinicia os containers. A construção consome recursos do host e deve ser feita na janela de preparação acordada. Ativação da imagem e da flag ocorre em etapa posterior, após homologação e autorização operacional.
O serviço valida a combinação de módulos e resolve os executáveis no startup. Habilitar a opção exige publicar a imagem adequada e reiniciar a API em uma janela operacional. Esta documentação não informa que a opção já esteja ativa em produção.
| Variável | Padrão | Valores aceitos |
|---|---|---|
ATENDROZAP_CALLS_VIDEO_RECORDING_ENABLED | false | Booleano; requer vídeo e gravação de áudio |
ATENDROZAP_CALLS_VIDEO_RECORDING_RETENTION_DAYS | 7 | 1–3650 dias desde o encerramento; independente da retenção de áudio |
ATENDROZAP_CALLS_VIDEO_RECORDING_INVERT_REMOTE_ORIENTATION | false | true troca 90°/270° somente no cliente remoto do MP4, compatível com o ajuste invert=true do Atendro |
ATENDROZAP_CALLS_VIDEO_RECORDING_FFMPEG | ffmpeg | Executável ou caminho local |
ATENDROZAP_CALLS_VIDEO_RECORDING_FFPROBE | ffprobe | Executável ou caminho local |
ATENDROZAP_CALLS_VIDEO_RECORDING_MAX_CONCURRENT | 1 | 1–8 renders ativos por worker |
ATENDROZAP_CALLS_VIDEO_RECORDING_MAX_PENDING | 8 | 1–128 renders esperando por worker |
ATENDROZAP_CALLS_VIDEO_RECORDING_MAX_BYTES | 1073741824 (1 GiB) | 1 MiB–16 GiB; limite da captura H.264 e, separadamente, do MP4 |
ATENDROZAP_CALLS_VIDEO_RECORDING_QUEUE_BYTES | 16777216 (16 MiB) | 1–256 MiB de quadros aguardando escrita por chamada |
ATENDROZAP_CALLS_VIDEO_RECORDING_RENDER_TIMEOUT | 30m | 1 segundo–6 horas; inclui espera por áudio e por vaga de render |
Os limites de render não alteram callsMaxConcurrent: uma chamada encerrada pode continuar gerando seu MP4 enquanto outra chamada usa a vaga de ligação. Há também limite de entradas na fila de captura. Fila ou disco indisponível produzem falha explícita na gravação; o gravador não bloqueia a mídia. Limites de fila não substituem limites de CPU e memória do processo.
Captura, sincronização e retomada
Durante a chamada, o motor observa H.264 já codificado, timestamps RTP, orientação e mudanças de câmera. A captura usa uma fila separada e realiza escrita fora dos goroutines de mídia. O relógio de cada fluxo é alinhado à linha do tempo da chamada, preservando intervalos sem câmera; não se concatena H.264 bruto supondo uma taxa fixa de quadros.
O observador pertence à chamada e permanece durante a substituição do socket do navegador. Uma retomada da ponte não inicia uma segunda gravação nem zera a linha do tempo. O material pode começar apenas no primeiro keyframe decodificável. A composição exige SPS/PPS e frames válidos; captura sem keyframe pode terminar como falha.
O MP4 só é anunciado como ready após inspeção das trilhas e decodificação integral sem erros. Uma saída truncada ou corrompida não é publicada como gravação pronta. Nesta versão, timestamps de apresentação que exigem reordenação de quadros H.264 geram unsupported_video_reordering; não são descartados silenciosamente nem recebem tempos inventados.
A rotação padrão usa o contrato CVO atual: quartos de volta no sentido horário. O Atendro pode aplicar uma correção adicional ao desenhar a câmera do cliente; seu controller atualmente inicia com invert=true quando não há preferência salva. Para usar a mesma convenção na gravação, habilitar ATENDROZAP_CALLS_VIDEO_RECORDING_INVERT_REMOTE_ORIENTATION=true no worker. A composição passa a usar (4-orientação)&3 apenas no vídeo remoto: troca 90°/270° e preserva 0°/180°. O lado local, metadados da API, captura e ponte ao vivo mantêm seus contratos. O ajuste ocorre durante o processamento offline e não acrescenta uma etapa ao caminho de mídia da chamada.
Essa opção não acompanha cliques manuais de rotação ou preferências locais do navegador. Ela deve corresponder à convenção de exibição da integração. Os testes sintéticos cobrem as quatro orientações, mudanças durante a chamada e quadros sem metadado novo; Android/iPhone reais e sincronização em chamadas prolongadas ainda precisam de homologação. Os segmentos originais são removidos depois do processamento; MP4 já gerados não são corrigidos automaticamente, pois a orientação de cada trecho não fica registrada no arquivo composto.
Estado e download
GET /call/{id} e GET /call/history podem incluir videoRecording:
{
"callId": "CHAMADA_DE_TESTE",
"videoRecording": {
"status": "ready",
"contentType": "video/mp4",
"bytes": 1048576,
"durationSeconds": 120,
"downloadPath": "/call/CHAMADA_DE_TESTE/recording/video",
"expiresAt": "2026-10-19T15:02:00Z"
}
}O exemplo é um recorte sintético. O objeto recording continua representando somente o WAV. video: true indica vídeo negociado na chamada; sozinho não garante que um MP4 tenha sido gravado. videoRecording ausente significa que não existe registro de captura de vídeo para aquela chamada, incluindo chamadas anteriores à ativação ou sem quadros capturados.
videoRecording.status | Significado para a interface |
|---|---|
recording | Capturando ou finalizando o material da chamada |
processing | Compondo o MP4 ou esperando vaga de processamento |
ready | Player e download disponíveis |
failed | Vídeo indisponível; failureReason informa uma categoria técnica |
expired | Retenção encerrada; preservar o histórico |
Não é obrigatório observar todos os estados intermediários. Uma falha pode ocorrer durante captura ou processamento. O primeiro resultado terminal é preservado; a expiração por retenção continua sendo aplicada a um arquivo pronto.
GET /call/{id}/recording/video usa exclusivamente o header token. Não aceita token na query. O backend do Atendro deve autenticar seu usuário, verificar a empresa e a instância e fazer proxy do arquivo, preservando Range e If-Range e as respostas 206/416. O navegador não recebe a credencial da instância. O arquivo usa Content-Type: video/mp4 e Cache-Control: private, no-store.
O endpoint devolve 404 recording_not_available enquanto não estiver pronto, 410 recording_expired após a retenção e 503 se o arquivo estiver indisponível ou pertencer a outro worker. O histórico continua consultável sem sessão WhatsApp conectada. O download precisa chegar ao worker dono do arquivo; não existe transferência automática de gravações entre workers.
Eventos e integração no Atendro
Assine call, valide a assinatura do envelope existente e identifique a chamada por (serverId, instanceId, data.callId). Os novos subeventos são video_recording_ready e video_recording_failed:
{
"EventType": "call",
"event": "call",
"eventId": "ID_DO_EVENTO",
"instanceId": "ID_DA_INSTANCIA",
"seq": 42,
"data": {
"callId": "CHAMADA_DE_TESTE",
"event": "video_recording_ready",
"videoRecording": {
"status": "ready",
"contentType": "video/mp4",
"durationMs": 120000,
"bytes": 1048576,
"failureReason": ""
},
"timestamp": 1789830120
}
}Esse também é um recorte sintético. Falhas usam o mesmo formato, com event: video_recording_failed, status: failed e uma categoria em failureReason. O evento não traz URL pública nem credencial. O webhook usa durationMs; o histórico usa durationSeconds.
Esses eventos atualizam apenas a gravação de vídeo. Não são ended, não mudam o motivo de encerramento, não alteram vagas de simultaneidade e não substituem recording do áudio. Não exigem data.status da chamada. Podem ser processados antes ou depois de ended; preserve cada domínio separado. Deduplicar por eventId e usar seq do envelope, não data.seq.
Prompt para implementar no atendro-ai
Implemente a gravação de videochamadas do provedor AtendroZAP no atendro-ai.
Leia o contrato em /docs/guias/captura-video no servidor de documentação.
1. Preserve todo o fluxo atual de chamadas e gravação WAV. Consulte a
capability opcional calls_video_recording para apresentar suporte à
captura; gravações antigas prontas continuam acessíveis se a flag mudar.
2. Persista videoRecording separadamente de recording, por servidor,
instância e callId. Aceite recording, processing, ready, failed e expired.
Ausência do objeto não pode apagar dados conhecidos nem sugerir MP4 pronto.
3. No receptor assinado EventType=call, trate data.event
video_recording_ready/video_recording_failed como atualização independente.
Deduplicate por eventId. Esses eventos não são ended e nunca liberam vaga,
encerram atendimento ou sobrescrevem endReason/endedBy. Podem chegar antes
do ended; faça upsert sem inventar estado terminal da chamada.
4. Reconcilie com GET /call/{id} ou GET /call/history ao abrir o histórico
e após reconexão/perda de eventos. Use consultas com intervalo limitado
enquanto processing; não espere gerar o vídeo na requisição de hangup.
5. Quando ready, exiba player de vídeo e botão de download, mantendo o áudio.
Mostre Preparando vídeo durante processing, falha e expiração sem player
quebrado. Webhook usa durationMs; consultas usam durationSeconds.
6. Crie proxy no backend para GET /call/{id}/recording/video no servidor
correto. Valide sessão, empresa, instância e acesso à chamada; envie token
só no header do backend. Replique Range/If-Range, Content-Type, Content-Range,
Accept-Ranges e status 206/416; use cache privado desabilitado. Não exponha
tokens no HTML, URLs, logs ou resposta ao navegador. Restrinja downloadPath
à API cadastrada; não aceite URLs arbitrárias recebidas no webhook.
7. Trate 404 enquanto indisponível, 410 expirado e 503 indisponibilidade do
worker sem recriar a chamada. Falha do vídeo não invalida o WAV.
8. Teste ready/failed repetidos, chegada antes/depois de ended, processing,
expiração, histórico sem vídeo, isolamento entre empresas, proxy Range e
preservação da contagem de ligações simultâneas. Não ligar ou migrar números
reais durante esses testes.Retenção, falhas e limites de validação
WAV e MP4 usam o mesmo UUID interno, diretório privado e arquivos 0600. Os prazos são independentes e contados do encerramento: vídeo expira em 7 dias por padrão (ATENDROZAP_CALLS_VIDEO_RECORDING_RETENTION_DAYS), áudio em 30 dias (ATENDROZAP_CALLS_RECORDING_RETENTION_DAYS). GET /v1/capabilities anuncia os dois valores separadamente em calls_video_recording_retention_days e calls_recording_retention_days. A configuração vale para as novas gravações; o histórico é preservado. A limpeza horária remove os arquivos vencidos e a API recusa o download assim que o prazo termina, mesmo antes da remoção física. Durante a composição coexistem WAV, segmentos temporários e MP4: 1 GiB por captura ou saída não significa limite total de 1 GiB no disco.
Exemplos de falha: video_queue_full, video_render_queue_full, video_too_large, video_keyframe_unavailable, video_audio_unavailable, video_render_timeout, video_render_failed ou unsupported_video_reordering. Há limites adicionais para arquivos H.264: até 64 segmentos, 1.024 NALs por unidade de acesso, SPS/PPS até 65.535 bytes, vídeo 8-bit 4:2:0 com até 16 referências e pixels codificados até 1920×1088 (inclui o padding de 1080p e aceita retrato). Um orçamento agregado dos buffers de decodificação limita fontes simultâneas do render; excessos geram video_segment_limit, video_decoder_limit ou invalid_video_frame. Esses limites protegem recursos, sem prometer uma capacidade de chamadas simultâneas. Perfis aceitos: Baseline, Main, Extended e High (66/77/88/100). Cada SPS é validado, mesmo quando há vários na mesma unidade de acesso; SPS/PPS diferentes nessa unidade e extensões MVC/SVC não suportadas são recusados. Repetições idênticas são permitidas. O processo não registra saída bruta do FFmpeg, frames, áudio ou identidades no log normal.
Após reiniciar o worker, registros recording ou processing viram failed com worker_restarted, com evento de falha e limpeza dos temporários. Não há retomada automática do render. Uma falha ou expiração do vídeo não apaga o áudio nem muda o motivo de término da ligação. Quando o prazo de encerramento do arquivo (Archive.Wait) se esgota, os processos de render e trabalhos esperando são cancelados; não continuam como processos soltos após o encerramento do serviço.
A validação com celulares reais, chamadas prolongadas, orientação e capacidade de render simultâneo na VPS continua pendente. A opção permanece desligada por padrão; ativá-la não comprova esses critérios.
As evidências da implementação e seus limites estão no relatório Gravação de videochamadas — 19/09/2026.