Como a Z-API Lida com a Conversão de Vídeo
Um walkthrough técnico do pipeline que normaliza uploads arbitrários de vídeo em arquivos MP4 universalmente reproduzíveis.
Introdução
A Z-API é uma plataforma de integração com WhatsApp que movimenta um enorme volume de mídia gerada por usuários todos os dias. Entre todos os tipos de mídia, vídeo é o mais exigente: os arquivos chegam em dezenas de containers diferentes (MP4, MOV, MKV, 3GP, WEBM, AVI…), produzidos por tudo — de smartphones topo de linha a gravadores de tela e câmeras legadas. Cada fonte pode usar um codec diferente, formato de pixel, estratégia de taxa de quadros (frame-rate) e configuração de áudio.
O problema que isso cria é simples, mas implacável: um arquivo que toca perfeitamente no dispositivo do remetente pode se recusar a tocar, ou tocar com áudio quebrado, cores erradas, ou engasgos no dispositivo do destinatário. Para garantir a entrega, a Z-API roda todo vídeo por um pipeline de normalização cujo trabalho é produzir um arquivo MP4 com vídeo H.264 + áudio AAC, otimizado para reprodução progressiva (streaming) em qualquer dispositivo moderno.
Este post explica esse pipeline do zero. Ele começa com os fundamentos de vídeo digital (Seção 1), depois percorre as ferramentas específicas que tornam a normalização possível — FFmpeg e o encoder x264 (Seção 2) — e então finaliza detalhando como a Z-API de fato orquestra essas peças, incluindo a lógica de decisão, parâmetros adaptativos e trade-offs por trás de cada escolha (Seção 3).
Seção 1 — Fundamentos de Mídia
Antes de conseguirmos raciocinar sobre qualquer pipeline de conversão, precisamos entender o que um “arquivo de vídeo” realmente é.
1.1 Containers vs. Codecs
Um arquivo de vídeo não é um blob monolítico; ele é um container que envolve um ou mais streams codificados por codec [1][4].
- Um container (
.mp4,.mkv,.mov,.avi,.webm) é o formato externo: ele define como streams e metadados são empacotados e como eles são mantidos em sincronia via timestamps [4]. - Um codec (H.264, HEVC, VP9, AV1 para vídeo; AAC, MP3, Opus para áudio) é o algoritmo de compressão aplicado aos dados reais de mídia.
Um único container como MP4 pode legalmente conter muitas combinações diferentes de codecs como, por exemplo, H.264+AAC, HEVC+AAC, e o container por si só não diz nada sobre se o arquivo vai tocar em um determinado dispositivo [1][4]. Por isso, na Z-API, a extensão do arquivo nunca é confiada: o pipeline sempre precisa inspecionar os codecs dentro do container.
O processo de envolver streams em um container é chamado de muxing; o processo reverso é demuxing [1].
1.2 Bitrate, Tamanho de Arquivo e Duração
Essas três quantidades estão ligadas por uma única aproximação:
File Size (bits) ≈ Bitrate (bits/second) × Duration (seconds)
Bitrate é a quantidade de dados alocada para cada segundo de mídia. A estratégia usada para alocar esses bits é chamada de rate control (controle de taxa), e ela é o fator dominante que determina o trade-off entre qualidade visual e tamanho do arquivo [7].
Existem duas famílias de controle de taxa:
- CBR (Constant Bitrate): aloca a mesma quantidade de dados para cada segundo, independentemente da complexidade visual. Cenas simples são “pagas demais” enquanto cenas complexas são “pagas de menos”, produzindo artefatos visíveis durante movimento ou cenas detalhadas [7].
- VBR (Variable Bitrate): aloca mais bits para cenas complexas e menos para cenas simples. Este é o padrão moderno [7].
O método VBR mais útil para processamento sob demanda é o Constant Rate Factor (CRF), coberto em detalhes na Seção 2.
1.3 GOP, Keyframes e Tipos de Frame
Para comprimir vídeo, um encoder não armazena todo frame como uma imagem completa. Frames são agrupados em GOPs (Group of Pictures), cada um começando com um keyframe:
- I-frames (Intra-coded / keyframes): uma imagem completa autocontida, similar a um JPEG. Um vídeo só pode ser “seekado” (buscar/saltar) para um I-frame.
- P-frames (Predicted): armazenam apenas as mudanças relativas ao I-frame ou P-frame anterior. Muito menores que um I-frame.
- B-frames (Bi-directional predicted): predizem a partir de frames passados e futuros. Eles oferecem a melhor compressão, mas exigem que o decoder processe um frame posterior antes de decodificá-los, por isso pipelines de mídia precisam tanto de um Presentation Timestamp (PTS) quanto de um Decoding Timestamp (DTS) separado [2].
Um GOP mais longo (mais P/B-frames entre I-frames) gera melhor compressão, mas piora a precisão de seek porque players só podem pular para keyframes.
1.4 Formatos de Pixel e Chroma Subsampling
Vídeo não é armazenado como RGB. Ele é armazenado em um espaço de cor luma/chroma (YUV), onde Y é brilho e U/V são componentes de diferença de cor.
A visão humana é muito mais sensível ao brilho do que à cor, e chroma subsampling explora isso armazenando cor em uma resolução espacial menor do que o brilho [6]. O formato de pixel quase universal para vídeo de consumo é yuv420p: para cada bloco 2×2 de amostras de luma existe um único par U/V, reduzindo os dados de cor em ~75% com quase nenhuma perda perceptual [6].
Isso importa enormemente para compatibilidade: os decoders de hardware H.264 embutidos na vasta maioria de smartphones, browsers e smart TVs suportam apenas yuv420p [5]. Entregar um arquivo H.264 codificado em yuv422p ou yuv444p (comum em captura profissional) resultará em falha de reprodução ou cores distorcidas na maioria dos dispositivos de consumo [5]. Forçar o formato de pixel de saída para yuv420p não é uma otimização; é um requisito de compatibilidade.
1.5 Taxa de Quadros: CFR vs. VFR
- CFR (Constant Frame Rate): o tempo entre frames é fixo (por exemplo, 30 FPS). Padrão para entrega profissional.
- VFR (Variable Frame Rate): o intervalo entre frames muda ao longo do tempo. Gravações de tela e muitas capturas de smartphones usam VFR para economizar energia.
Transcodificar VFR de forma ingênua frequentemente leva a dessincronização de áudio/vídeo ou engasgos na saída [1]. A correção é forçar um CFR na saída (no FFmpeg, via -vsync cfr), o que duplica ou descarta frames conforme necessário para impor uma cadência constante [1].
1.6 Fundamentos de Áudio
Áudio é mais simples, mas igualmente importante. AAC (Advanced Audio Coding) é o codec de áudio com perdas de-facto para MP4, oferecendo melhor qualidade que MP3 no mesmo bitrate. Dentro de AAC, o profile mais universalmente suportado é AAC-LC (Low Complexity), que é o que o encoder embutido aac do FFmpeg produz por padrão. Combinar AAC-LC com layout estéreo, sample rate de 44,1 ou 48 kHz, e 128 kbps é um “safe default” bem conhecido para máxima compatibilidade.
1.7 O Atom moov e faststart
O container MP4 armazena todos os seus metadados de indexação, informações de trilha, duração e timescale em uma estrutura chamada atom moov. Por padrão, muitos encoders escrevem o atom moov no final do arquivo, após todo o payload de vídeo/áudio ter sido escrito.
Para streaming, isso é fatal: um player web não pode iniciar a reprodução até ter lido o índice; então, se o índice está no final, o player precisa baixar o arquivo inteiro antes. A flag -movflags +faststart faz o FFmpeg executar um pós-processamento que realoca o atom moov para o início do arquivo, habilitando reprodução progressiva.
Para qualquer serviço que entrega MP4 para browsers, +faststart é inegociável.
Seção 2 — FFmpeg e x264
A etapa de conversão da Z-API é construída sobre FFmpeg [2], o framework multimídia de-facto. Dentro do FFmpeg, a codificação de vídeo H.264 é feita por libx264, um dos encoders de software mais maduros que existem.
2.1 O Pipeline de Transcodificação do FFmpeg
Todo job do FFmpeg segue o mesmo pipeline lógico [1]:
- Demuxer — lê o container de entrada e o divide em streams elementares (vídeo, áudio, legendas), emitindo pacotes comprimidos [1].
- Decoder — transforma cada pacote em frames brutos (arrays de pixels para vídeo, amostras PCM para áudio). Isso reverte a compressão original e é intensivo em CPU [1].
- Filtergraph (opcional) — opera sobre frames brutos para escalar, converter formatos de pixel, deinterlace, crop, mudar frame rate, etc. [1].
- Encoder — recomprime os frames brutos usando o codec alvo (
libx264,aac). Tipicamente é a etapa mais cara de todo o pipeline [1]. - Muxer — intercala os pacotes recém-codificados de acordo com seus timestamps e os envolve no container de saída (por exemplo, MP4) [1].
Com o FFmpeg 7.0, essas etapas podem rodar como threads paralelas, o que melhora utilização de CPU e throughput em hardware multicore [1][3].
2.2 Stream Copy: Pulando as Etapas Caras
O FFmpeg suporta um caminho alternativo de “curto-circuito”: se os streams dentro da fonte já correspondem aos codecs de saída desejados, as etapas decode/filter/encode podem ser puladas completamente. Isso é chamado de stream copy e é invocado com -c copy (ou -c:v copy / -c:a copy) [1].
Em um stream copy, os pacotes fluem direto do demuxer para o muxer, então o arquivo é simplesmente reempacotado no novo container. As implicações são significativas:
- Extremamente rápido, porque não ocorre decodificação nem codificação.
- Preservação perfeita de qualidade, porque nenhuma perda geracional é introduzida.
- Custo de CPU muito baixo, o que importa em escala.
O porém é que stream copy só funciona quando os codecs de origem já são os que você quer e nenhum filtering é necessário [1]. Qualquer operação que toque os frames brutos (scaling, conversão de frame rate, conversão de formato de pixel) força uma transcodificação completa.
2.3 CRF: Mirando Qualidade em vez de Bitrate
Dentro do libx264, o modo de controle de taxa recomendado para workloads sob demanda é Constant Rate Factor (CRF). Em vez de mirar um bitrate específico, CRF mira um nível constante de qualidade perceptual, deixando o bitrate subir e descer com a complexidade da cena [8].
Valores de CRF vão de 0 (lossless) a 51 (pior), com 23 como default. Regras gerais aproximadas [8]:
- CRF 18 — visualmente lossless, arquivo grande.
- CRF 23 — bom default; alta qualidade, tamanho moderado.
- CRF 26–28 — qualidade visivelmente reduzida, mas ainda aceitável para muitos casos; arquivos notavelmente menores.
CRF é uma forma de VBR e produz melhor qualidade-por-byte do que CBR para conteúdo sob demanda [7][8]. Sua principal limitação é spikes imprevisíveis de bitrate: em cenas muito complexas, o encoder pode temporariamente emitir um bitrate muito acima da média para preservar qualidade. Para streaming com restrição de banda, isso pode ser mitigado com uma configuração de “Capped CRF” (CRF + -maxrate + -bufsize) [8].
2.4 Presets: Trocando Tempo de CPU por Eficiência de Compressão
libx264 expõe um ajuste ortogonal chamado preset, que controla quanto esforço de CPU o encoder gasta buscando a melhor forma de comprimir cada frame. Os presets vão de ultrafast (menos esforço, arquivos maiores) passando por superfast, veryfast, faster, fast, medium (o default), slow, slower, veryslow, até placebo.
A ideia crucial: o preset muda velocidade de codificação e eficiência de compressão, mas não a qualidade alvo [8][9]. Um preset mais lento produz um arquivo menor no mesmo CRF, porque o encoder faz uma busca mais exaustiva por redundância. Presets mais lentos entregam a maior eficiência de compressão (melhor qualidade por bit) e é isso que tornou o libx264 conhecido [9].
Para um serviço de alto throughput, o preset é onde os trade-offs CPU-vs-tamanho são de fato feitos.
2.5 Faststart: A Flag MP4 Inegociável
Como descrito na Seção 1.7, -movflags +faststart realoca o atom moov para o início do arquivo, habilitando reprodução progressiva em browsers. Qualquer MP4 que será streamado via HTTP precisa dessa flag.
2.6 Codificação por Hardware vs. Software
Uma alternativa ao libx264 é a codificação H.264 acelerada por hardware usando silício dedicado em GPUs modernas (NVIDIA NVENC, Intel Quick Sync Video) [9]. Encoders por hardware podem ser dramaticamente mais rápidos e oferecer maior densidade de streams por servidor, o que importa para workloads em tempo real ou de alto volume [9].
O trade-off é flexibilidade e eficiência de compressão: codificação por software com libx264 em presets mais lentos geralmente ainda produz a melhor qualidade-por-bit [9]. Na prática, estratégias híbridas (hardware por velocidade, software por qualidade) são comuns [9].
Seção 3 — Como a Z-API Implementa a Conversão de Vídeo
Com os fundamentos estabelecidos, agora podemos olhar como a Z-API de fato normaliza vídeos. O serviço é escrito em Go e orquestra FFmpeg/ffprobe por baixo. Ele é desenhado em torno de três princípios:
- Saída universal: todo arquivo que sai do pipeline deve ser H.264 + AAC em um container MP4 com
faststart. - Faça o mínimo de trabalho possível: se a fonte já é compatível, evite re-encoding.
- Adapte-se à fonte: parâmetros de encoding mudam com base no tamanho do arquivo e duração, então clipes pequenos e clipes grandes não são processados de forma idêntica.
3.1 O Pipeline
Cada conversão passa por uma sequência fixa de etapas:
Download → Size Validation → Probe (ffprobe) → Decide → Execute (FFmpeg) → Upload → Cleanup
- Download — o vídeo fonte é buscado em sua origem.
- Validação de tamanho — impõe um limite superior de tamanho de arquivo (um limite menor e mais estrito se aplica a mídia de status do WhatsApp; um limite maior se aplica a mídia geral) para que o pipeline não processe input ilimitado.
- Probe —
ffprobeé invocado com um timeout limitado. Ele faz demux do arquivo apenas para inspecioná-lo [1], retornando codec de vídeo, formato de pixel e codec de áudio sem decodificar nenhum frame. Essa inspeção é a fundação de toda decisão subsequente. - Decide — baseado na saída do probe, o serviço escolhe entre stream copy e transcode completo.
- Execute — o comando FFmpeg escolhido é executado. Seja copy ou transcode, a saída é sempre um MP4 com
+faststart. - Upload — o arquivo normalizado é enviado ao destino.
- Cleanup — arquivos temporários são removidos.
Cada etapa emite logs estruturados (início, download, detecção de codec, decisão de branch, tratamento de áudio, conclusão, erros) e é envolvida em spans OpenTelemetry, o que permite ao time de operações observar onde uma conversão gastou tempo e por que um determinado branch foi escolhido.
3.2 Lógica de Decisão e Gestão de Recursos
O estágio central do pipeline de normalização é o mecanismo de decisão que determina a rota de processamento mais eficiente para cada arquivo. Em vez de aplicar uma transformação universal e custosa a todos os uploads, o sistema utiliza os metadados extraídos na fase de sondagem (probing) para classificar o conteúdo e minimizar o consumo de recursos computacionais.
Otimização via Short-Circuiting (Stream Copy)
A principal estratégia para garantir alto throughput e fidelidade original é o uso de short-circuiting. Se os streams de vídeo e áudio detectados já estiverem em conformidade com os padrões de interoperabilidade global (H.264 e AAC), o pipeline ignora as etapas de decodificação e recodificação.
Este processo, conhecido como Stream Copy, oferece três vantagens críticas:
- Latência Irrisória: O arquivo é apenas reempacotado para o novo container, uma operação limitada estritamente pelo I/O de disco.
- Integridade de Dados: Elimina-se o risco de perda geracional, preservando a qualidade visual exata do arquivo original.
- Eficiência Operacional: A economia de ciclos de CPU permite que o sistema suporte volumes massivos sem degradação de performance.
Critérios de Interoperabilidade e Conformidade
Quando a compatibilidade não é garantida, o sistema encaminha o arquivo para uma transcodificação completa. Esta decisão não se baseia apenas no codec, mas em propriedades técnicas específicas que garantem a reprodução em dispositivos de consumo:
- Espaço de Cores e Formato de Pixel: Mesmo codecs modernos (como HEVC) podem falhar se estiverem em perfis profissionais (como
yuv422p). O pipeline garante a conversão parayuv420p, o padrão ouro para decoders de hardware em smartphones e navegadores. - Sincronização de Cadência: Arquivos com Taxa de Quadros Variável (VFR) — comuns em gravações de tela — são forçados em uma Taxa de Quadros Constante (CFR) para evitar problemas de dessincronização de áudio.
- Normalização de Áudio: Streams de áudio em formatos legados ou complexos são normalizados para AAC-LC, garantindo que o áudio seja reproduzido de forma consistente em qualquer player moderno.
Gestão de Trade-offs na Transcodificação
Para arquivos que exigem processamento, a lógica de execução prioriza a estabilidade do output. O objetivo final é transformar uma entrada técnica arbitrária em um ativo previsível: um container MP4 com o atom moov realocado para o início do arquivo (faststart), permitindo que o usuário comece a assistir ao vídeo enquanto o download ainda está em curso.
3.3 Estratégia de Codificação Adaptativa
Quando uma transcodificação é necessária, a Z-API não depende de uma única configuração fixa. Em vez disso, os parâmetros de codificação são adaptados dinamicamente com base nas características de entrada, principalmente tamanho do arquivo e duração.
O objetivo é equilibrar três dimensões concorrentes:
- Qualidade perceptual
- Tamanho do arquivo de saída
- Latência de processamento / custo de CPU
Em vez de expor limiares fixos, o sistema agrupa entradas em classes amplas de workload (por exemplo, clipes curtos vs. conteúdo de longa duração, arquivos pequenos vs. grandes) e ajusta o comportamento de codificação de acordo.
Definição de Qualidade com CRF
A Z-API usa CRF (Constant Rate Factor) como o principal mecanismo de controle de taxa [8]. Valores de CRF mais baixos são usados quando preservar a fidelidade visual é mais importante, enquanto valores mais altos são selecionados quando reduzir o tamanho do arquivo e o custo de transferência se torna uma prioridade.
Na prática:
- Vídeos menores ou mais curtos são codificados com uma compressão mais conservadora (CRF mais baixo), preservando detalhes onde o tamanho do arquivo já é gerenciável.
- Vídeos maiores ou mais longos são codificados com uma compressão ligeiramente mais agressiva (CRF mais alto), mantendo os tamanhos de saída sob controle e evitando uso excessivo de banda.
Isso segue a curva padrão de trade-off de CRF documentada pelo x264: aumentar o CRF reduz o bitrate exponencialmente enquanto degrada a qualidade gradualmente [8].
Seleção de Preset: CPU vs Eficiência de Compressão
CRF define qual qualidade mirar, mas o preset define quanto esforço de CPU é gasto para atingi-la [8][9].
A Z-API ajusta presets com base no retorno esperado do investimento em CPU:
-
Para entradas curtas ou leves, presets mais rápidos são preferidos.
A economia absoluta de tamanho com presets mais lentos é mínima, enquanto a penalidade de latência é significativa. -
Para entradas mais longas ou pesadas, presets mais lentos são usados.
Nesses casos, a melhora na eficiência de compressão leva a reduções significativas no tamanho do arquivo, o que se acumula nos custos de armazenamento e entrega.
Isso reflete uma propriedade bem conhecida do x264: presets mais lentos rendem melhor eficiência de compressão (arquivos menores com a mesma qualidade), mas com retornos decrescentes em relação ao tempo de codificação [9].
Por que Ambos os Eixos Importam
CRF e preset operam em eixos ortogonais:
- CRF → controla qualidade vs bitrate
- Preset → controla tempo vs eficiência de compressão
Otimizar apenas um deles leva a resultados subótimos. Ao adaptar ambos simultaneamente, o pipeline garante que:
- CPU é gasta onde tem impacto mensurável
- A latência é minimizada onde não é
- Arquivos de saída permanecem dentro de limites práticos de tamanho sem sacrificar compatibilidade
Princípio de Design
O princípio orientador é simples:
Gastar computação proporcionalmente ao quanto o resultado custará para armazenar e entregar.
Clipes curtos são processados rapidamente com overhead mínimo.
Conteúdo mais longo ou mais pesado justifica esforço adicional de codificação porque mesmo pequenos ganhos percentuais em compressão se traduzem em economias absolutas significativas.
3.4 Flags de Saída Sempre Aplicadas
Independentemente de o pipeline seguir o caminho de stream-copy ou o caminho de transcode, todo arquivo de saída é produzido com:
-f mp4— força o container MP4 independentemente da extensão de entrada, então a saída é sempre o formato esperado.-movflags faststart— realoca o atommoovpara o início do arquivo, habilitando reprodução progressiva em browsers. Isto é obrigatório para entrega web.- Vídeo H.264 + áudio AAC — o par de codecs universalmente suportado para MP4 [9]. Quando stream copying, isso já é verdade na fonte por construção da lógica de decisão; quando transcodificando, isso é imposto por
libx264+aac. - Formato de pixel
yuv420pno transcode forçado via-pix_fmt yuv420pporque decoders H.264 de consumo só suportam isso de forma confiável [5][6]. - AAC a 44,1 kHz, 128 kbps, estéreo quando o áudio é re-encoded, os defaults seguros padrão da indústria para MP4.
3.5 Observabilidade e Segurança
Várias escolhas operacionais tornam o pipeline resiliente em produção:
- Timeout limitado para
ffprobe. Probing roda com uma duração máxima fixa, então um input malformado ou patológico não pode travar o pipeline indefinidamente [1]. - Limites explícitos de tamanho. Inputs acima de um máximo absoluto são rejeitados antes de qualquer trabalho de CPU começar.
- Logging estruturado de decisões de branch. Todo job emite os resultados do probe e o branch escolhido (copy vs. transcode), então a razão entre trabalho barato e caro é mensurável.
- Tracing com OpenTelemetry. Cada etapa (download, probe, decide, execute, upload) é seu próprio span, tornando análise de gargalos direta.
Conclusão
O pipeline de conversão de vídeo da Z-API é um exercício aplicado de casar cada input com a operação mais barata que ainda garanta reprodução universal. Três ideias o sustentam:
- Probe primeiro, decida depois.
ffprobepermite ao sistema tomar decisões informadas sem gastar CPU decodificando [1]. A saída do probe (codec de vídeo, formato de pixel, codec de áudio) é suficiente para escolher entre um stream copy em escala de milissegundos e um transcode completo. - Stream copy quando a fonte já é compatível. H.264, e HEVC já em
yuv420p, são re-muxed em MP4 com+faststartem vez de re-encoded [1]. Isso preserva qualidade, economiza CPU, e é o maior ganho de performance em escala. - Quando transcodificar é necessário, adapte. CRF e preset variam com tamanho e duração da fonte para que o encoder gaste esforço proporcional a quanto a saída será vista e transferida [8][9]. Combinado com defaults obrigatórios de compatibilidade (H.264,
yuv420p[5][6], AAC-LC,faststartCFR), o pipeline produz um MP4 que toca em qualquer lugar, sempre.
A lição para quem está construindo um sistema similar é que um pipeline de vídeo não é um único comando FFmpeg — é uma árvore de decisão onde o comando certo para cada input depende do que esse input já é. Um probe rápido e uma tabela de decisão bem escolhida podem cortar o custo de normalização de vídeo em uma ordem de grandeza comparado a re-encoding ingênuo de tudo.
Referências
- ffmpeg Documentation, ffmpeg.org. https://ffmpeg.org/ffmpeg.html
- FFmpeg, FFmpeg Project. https://www.ffmpeg.org/
- FFmpeg/FFmpeg architectural overview, DeepWiki. https://deepwiki.com/FFmpeg/FFmpeg
- What is a codec?, red5.net. https://www.red5.net/blog/what-is-a-codec/
- Which pixel format for web mp4 video, Stack Overflow. https://stackoverflow.com/questions/32829514/which-pixel-format-for-web-mp4-video
- A Comprehensive Guide to Chroma Subsampling, Cablematters. https://www.cablematters.com/Blog/HDMI/a-comprehensive-guide-to-chroma-subsampling
- What is CBR, VBR, CRF, Capped CRF? Rate Control Explained., OTTVerse. https://ottverse.com/what-is-cbr-vbr-crf-capped-crf-rate-control-explained/
- Rate Control revisited, slhck.info. https://slhck.info/video/2017/03/01/rate-control.html
- GPU vs CPU Transcoding: Which One Delivers Better Streaming, Ant Media. https://antmedia.io/gpu-vs-cpu-transcoding-for-streaming/