pipeline ffmpeg
Os scripts auxiliares estão em scripts/ ao lado deste arquivo. Execute-os com python <skill_dir>/scripts/<nome>.py.
probe.py ENTRADA...— resumo de uma linha por arquivo (tamanho, fps, rotação, HDR, duração, áudio).export.py ENTRADA SAÍDA --preset instagram|tiktok|shorts|youtube|master|preview— codificação final com as configurações corretas.frames.py ENTRADA DIRSAÍDA --times 0.5,3,7.2ou--every 1— captura quadros de verificação.
1. Sondar e normalizar primeiro
Armadilhas de vídeos de telefone:
- Rotação: iPhone armazena pixels paisagem + um sinalizador de rotação. ffmpeg auto-rotaciona na decodificação (
-noautorotatedesativa). OpenCVVideoCapturepode não — verifiqueframe.shapee rotacione você mesmo. - VFR: telefones gravam taxa de quadros variável. Converta para CFR na extração:
-vf fps=30(ou-r 30na saída) para que a matemática dos quadros seja exata. - HDR:
color_transfer=arib-std-b67(HLG) ousmpte2084(PQ). Intocado, parece desbotado/cinza em SDR. Faça mapeamento de tons:
# HDR (HLG ou PQ) -> SDR Rec.709, caminho zscale na CPU
-vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"
hable = filmico; mobius mantém mais saturação. Se zscale estiver faltando, use libplacebo (-vf libplacebo=tonemapping=bt.2390:colorspace=bt709:color_primaries=bt709:color_trc=bt709).
- Apple Log / log de câmera: aplique primeiro a LUT Log → Rec.709 do fabricante (
lut3d=AppleLog_to_Rec709.cube), depois faça a correção de cor. - Fontes de 10 bits: mantenha 10 bits nos intermediários (
-pix_fmt yuv422p10leProRes ouyuv420p10lex265) ao fazer correção de cor intensa; converta para 8 bitsyuv420papenas na exportação final para redes sociais.
2. Arquitetura de um script de construção
sources/ (nunca modificado)
edit/
build.py # regenera tudo a partir de sources + arquivos de dados
edl.json # [{src, in, out, speed, grade, transition}]
segments/ # um arquivo graduado e normalizado por entrada EDL (quase sem perdas)
overlays/ # PNG / sequência PNG / ProRes 4444 / WebM alfa
preview.mp4, final.mp4
Regras:
- Extrair por segmento com todo o trabalho por clipe (corte, velocidade, escala/corte, graduação, fps) em uma passagem → intermediário em
-crf 10(x264) ou ProRes 422 HQ. - Concatenar segmentos com o demuxer concat e
-c copyquando nenhuma transição for necessária (todos os segmentos devem compartilhar codec, resolução, fps, pix_fmt, timebase). - Overlays e texto em uma passagem final sobre o vídeo concatenado, depois codifique uma vez para a especificação de entrega.
- O áudio é construído separadamente (música + SFX + diálogo → um WAV), depois multiplexado na passagem final.
Corte preciso
# -ss antes de -i é rápido e preciso no quadro ao re-encodificar (ffmpeg >= 2.1)
ffmpeg -ss 12.40 -i IN.mov -t 2.333 -vf "fps=30,scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
-c:v libx264 -crf 10 -preset medium -pix_fmt yuv420p -an seg_003.mp4
Nunca use -c copy para cortes que devem ser precisos no quadro (ele salta para keyframes).
Preencher 9:16 a partir de 16:9
- Corte (cover):
scale=-2:1920,crop=1080:1920:(iw-1080)*X:0com X 0..1 para escolher a posição do sujeito (rastreie se o sujeito se mover). - Fundo desfocado (contain):
split[a][b];[a]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=40:2[bg];[b]scale=1080:-2[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2
Concatenar
# list.txt: file 'segments/seg_001.mp4' (um por linha)
ffmpeg -f concat -safe 0 -i list.txt -c copy joined.mp4
Transições com xfade
ffmpeg -i a.mp4 -i b.mp4 -filter_complex \
"[0:v][1:v]xfade=transition=fade:duration=0.5:offset=4.5,format=yuv420p[v]; \
[0:a][1:a]acrossfade=d=0.5[a]" -map "[v]" -map "[a]" out.mp4
offset = duração do primeiro clipe − duração da transição. Tipos: fade, fadeblack, fadewhite, wipeleft, slideup, smoothleft, circleopen, radial, zoomin, hblur, pixelize, dissolve… Encadeie offsets cumulativamente para muitos clipes (cada xfade encurta o total pela sua duração).
Overlays
# PNG estático (quadro inteiro, transparente) para o vídeo inteiro — sem necessidade de -loop
[0:v][1:v]overlay=0:0:format=auto
# limitado no tempo
overlay=0:0:enable='between(t,2.0,5.5)'
# clipe overlay animado começando em T na saída
[1:v]setpts=PTS-STARTPTS+T/TB[ov];[0:v][ov]overlay=0:0:eof_action=pass
Overlays com alfa: sequência PNG (-framerate 30 -i ov/%04d.png), ProRes 4444 (-c:v prores_ks -profile:v 4444 -pix_fmt yuva444p10le) ou VP9 WebM (-c:v libvpx-vp9 -pix_fmt yuva420p).
Não use -loop 1 com um PNG RGBA 4K para vídeos longos; ele consome muita RAM. Uma entrada de imagem única é repetida automaticamente pelo overlay.
Enviando quadros brutos do Python (composição PIL/OpenCV) para o ffmpeg:
p = subprocess.Popen(["ffmpeg","-y","-f","rawvideo","-pix_fmt","rgb24","-s",f"{W}x{H}","-r","30","-i","-",
"-c:v","libx264","-crf","12","-pix_fmt","yuv420p","out.mp4"], stdin=subprocess.PIPE)
p.stdin.write(frame.tobytes()) # numpy uint8 HxWx3, por quadro
Leia quadros de origem da mesma forma (-f rawvideo -pix_fmt rgb24 -), o que evita problemas de rotação do OpenCV e de codec.
Velocidade
- Constante:
setpts=PTS/2(2x) +atempo=2.0(faixa do atempo 0.5-100; encadeie para mais). - Câmera lenta suave a partir de 30 fps:
minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1, depoissetpts=2*PTS. Verifique se há distorções em bordas/mãos. - Rampas de velocidade variável: renderize quadro a quadro a partir de um mapa de tempo (veja
beat-sync), nunca empilhe muitas expressõessetpts.
Legendas (por último)
-vf "subtitles=captions.ass:fontsdir=fonts" # ASS dá controle total de estilo
3. Predefinições de exportação
| Alvo | Vídeo | Áudio | Notas |
|---|---|---|---|
| Instagram Reels / TikTok / Shorts | 1080x1920 (ou 2160x3840) H.264 High, 30 fps, CRF 16-18, -maxrate 25M -bufsize 50M, yuv420p | AAC 48 kHz 256-320k | -movflags +faststart; as plataformas re-encodificam, então forneça um arquivo de alta qualidade |
| YouTube 16:9 | 3840x2160 ou 1920x1080, H.264 CRF 16 ou HEVC CRF 18, fps da fonte | AAC 48 kHz 320k | envie 4K mesmo para conteúdo 1080p para obter a melhor codificação VP9/AV1 |
| Master | ProRes 422 HQ ou x264 CRF 10 / x265 CRF 14 -tag:v hvc1 | PCM 24-bit 48 kHz | arquivo |
| Preview | 720p, CRF 28, -preset veryfast | AAC 128k | iteração rápida |
Sempre adicione -pix_fmt yuv420p para redes sociais (10-bit ou 4:2:2 pode falhar ou parecer errado após o upload) e -color_primaries bt709 -color_trc bt709 -colorspace bt709 para marcar SDR corretamente.
4. Verificar
ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt -show_entries format=duration -of compact out.mp4
ffmpeg -i out.mp4 -af ebur128=peak=true -f null - 2>&1 | tail -12
ffmpeg -i out.mp4 -vf "select='gt(scene,0.3)',showinfo" -f null - 2>&1 | grep pts_time # onde os cortes realmente estão