Fazendo vídeos com fframes
fframes é uma biblioteca Rust que renderiza vídeo a partir de código. Um vídeo é uma struct Rust com um método render_frame(frame) -> Svgr: para cada frame ele retorna uma árvore SVG, construída com a macro svgr! (marcação SVG com {expressões rust}). As cenas dividem a linha do tempo, timeline! e molas animam valores, um mapa de áudio coloca o som e o ffmpeg codifica o resultado.
Cada projeto recebe uma linha de comando (de fframes::cli). Use-a para tudo: renderizar um vídeo, abrir a janela de pré-visualização em tempo real e visualizar frames, folhas de contato e números de loudness como arquivos que você pode ler. Leia references/design.md antes de projetar, references/api.md enquanto escreve código, references/audio.md para som e references/3d.md para tomadas 3D (fframes::three_d).
Ao corresponder a um vídeo fornecido, siga primeiro a orientação de análise de referência em references/design.md. Seus cortes, ritmo e movimento têm precedência sobre os padrões de design abaixo; edições rápidas e quadros pretos intencionais podem fazer parte do resultado solicitado.
Duas regras antes de tudo
Planeje antes de escrever código
Não abra o template e comece a animar. Primeiro:
- Defina o briefing: quem assiste e onde (a plataforma decide formato e duração), a única coisa que o espectador deve lembrar, o tom e os ativos que existem (logotipo, fotos do produto, filmagens, música, voz-over). Pergunte ao usuário o que está faltando e é importante. Nunca invente fatos, números ou citações do produto.
- Quando o vídeo for cortado para música ou voz, escolha o som primeiro. Seu tempo, batidas e frases definem o tempo de tudo o resto.
- Planeje o design de movimento primeiro, sincronize o design de movimento com os picos de som para criar um fluxo único: de onde os elementos aparecerão, como eles se transformarão. Tente criar um design de movimento único para cada cena para que pareça único e polido. O vídeo não deve parecer uma apresentação de slides.
- Tente refletir sobre a sensação da pessoa que está assistindo ao vídeo final e ajuste cada parte do design que você fizer de acordo: o fluxo, tempo, batidas, movimento e cores.
Sem porcaria
Porcaria é um vídeo feito de escolhas padrão: poderia ser sobre qualquer produto apenas com o texto alterado, e os espectadores o reconhecem em um segundo. A menos que o usuário peça, não entregue:
- Sobras do template: o layout inicial, cores, fonte ou decorações, texto placeholder, logotipos falsos, números inventados.
- Spam de batida: toda a tela com zoom, tremor ou piscando em cada batida. Sincronize coisas pequenas com a batida (um traço, uma palavra, uma faísca) e mude a tela inteira apenas em alguns cortes.
- Um movimento para tudo: todo elemento desvanecendo 40 px com a mesma suavização e duração.
- Cenas estáticas: uma coisa aparece, segura e desvanece enquanto nada mais se move.
- Cópia genérica: "Apresentando", "Liberte", "Integrado", "Próximo nível", slogans de três palavras ("Rápido. Simples. Poderoso."). Escreva o texto na tela como uma pessoa diria.
- Texto em geral: a quantidade de texto deve ser limitada e direta.
Em vez disso, dê a cada elemento uma razão ligada ao assunto e remova o que não tem. Gaste o esforço em detalhe e movimento em vez de palavras: movimento secundário, entradas escalonadas, traços desenhados, objetos que reagem uns aos outros, elementos transportados entre cortes. Antes de mostrar qualquer coisa ao usuário, olhe para uma tira e pergunte se o vídeo ainda funcionaria para um produto diferente apenas com o texto alterado. Se funcionasse, mude a ideia, não as cores.
1. Instalação
Rust de https://rustup.rs, depois as bibliotecas do sistema com as quais o ffmpeg é construído:
# macOS
brew install pkg-config ffmpeg x264 x265 opus nasm ninja
# Debian / Ubuntu
sudo apt-get install -y yasm nasm ffmpeg libx264-dev libx265-dev libopus-dev libclang-dev clang ninja-build libvpx-dev libasound2-dev
# Arch
sudo pacman -S ninja yasm nasm ffmpeg x264 x265 opus clang
Windows vincula uma compilação compartilhada pré-construída do FFmpeg 9 (defina FFMPEG_DIR para a compilação descompactada ffmpeg-n9.0-latest-win64-gpl-shared de BtbN/FFmpeg-Builds, adicione seu bin ao PATH, instale o LLVM com winget install LLVM.LLVM e defina LIBCLANG_PATH). Os detalhes estão no README do fframes.
2. Criar um projeto
curl -fsSL https://raw.githubusercontent.com/dmtrKovalenko/fframes/main/scripts/new-video.sh | bash -s -- my-video --yes
# ou instale o gerador uma vez e use-o diretamente:
cargo install --locked cargo-fframes
cargo fframes new my-video --format landscape --fps 30 --yes
Opções: --template single-scene|multi-scene, --format landscape|portrait|square|uhd, --fps, --title, --backend, --dir. O projeto depende da versão do fframes que corresponde ao cargo-fframes instalado; --git usa o branch main do repositório. Sempre passe --yes para que nada espere por entrada.
Templates:
single-scene(padrão): uma cena. Adapta-se a todos os formatos; comece aqui para clipes de uma única tomada, cartões de título e vídeo vertical.multi-scene: duas cenas. Comece aqui para qualquer coisa com várias cenas; aceita apenaslandscapeeuhd.
O conteúdo do template é código placeholder que mostra a API. Substitua seu layout, cores, fontes e decorações por um design feito para o vídeo que o usuário solicitou.
Use o backend GPU Skia (o padrão). cargo fframes new escolhe Skia no Metal (macOS) ou Vulkan (Linux, Windows). Renderiza cerca de 10x mais rápido que o backend CPU e fornece a janela de preview em tempo real. No macOS e Linux, a primeira compilação baixa Skia e ffmpeg pré-construídos e compila apenas as dependências Rust (cerca de um minuto; outros alvos, ou metal e vulkan juntos, compilam Skia da fonte por ~20 minutos). Inicie logo e escreva o vídeo enquanto ele é executado:
cd my-video && cargo build --release # execute em segundo plano, a primeira compilação é a mais lenta
--backend cpu não precisa de Skia, mas não tem janela de pré-visualização; escolha-o apenas quando não houver GPU.
O projeto é renderizado conforme gerado:
my-video/
src/lib.rs # o vídeo (do template)
src/main.rs # a linha de comando
media/ # fontes, imagens e áudio compilados no binário (uma fonte inicial)
README.md # os comandos abaixo
Cada comando abaixo é cargo run --release -- <command>. Defina uma função shell curta (uma variável como R="cargo run --release --" não divide em palavras no zsh, o shell do macOS):
R() { cargo run --release -- "$@"; }
3. O loop
Não escreva testes ao usar esta skill. Não crie testes unitários, de integração, snapshot ou de regressão visual, módulos de teste ou harnesses de teste. Valide o vídeo com inspect, frames renderizados, folhas de contato, verificações de áudio e a pré-visualização nativa.
Pré-visualize, não renderize. Se o vídeo usa o backend de renderização GPU e uma GPU está disponível nesta máquina, execute a pré-visualização nativa (ou mostre o comando ao usuário) em vez de renderizar o vídeo novamente após cada alteração. Depois que as alterações estiverem prontas para revisão:
- Mostre um comando completo em um bloco de código shell copiável, usando o caminho real do projeto e o nome do pacote; não exija que o usuário tenha definido o auxiliar
$R. Por exemplo:cd /caminho/absoluto/para/fframes && cargo run --release -p meu-video -- preview 0s. - Ofereça-se explicitamente para executar essa pré-visualização para o usuário. Se eles já autorizaram abrir ou atualizar a pré-visualização, mostre o comando e continue dentro dessa autorização.
Após uma alteração solicitada, defina o horário de início do comando para um segundo específico em ou logo antes da seção alterada, com tempo suficiente de lead-in para ver sua entrada ou transição. Por exemplo, preview 8.5s permite que o usuário revise uma alteração por volta de 9 segundos sem reproduzir o início. Indique qual seção eles verão. Renderize o vídeo completo quando o usuário solicitar a exportação final; não exija outra confirmação se essa solicitação já for explícita.
Após cada alteração:
$R timelinelista as cenas com seus intervalos de frame e segundo, e cada faixa de áudio. Verifique a estrutura e o ritmo aqui antes de olhar para os pixels.$R inspectverifica um frame a cada 0,25 s mais o primeiro e o último frame de cada cena. Relata imagens, fontes ou glifos ausentes, texto cortado pela borda do canvas, SVG inválido (retângulos de tamanho zero, raios ruins), transformações quebradas e panics, cada um com seu tempo e cena. Ele sai com código 2 em erros. Corrija tudo o que relatar. Um aviso que aparece apenas nos primeiros frames de uma cena geralmente é uma entrada; verifique com uma tira.$R strip <scene or range> -n 12escreve uma folha de contato (strip.png): frames uniformemente espaçados em uma imagem rotulada. Abra-a com sua ferramenta de imagem. É a maneira mais rápida de julgar layout, ritmo e movimento.$R strip 2s,5s,9scoloca os tempos escolhidos lado a lado.$R frame Intro@end,Outro@50%escreve PNGs em tamanho real emframes/para verificações de detalhes (tipografia, alinhamento, contraste) e imprime problemas encontrados nesses frames. Julgue pequenos detalhes (contato entre elementos, bordas, formas pequenas) em um recorte ampliado e nítido, nunca em um frame reduzido:$R frame 3s --crop 1100,520,240,120(pixels do vídeo, ampliado para cerca de 960 px de largura).--idscontorna e nomeia cada elemento que tem umid, para descobrir qual forma é qual.$R onion "[email protected]@1s" -n 6mescla frames em uma imagem (onion.png) para mostrar o caminho e o espaçamento de um movimento: suavização, overshoot, stagger.$R preview Introabre uma janela em tempo real com som para o usuário assistir (espaço play/pause, h/l avança um segundo, j/k avança um frame, q sair). Ela bloqueia até ser fechada, então inicie em segundo plano ao executá-la para o usuário. Mostre o comando e ofereça-se para lançá-lo conforme descrito acima; após edições, use um segundo específico próximo à alteração.
Quando uma amostra codificada for necessária, $R render Intro --draft codifica uma cena na metade da resolução. $R render escreve o vídeo completo para uma exportação final solicitada.
Antes de mostrar um vídeo finalizado, execute $R inspect --pops: ele renderiza cada frame pequeno e avisa sobre pops, partes da imagem que mudam em um frame entre frames parados, como um elemento que aparece, desaparece ou salta na frente de outro em vez de se mover para lá. Cada aviso fornece o comando frame ... --crop ... para visualizá-lo; repetições da mesma região (contadores, luzes piscando) são agrupadas em um aviso.
Regras:
- Olhe para os PNGs antes de dizer que algo parece bom.
- Prefira
stripa muitas chamadasframe; adicione--scale 0.5quando a composição for tudo que você precisa. - Adicione
--jsonao analisar a saída: o resultado vai para stdout, o progresso para stderr. - Mantenha
--release: compilações debug renderizam muitas vezes mais devagar.
Endereçando tempo
120 (frame), 3.2s, 500ms, 1:05.5, 50%, start, end; cenas pelo nome da struct, Intro (também corresponde a IntroScene, case-insensitive), #3 (índice da cena), Intro[1] (segunda cena desse tipo); dentro de uma cena [email protected], Intro@12, Intro@50%, Intro@end. Intervalos: a..b (final exclusivo), a.., ..b, all, ou um nome de cena para a cena inteira. Separe vários tempos com vírgulas.
Todos os comandos
| comando | use para |
|---|---|
timeline | ver cenas, durações, faixas de áudio e suas configurações de mix |
inspect [RANGE] [--every 0.1s | --all-frames] [--fail-on warning] [--pops] | encontrar problemas sem renderizar pixels; --pops também encontra elementos que aparecem ou desaparecem |
strip [RANGE | TIMES] -n N [--columns 4] [--width 480] [--crop x,y,w,h] | revisar fluxo e movimento em uma imagem |
frame TIMES [-o dir] [--svg] [--crop x,y,w,h] [--zoom N] [--ids] | PNGs em tamanho real (e o SVG layoutado) de frames escolhidos, recortes nítidos, elementos rotulados |
onion RANGE -n N | ver a trajetória e suavização de um movimento |
svg TIME | ler o SVG final de um frame como texto (posições, texto, cores) |
preview [TIME] [--paused] [--mute] | janela em tempo real com som, para o usuário |
render [RANGE] [-o out.mp4] [--draft] | codificar o vídeo ou uma parte dele (áudio cortado para corresponder) |
audio analyze [RANGE] [--waveform w.png] | loudness (LUFS), pico real, clipping, silêncio, por cena |
audio at TIMES | quais sons tocam em um momento, onde em seu arquivo e quão alto |
audio render [RANGE] -o a.wav | o mix como um arquivo WAV |
Editor no navegador
fframes também tem um editor web com linha do tempo e scrubbing, útil quando uma pessoa deseja ajustar um vídeo interativamente. Ele executa o vídeo compilado para WebAssembly e precisa de Node.js mais wasm-pack; a configuração é a pasta editor/ do exemplo hello-world no repositório fframes (https://github.com/dmtrKovalenko/fframes/tree/main/examples/hello-world). Mencione-o quando o usuário pedir um editor GUI. Para assistir, use preview, e para sua própria revisão, use o CLI.
4. Escrevendo o vídeo
impl Video for MyVideo<'_> {
const FPS: usize = 30; const WIDTH: usize = 1920; const HEIGHT: usize = 1080;
fn duration(&self) -> Duration<'_> { Duration::Auto } // soma das cenas
fn audio(&self) -> AudioMap<'_> { AudioMap::none() } // veja references/audio.md
fn define_scenes(&self) -> Scenes<'_> { Scenes::from(vec![&self.intro as &dyn Scene, &self.main]) }
fn render_frame<'a>(&'a self, frame: Frame, ctx: &FFramesContext<'a, '_>) -> Svgr<'a> {
fframes::svgr!(<svg xmlns="http://www.w3.org/2000/svg" width={Self::WIDTH} height={Self::HEIGHT}>
<rect width={Self::WIDTH} height={Self::HEIGHT} fill={BACKGROUND} />
{ctx.render_scenes(&frame)}
</svg>)
}
}
- Uma cena por ideia, 2-6 s cada. Dentro de uma cena
frame.seconds()conta a partir do início da cena. - Padrão: use
frame.animate(fframes::timeline!(...))diretamente nos atributossvgr!para movimento com valores fixos.svgr!eleva essas animações para estáticos em cache. Defina suavizações inline no timeline, incluindoEasing::Spring { mass: 1.0, stiffness: 180.0, damping: 20.0 }; constantes de suavização nomeadas são opcionais. Deixe o tempo final fora de uma mola para que ela...