Produção de vídeo com código
Trate um vídeo como uma função pura render(t) mais um pipeline em estágios e passível de asserção. Quase todo modo de falha nesta classe de tarefa é um erro silencioso de unidades/sinal/canal/métrica — portanto, o fluxo de trabalho abaixo é construído para tornar cada um deles observável.
0. Congele o contrato antes de desenhar qualquer coisa
- Uma única fonte de tempo. Tudo vive dentro de
render(t); o mapeamento quadro↔tempo (t = quadro/fps) é toda a API pública. Sem animação CSS, sem timers, sem estado quadro-a-quadro. Resultado: quadros são paralelizáveis, retomáveis, comparáveis, e um bug é localizável a um intervalo de tempo. - Determinismo. PRNG seedada (
hash(i)/mulberry32), nuncaMath.random; nuncaDate.now; incorpore fontes e texturas como data URIs para que nenhuma rede/fallback de fonte possa alterar pixels durante a execução. - Escreva a especificação como asserções, não como prosa. Duração, fps, resolução, codec, formato de pixel, loudness, pico verdadeiro, área segura/margens, técnicas banidas, caminhos de arquivo, flags de CLI, códigos de saída, formato de saída JSON. Se um número está no briefing, ele deve terminar em uma verificação.
- Decida a política de emenda antecipadamente — tomada única / loop / segmentos concatenados. Para um loop: todo oscilador é
sin(2π·k·t/D)comkinteiro; o filme converge de volta ao seu próprio primeiro estado (não "parece semelhante"); tabelas de eventos e contadores/rótulos na tela são reiniciados dentro da transição final; verifiquerender(0)vsrender(D). - Orçamento por quadro. Meça
render(t)antes de construir o conteúdo (um desenho em canvas 1080p leva 2–30 ms; 3 subquadros × 5400 quadros multiplica isso). Registre o orçamento e mantenha-o. - Congele a interface e então faça o hash. Tamanho do canvas, superfície da página/API, CLI do worker, caminhos de saída. Todo o QC está vinculado a esse hash de revisão — um render QC'd contra um hash desatualizado não vale nada.
- Registre desvios deliberados. Cada lugar onde a realidade se afasta do briefing (uma ondulação da grade em linhas "limpas", micro-tipografia menor que o mínimo declarado, uma taxa de mola fora do intervalo indicado) vai para uma lista curta com uma razão, para que um revisor possa julgar em vez de descobrir.
1. Três sondas no primeiro dia
| Sonda | Asserção | Por quê |
|---|---|---|
| Integridade da captura | pixels exatos, alpha == 255, faixa de cor, primeiro/último quadro presente | acumulação translúcida, erros de faixa/espaço de cor silenciosamente arruínam todo o filme |
| Custo | distribuição de ms de render(t) por cena, memória | mudanças de arquitetura são baratas agora, caras depois |
| Movimento | série de diferença por quadro sobre a duração total + execução congelada mais longa | mostra os intervalos de tempo morto imediatamente |
Regras que vieram de falhas reais:
- A armadilha da composição sobre branco. Revisar quadros através de um visualizador que compõe alfa sobre branco esconde translucidez; a codificação compõe sobre preto. Faça o caminho de revisão compor exatamente como o caminho de entrega, e diferencie um pixel entre os dois.
- Saiba o que uma métrica pode ver. Após uma redução de 12×, o movimento sub-pixel de uma linha fina altera a diferença absoluta média em ~0,03/255, enquanto uma mudança de brilho no quadro inteiro a move em ~1,5/255. Se a métrica de aceitação é uma média reduzida, a alavanca real mais barata é um canal de valor global (por exemplo, um pulso de luminância de 2 Hz ±3 % na taxa da batida), não uma oscilação maior.
- Verifique a métrica antes do ativo. Quando uma verificação falha, despeje o sinal bruto (envelope em 1 ms, perfil de linha de pixel, série de quadros) primeiro. Metade de "o ativo está errado" acaba sendo o detector.
- Torne os detectores autodescritivos. Modele e compense o viés sistemático (atraso de grupo do filtro, lag da janela de análise causal); prefira o candidato mais próximo do evento sobre o mais alto; nunca ajuste um limiar até que ele passe.
- Alegações exatas vs aproximadas. "render(0) ≡ render(D), PSNR ∞" é uma prova; "primeiro vs último quadro codificado diferem por 2/255 (um quadro de movimento)" é uma observação. Nunca confunda os dois em um relatório.
Incluso: scripts/av_audit.mjs implementa a metade de contêiner/movimento/loop/loudness/folha disto (apenas ffmpeg, sem deps npm).
2. O loop de revisão é um recurso, não uma sobrecarga
- Gradeie: centenas de stills → uma dúzia de folhas de contato, revisadas em uma passada.
- Incorpore guias: caixa de área segura, cruz central, timecode, id do evento. O revisor não deve ter que segurar o layout na cabeça.
- Amostre por evento, não por intervalo fixo: um quadro em cada evento pretendido e um no meio do movimento.
- Olhe mais atentamente para as emendas: transições de cena, fechamento de loop, limites de segmento, primeiro/último quadro.
- Isole e amplie um elemento suspeito em uma tela em branco a 4–6×; a maioria dos bugs é invisível no quadro completo.
- Artefatos são descartáveis: um comando os regenera; nunca corrija manualmente um quadro de revisão.
- Mantenha uma lista de problemas (confuso / fora do ritmo / sobreposto / fora da área segura / ilegível / congelado) e re-revise após cada rodada de correção.
3. Arte de movimento e loop
- Ambiente ≠ ação. Cada cena precisa de um canal contínuo em uma frequência diferente da ação (deriva, onda, rotação ociosa, pulso de valor) com cruzamentos de zero alinhados ao ritmo para que as ações ainda caiam limpas. Isso é o que satisfaz "nada jamais congelado".
- Uma família de easing. Mola criticamente amortecida
1-(1+x)e^-x(nunca ultrapassa) para movimentos; ease-out/in exponencial para transições, pressionamentos, saídas. Faça a aterrissagem coincidir com o evento: início = evento − duração, e normalize para que progresso = 1 exatamente no evento. - Desfoque de movimento em dois lugares: dentro da função do quadro (2–4 amostras de obturador médias com uma média móvel —
alpha = 1/(k+1)— que termina totalmente opaca), mais densa perto de eventos; mais uma mistura temporal do lado do codificador. Prove a semântica da janela do filtro com um dispositivo de teste codificado por índice (alimente quadros cujos pixels codificam o número do quadro e leia de volta quais índices foram médios), nunca a partir de documentação. - Continuidade por construção. Carregue um modelo de geometria/estado através das cenas e transforme-o (morph, troca de material, inundação de wipe de borda dura, linha de varredura) em vez de cortar ou dissolver.
- Morphs que preservam comprimento: parametrize formas por ângulo/comprimento de arco para que as misturas cresçam em vez de escalar.
- Lista banida por padrão (delete a menos que solicitado): partículas, brilhos, flares de lente, tremor de câmera, divisão RGB, crossfades, gradientes genéricos, overshoot saltitante.
4. Render e codificação
- Formato vs alvo: meça o throughput primeiro. Quadros PNG sem perdas são multi-MB e sobrecarregam o pipe; JPEG de alta qualidade é ~1,5× mais rápido. Decida uma vez, depois mantenha.
- Pipe, não armazene: alimente quadros para o stdin do ffmpeg (
-f image2pipe -framerate <fps*sub> -vcodec png -i -). Misture comtmix, selecione o último quadro do grupo, re-tempo,-frames:v <n>para cortar. - Paralelize por custo, não por contagem de quadros. Meça ms por cena, depois particione em pedaços de tempo aproximadamente igual; contagens uniformes de quadros fazem o worker mais lento definir o tempo de parede.
- Segmentos devem ser seguros para concat: repita 2 quadros de limite em cada extremidade, parâmetros de codificador idênticos e base de tempo, verifique quadros/duração com ffprobe, depois concat com
-c copy. - Estagie o pipeline para que uma falha custe um estágio: render → blend → encode → áudio → mux → medir; cada estágio escreve um arquivo.
- Mantenha um mestre de vídeo sem perdas entre estágios (
-c copyem toda parte), e faça a loudness por último. - Teste um conjunto de ferramentas desconhecido antes de confiar nele: listas de filtros/codificadores, executáveis do navegador, versões; permitir substituições
$FFMPEG/$FFPROBE; preferir binários locais fixados sobre PATH. Um ffmpeg "media" reduzido enviado com um navegador geralmente carece dex264/tmix/loudnorm. - Capture a saída do processo filho através de descritores de arquivo, não pipes. Dentro de uma sandbox confinada,
spawn/execSynccomstdio: 'pipe'pode falhar comEPERMenquanto o mesmo binário funciona bem do shell. Passe handles de arquivo abertos:spawnSync(cmd, a, { stdio: ['ignore', fdOut, fdErr] }), depois leia os arquivos — e usestdio: 'ignore'para sondagens de existência/versão pura. Roteie stdout e stderr para arquivos separados para que dados de quadro binários não sejam intercalados com texto de log. - Termine com uma reconstrução completa a partir do código-fonte, para que o arquivo entregue não seja um cache de sorte.
5. Áudio e mux
- Mesma base de tempo e mesmo determinismo que a imagem (render offline, seedado, sem dependência de tempo real).
- Uma tabela de cues (
{t, name}) como a única fonte da verdade para cada evento audível/visível; SFX vivem em seu próprio barramento, mais silenciosos que a música. - Normalize com loudnorm de duas passagens (
print_format=json→ measured_* → segunda passagem comlinear=true), depois verifique com uma terceira medição independente; afirme a loudness integrada dentro de ±0,5 LU e o pico verdadeiro ≤ alvo. - Loops: desvaneça a cauda (~40 ms) para zero exato, primeira amostra não clicante; meça o alinhamento de golpes percussivos com a grade e reporte.
- Mux
-c:v copy -c:a aac -b:a <spec>, depois ffprobe o contêiner como uma asserção, não uma olhadela. -b:a Nké um alvo, não um piso para o codificador AAC nativo do ffmpeg (medido ~180–250 kbps contra uma solicitação de 320k em material silencioso;-aac_coder twoloopajuda). Leia obit_ratedeclarado de volta e reporte a média medida em vez de repetir o número solicitado, e diga isso explicitamente quando a especificação exigir uma bitrate.
Incluso: scripts/mux_normalize.mjs.
6. Mantenha a camada de desenho barata e determinística
- Armazene em cache tudo que é invariante no tempo: fundos pré-renderizados, máscaras de texto, padrões, Path2D. As chaves de cache contêm todos os parâmetros que podem alterar pixels.
- Os pools de canvas devem ser dimensionados a partir das dimensões do canvas de origem, não do tamanho do conteúdo lógico — caso contrário,
drawImageestica ou corta silenciosamente (isso produziu tipo cortado/mutilado). - Evite
shadowBlur/filterno loop de quadro; prefira geometria. - Memória: um canvas 1080p tem ~8 MB; limite o pool.
- Lembre-se de que a contagem de subquadros multiplica o orçamento do quadro linearmente.
7. Colaboração
- Divida autor vs verificador. O verificador possui os scripts de QC e a autoridade para reprovar a entrega, e verifica independentemente dimensões que o autor não verificou (alfa, fidelidade vs página, loudness, fechamento de loop). Isso é o que captura a classe de bug à qual o autor é cego.
- Escopos de escrita disjuntos + uma interface congelada para que o trabalho paralelo não possa colidir; faça o hash da revisão e re-execute o QC após qualquer alteração.
- Falhe alto: saídas não zero, uma linha
PASS/FAILpor alegação, JSON para qualquer coisa lida por máquina, sem fallbacks silenciosos.
8. Anti-padrões
- Julgar cor através de uma imagem de revisão composta sobre branco.
- Editar o ativo para satisfazer uma métrica que você nunca caracterizou.
- Ajustar o limiar de um detector em vez de corrigir seu modelo.
- Re-executar o render completo para responder a uma pergunta que uma sonda de 20 segundos do lado da página responde.
- Particionamento uniforme por contagem de quadros entre cenas de custos muito diferentes.
- Um caminho de revisão que difere do caminho de entrega (escala, faixa de cor, composição).
- "Parecia bom" em vez de uma asserção; falhas que saem com 0.
- Cirurgia de texto/ativo com ferramentas que danificam não-ASCII (verifique a codificação após a edição).
- Corrigir quadros de revisão manualmente em vez de corrigir a fonte.
- Esconder uma descontinuidade de estado atrás de um crossfade.
9. Listas de verificação
Pré-voo — especificação → asserções; assinatura render(t) congelada; política de emenda + álgebra de loop; ativos embutidos; três sondas existem; orçamento por quadro definido; FFMPEG/FFPROBE resolvido e testado quanto a capacidades.
Em voo — após cada cena: sonda de custo, série de movimento, grade de revisão amostrada por evento; lista de problemas fechada; verificação de sintaxe/tipo mais uma verificação de não-ASCII em fontes editadas; hash da revisão.
Pré-entrega — reconstruir a partir do código-fonte; render completo (ponderado por custo, quadros de limite, concat verificado); loudness de duas passagens + terceira verificação; script de medição independente → PASS/FAIL; reportar alegações exatas vs aproximadas explicitamente; deletar scripts/screenshots temporários e documentar o comando único de regeneração para cada um.
Definição de pronto — metadados do contêiner exatos; comportamento de emenda comprovado; limite de execução congelada atendido; loudness/pico verdadeiro atendido; texto na tela corresponde ao briefing literalmente; um comando reconstrói a partir do código-fonte; todo o QC verde no hash congelado.
10. Scripts inclusos
Ambos são de arquivo único, sem dependências (apenas ffmpeg/ffprobe), resolvem suas ferramentas de $FFMPEG/$FFPROBE → PATH → locais comuns, imprimem uma linha PASS/FAIL por alegação, emitem JSON com --json e saem com código não zero em caso de falha. Resolvem seu diretório a partir do próprio caminho desta skill.
# QC de um arquivo finalizado: contêiner vs expectativas, limite de execução congelada, emenda de loop,
# loudness/pico verdadeiro, folhas de contato opcionais para revisão humana
node av_audit.mjs film.mp4 --expect-duration 30 --expect-fps 60 --expect-size 1920x1080 \
--static-limit 0.5 --sheets ./sheets # exit 0 = todas as verificações passaram
# Normalizar um WAV para um alvo e multiplexá-lo em um vídeo codificado (stream de vídeo copiado),
# depois verificar com uma terceira passagem de medição independente
node mux_normalize.mjs silent.mp4 score.wav film.mp4 --lufs -14 --tp -1 --abr 320k
av_audit.mjs usa deliberadamente uma série MAD de baixa resolução (160×90): ela vê mudanças de valor de quadro inteiro e movimento de bloco, não movimento sub-pixel de linhas finas — emparelhe-a com a regra de sensibilidade de métrica na §1. Para um loop, trate first_vs_last_psnr_db como uma observação sobre um quadro de movimento e prove exatidão separadamente comparando dois renders do próprio renderizador.
Resumo de uma linha — o sucesso depende de três coisas, nenhuma delas habilidade de desenho: a função temporal é pura e verificável, as métricas são compreendidas antes de serem otimizadas, e os caminhos de revisão e entrega são o mesmo caminho.