Todos os projetos

KUBRICK

Motor de vídeo declarativo

Um motor de vídeo declarativo: de JSON a MP4, frame a frame

Papel Desenhado junto com o tech lead do lab e implementado por mim (todos os commits de implementação do repositório são meus).
Período Fev 2026 –
Resultado Em desenvolvimento: formato do spec e compilador prontos, etapa de renderização em andamento.
Stack
  • Python
  • OpenTimelineIO
  • PyAV (ffmpeg)
  • NumPy
  • Pillow
  • uv
Em desenvolvimento · Folha

Contexto

O Lab de IA da Folha de S.Paulo produz vídeos de notícia de forma automática. Um serviço upstream no pipeline escreve o roteiro a partir da matéria, e alguma coisa precisa transformar esse roteiro num MP4 de verdade. Essa etapa rodava no motor do Mosaico, o framework open source de vídeo do lab, baseado em MoviePy. O Kubrick é o substituto: recebe um spec e renderiza o arquivo. Ainda é um trabalho em andamento (a etapa de renderização é o que estou construindo agora), e esta página descreve o desenho e as partes que já estão de pé.

Meu papel

Desenhei o Kubrick junto com o tech lead do lab e escrevi a implementação. Todos os commits de implementação do repositório são meus. Especifiquei o formato JSON e escrevi o compilador e o renderizador.

Houve uma primeira tentativa. Apaguei tudo e reconstruí o motor do zero em fevereiro de 2026.

A entrada é um ScriptSpec, um formato JSON que eu desenhei (uma pequena linguagem de domínio). Ele descreve cenas, áudio, legendas e efeitos. Os intervalos de tempo são absolutos ou relativos à própria mídia, então um clipe consegue dizer “começa dois segundos depois do início deste áudio” sem ninguém precisar calcular os offsets antes. Uma cena pode ser reaproveitada por uma chave, com overrides em cima. Um efeito vale para um clipe específico ou para a trilha inteira.

  1. 01 Serviço de roteirização por LLM upstream, não é meu: escreve o roteiro a partir da matéria
  2. 02 ScriptSpec uma DSL em JSON, o formato canônico
    • intervalos de tempo absolutos ou relativos à mídia
    • cena reaproveitada por chave, com overrides
    • efeitos por clipe ou por trilha
  3. 03 Compilador do spec para uma timeline OpenTimelineIO
    • trilhas de vídeo, áudio, legenda e overlay
    • o OTIO nunca sai do backend
  4. 04 Renderizador escrito direto em cima de PyAV, NumPy e Pillow; sem MoviePy
    • um gerador de frames por trilha
    • composição com alpha de overlays e legendas
    • reamostragem estéreo e mixagem de áudio em FIFO
    • mux intercalado; efeitos de frame e de áudio no mesmo loop
  5. 05 MP4 com precisão de frame

= meu trabalho

O Kubrick transforma uma especificação declarativa em vídeo pronto: entra JSON, sai MP4. As etapas marcadas são as partes que eu projetei e escrevi.

O Kubrick compila o spec para uma timeline OpenTimelineIO (OTIO), o formato de intercâmbio usado pelos softwares de edição profissionais, e renderiza essa timeline com código escrito direto em cima do PyAV, usando NumPy e Pillow na parte de imagem. Sem MoviePy. Cada trilha vira um gerador que entrega frames. Trilhas de overlay e de legenda são compostas com alpha por cima do vídeo. As trilhas de áudio são reamostradas para um formato estéreo comum e mixadas num buffer FIFO. Vídeo e áudio saem intercalados no mux do MP4. Os efeitos de frame (blur, crop, fade, flip, rotate) rodam no mesmo loop, e o áudio tem os seus.

A parte difícil

Duas coisas.

A primeira foi a DSL. Outras ferramentas precisam escrever e alterar esses specs (o serviço de roteirização anterior no pipeline e o tooling de edição que ajusta um vídeo antes de renderizar), então o formato tem que aguentar ser mexido por algo que não é o Kubrick e continuar compilando. Mantive o ScriptSpec como formato canônico e empurrei o OTIO inteiro para o backend (nada fora do Kubrick enxerga um objeto de timeline). Essa restrição definiu boa parte do desenho. Os intervalos relativos à mídia existem para que uma edição não obrigue a recalcular todos os offsets seguintes. A chave de reúso existe para que uma cena repetida seja uma declaração com overrides, em vez de um bloco copiado que sai de sincronia com o original.

A segunda é escrever o compositor e o mixer na mão, e essa é a parte da renderização que ainda está em andamento. As bindings do ffmpeg entregam frames decodificados e pacotes, não uma timeline. Tudo entre uma coisa e outra é código que você escreve: o timing dos frames, qual trilha está por cima em determinado instante, o que fazer quando o áudio é mais curto que o clipe que aponta para ele. Precisão de frame é o requisito. Se a imagem e a narração ficam um frame fora de sincronia, o vídeo parece quebrado.

Um ScriptSpec de demonstração e quatro frames que o Kubrick renderizou a partir dele (mídia de teste; os efeitos de fade e blur estão visíveis).
Um ScriptSpec de demonstração e quatro frames que o Kubrick renderizou a partir dele (mídia de teste; os efeitos de fade e blur estão visíveis).

Resultado

O Kubrick é um trabalho em andamento. O formato do spec e o compilador estão de pé, e a etapa de renderização é o que estou construindo agora; quando ela fechar, o Kubrick assume a renderização do pipeline de vídeo do lab. É interno, não tem link. A parte que eu mostraria hoje é o formato do spec. É JSON simples, e as ferramentas que geram o spec não precisam saber nada de vídeo.

Projeto interno da Folha de S.Paulo (descrito aqui sem código, dados editoriais ou detalhes de infraestrutura).