Vídeo com IA: Claude Code, Codex e ElevenLabs · Capítulo 22 de 25 · 24 min
Remover pausas e reenquadrar o vídeo de 16:9 para 9:16 com legenda por palavra
Aplique a lista de cortes da aula 17 com as pausas da montagem final e entregue um vertical 9:16 que segue o rosto com a legenda acesa palavra por palavra.
Este capítulo faz parte do curso gratuito Vídeo com IA: Claude Code, Codex e ElevenLabs.
Um corte vertical que prende no Reels pede três coisas que a transcrição sozinha não entrega: um ritmo mais enxuto que o do vídeo longo, a pessoa sempre dentro da janela estreita e uma legenda que acompanha cada palavra dita. Sem isso, o recorte central deixa a cozinheira fora do quadro quando ela anda até o fogão, e a legenda em frases inteiras perde quem assiste sem som.
Refazer esse trabalho à mão em cada corte da semana consome mais horas do que gravar. O prejuízo dobra quando a legenda escorrega meio segundo depois do primeiro corte, porque os tempos do bruto não valem mais para a versão editada.
Nesta aula você aplica a lista de cortes da aula 17 com os limiares da montagem final, reenquadra de 16:9 para 9:16 seguindo o rosto e acende a legenda palavra a palavra no tempo novo. A máquina propõe e você aprova cada corte antes de qualquer render.
Aplicar a lista de cortes da aula 17 na montagem final
A transcrição, a lista de cortes e o corte com trim e concat já existem: são o scripts/cortar_por_palavra.py da aula sobre transcrever com Scribe e cortar por palavra, que lê transcricoes/bruto.json e grava edicao/cortes.json. Esta aula não escreve outro script de corte nem chama a API de novo; ela chama o mesmo script com os limiares da montagem final do corte 9:16. Se o bruto mudou, transcreva de novo pelo script daquela aula antes de seguir.
Na montagem final, só vira candidata a pausa acima de 1 segundo, para o corte não soar picotado, e o respiro cai para 0,12 segundo, o suficiente para não engolir consoantes. As retomadas de frase aparecem na mesma lista, com o tipo retake, e só saem quando você as aprova.
Pausa e respiro da montagem final são escolhas editoriais
Os números abaixo são pontos de partida do curso para o corte 9:16, não limites do Scribe nem do FFmpeg.
- Pausa candidata acima de 1 s; abaixo disso o corte soa picotado
- Respiro de 0,12 s protege consoantes como o p de panela
- Intervalo sem palavra não prova silêncio: pode ser a mão mostrando a massa
- Retake sai só quando você aprova o item na lista de cortes
O teste: Ouça os cinco primeiros cortes antes de aprovar o resto da lista.
alexandrecaramaschi.com/educacao/frontends-com-vibecoding
# Rode na raiz video-ia. O script é o da aula 17; esta aula muda só os limiares.
set -euo pipefail
# 1) Candidatos com as pausas da montagem final (todos com "aprovado": false)
python scripts/cortar_por_palavra.py propor --pausa-max 1.0 --respiro 0.12
# 2) Ouça cada candidato e marque "aprovado": true em edicao/cortes.json no que deve sair.
# 3) Corte com áudio e vídeo juntos; a legenda por palavra sai no tempo novo
python scripts/cortar_por_palavra.py executar
# -> renders/cortado.mp4 e legendas/palavras_cortado.json
# 4) Duração antes e depois
ffprobe -v error -show_entries format=duration -of csv=p=0 midia/raw/bruto.mp4
ffprobe -v error -show_entries format=duration -of csv=p=0 renders/cortado.mp4O curso Astra profundo mostra o mesmo raciocínio do lado editorial em montar o vídeo a partir da função das tomadas. Aqui a função de cada tomada vira um item aprovado ou recusado em edicao/cortes.json.
No laboratório abaixo, faça o mesmo ciclo sem gastar nada: aperte as palavras da repetição e do erro corrigido, ligue a remoção de pausas e compare o comando FFmpeg e a legenda que ele gera com a lista que o script da aula 17 propôs.
Corte o vídeo apagando palavras da transcrição
Aperte as palavras para removê-las, ligue a remoção de pausas e copie o comando FFmpeg e a legenda SRT gerados.
Edição de vídeo pelo texto. A transcrição de 30 segundos no formato do ElevenLabs Scribe (cada palavra com início e fim, mais o evento "(risos)") mostra uma cozinheira explicando o bolo de rolo, com uma repetição ("é, é"), um erro corrigido (cinco ovos, são quatro) e duas pausas longas. Cada palavra apertada sai do corte, e um controle encurta as pausas acima de um limite em milissegundos. O laboratório mostra a linha do tempo original e editada, o tempo ganho, a lista de cortes, o comando FFmpeg (select e aselect, ou trim e concat) e a legenda SRT com os tempos reajustados.
A demonstração interativa carrega quando este trecho entra na tela.
Quando não há fala para guiar o corte, como num plano de apoio com som ambiente ou numa gravação em que a transcrição falhou, corte pelo volume do áudio. As duas abas fazem a mesma coisa por caminhos diferentes: um script seu, que você controla linha a linha, e o auto-editor, um programa pronto que corta pausas pelo volume e exporta a linha do tempo para editores profissionais. Nos dois, o plano de apoio sai de midia/raw/ e o resultado vai para renders/.
Duas formas de cortar pausas pelo volume do áudio
code silencedetect em Python
O filtro silencedetect do FFmpeg escreve no log onde cada silêncio começa e termina. O script lê esses tempos, mantém 0,12 segundo de folga em cada borda e monta um único filtro com trim, atrim e concat, reencodando uma vez só. Ajuste LIMIAR_DB para o ruído da sua cozinha: exaustor ligado pede um valor menos negativo.
"""Uso, na raiz video-ia:
python scripts/remover_silencio.py midia/raw/apoio.mp4 renders/apoio_limpo.mp4"""
import re
import subprocess
import sys
ENT, SAI = sys.argv[1], sys.argv[2]
LIMIAR_DB, MIN_SIL, MARGEM = -35, 0.45, 0.12 # dB, segundos, folga em cada borda
def duracao(caminho):
saida = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "csv=p=0", caminho], capture_output=True, text=True).stdout
return float(saida.strip())
# 1) O FFmpeg escreve silence_start e silence_end no stderr.
log = subprocess.run(["ffmpeg", "-i", ENT, "-af",
f"silencedetect=noise={LIMIAR_DB}dB:d={MIN_SIL}", "-f", "null", "-"],
capture_output=True, text=True).stderr
inicios = [float(x) for x in re.findall(r"silence_start: ([\d.]+)", log)]
fins = [float(x) for x in re.findall(r"silence_end: ([\d.]+)", log)]
total = duracao(ENT)
if len(fins) < len(inicios): # silêncio que vai até o fim do arquivo
fins.append(total)
# 2) Silêncios viram trechos falados, com margem para não comer consoantes.
falas, cursor = [], 0.0
for s, e in zip(inicios, fins):
fim_fala = min(s + MARGEM, total)
if fim_fala - cursor > 0.05:
falas.append((cursor, fim_fala))
cursor = max(e - MARGEM, 0)
if total - cursor > 0.05:
falas.append((cursor, total))
# 3) Um filtro só: trim e atrim de cada trecho, depois concat.
partes, rotulos = [], []
for i, (a, b) in enumerate(falas):
partes.append(f"[0:v]trim={a}:{b},setpts=PTS-STARTPTS[v{i}];"
f"[0:a]atrim={a}:{b},asetpts=PTS-STARTPTS[a{i}];")
rotulos.append(f"[v{i}][a{i}]")
filtro = "".join(partes) + "".join(rotulos) + f"concat=n={len(falas)}:v=1:a=1[v][a]"
subprocess.run(["ffmpeg", "-y", "-i", ENT, "-filter_complex", filtro, "-map", "[v]",
"-map", "[a]", "-c:v", "libx264", "-crf", "20", "-c:a", "aac", SAI], check=True)
print(f"{len(falas)} trechos mantidos de {total:.1f} s")terminal auto-editor 31.7.2
O auto-editor 31.7.2 saiu em 03/10/2026 e hoje é escrito em Nim. O PyPI parou na 29.3.1, de 04/11/2025, então instale o binário pelo site oficial. Os padrões documentados são limiar linear 0.04 e margem de 0.2sec; a exportação para o DaVinci Resolve deixa a decisão de corte aberta para revisão humana. Desde a 31.7.2, todo o progresso vai para o stderr e o stdout traz só dados, o que permite encadear a saída num script sem filtrar ruído.
# Rode na raiz video-ia. Corte automático por limiar em dB, com a margem padrão documentada
auto-editor midia/raw/apoio.mp4 --edit audio:-30dB --margin 0.2sec -o renders/apoio_limpo.mp4
# Padrão documentado (limiar linear 0.04) exportando a timeline para o DaVinci Resolve
auto-editor midia/raw/apoio.mp4 --edit audio:threshold=0.04 --margin 0.2sec --export resolve
# Skill oficial para o agente usar o auto-editor no projeto
npx skills add WyattBlue/auto-editorReenquadrar de 16:9 para 9:16 seguindo o rosto
O recorte central com crop é geometria pura: corta a faixa do meio e ignora quem está nela. Funciona quando a pessoa fica parada no centro. Quando ela anda até o fogão, o assunto sai do quadro vertical. O FFmpeg 9.0.2, estável mais recente na página oficial em 12/10/2026, não tem parâmetro de reenquadramento automático; esse recurso exige detectar o assunto a cada quadro, suavizar o movimento e mover o recorte ao longo do tempo.
O AutoFlip do Google, de fevereiro de 2020, é a referência aberta do método e está sem manutenção recente. A Video Intelligence API do Google detecta pessoas com caixas de posição, mas não entrega o vídeo vertical pronto. Por isso o caminho desta aula é um script próprio, e a comparação abaixo mostra quando cada recorte serve.
Recorte central fixo ou recorte que segue o rosto
Recorte central fixo
Geométrico
- Um filtro crop com posição constante
- Serve para assunto parado no centro
- Custo zero de processamento extra
- Corta quem anda até a bancada
Recorte que segue o rosto
Dinâmico
- Detecta o maior rosto a cada 0,5 s
- Suaviza a posição para não tremer
- Move o crop com sendcmd no tempo
- Pede ajuste quando o assunto é a panela
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O script usa o detector de rostos do OpenCV, uma biblioteca aberta de visão computacional, a cada meio segundo, e escolhe o maior rosto como quem fala. A suavização exponencial dá 85% de peso à posição anterior e 15% à nova, uma média que impede a janela de tremer. Os comandos de posição vão para edicao/crop_cmds.txt, lido pelo filtro sendcmd, que muda o x do crop no instante certo. No mesmo comando, o vídeo é escalado para 1080 por 1920 e recebe a legenda palavra a palavra, um bloco por palavra, montada a partir de legendas/palavras_cortado.json. Anúncios verticais seguem a mesma lógica de atenção, como mostra o curso Meta Ads 2027 em produzir Reels e Stories que seguram a atenção.
"""scripts/reframe_rosto.py: reenquadra renders/cortado.mp4 para 9:16 seguindo o rosto.
pip install opencv-python | Uso, na raiz video-ia: python scripts/reframe_rosto.py
Requer renders/cortado.mp4 e legendas/palavras_cortado.json (saídas do script da aula 17)."""
import json
import subprocess
from pathlib import Path
import cv2
ENT, SAI = "renders/cortado.mp4", "renders/final_9x16.mp4"
PALAVRAS = Path("legendas/palavras_cortado.json")
SRT = Path("legendas/palavras_cortado.srt")
CMDS = Path("edicao/crop_cmds.txt")
QUADROS = Path("qa/frames/final_9x16")
QUADROS.mkdir(parents=True, exist_ok=True)
def tempo_srt(ms):
h, ms = divmod(int(ms), 3_600_000)
m, ms = divmod(ms, 60_000)
s, ms = divmod(ms, 1000)
return f"{h:02}:{m:02}:{s:02},{ms:03}"
# Legenda palavra a palavra: um bloco por palavra, já no tempo novo do corte.
palavras = json.loads(PALAVRAS.read_text(encoding="utf-8"))
with SRT.open("w", encoding="utf-8") as f:
for n, p in enumerate(palavras, 1):
f.write(f"{n}\n{tempo_srt(p['startMs'])} --> {tempo_srt(p['endMs'])}\n{p['text']}\n\n")
cap = cv2.VideoCapture(ENT)
fps = cap.get(cv2.CAP_PROP_FPS)
W, H = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cw = int(H * 9 / 16) // 2 * 2 # largura par: exigência do H.264
detector = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
passo, amostras, quadro, ultimo_x = max(1, int(fps / 2)), [], 0, W / 2
while True:
ok, img = cap.read()
if not ok:
break
if quadro % passo == 0: # uma detecção a cada meio segundo
cinza = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rostos = detector.detectMultiScale(cinza, 1.1, 5, minSize=(80, 80))
if len(rostos):
x, y, w, h = max(rostos, key=lambda r: r[2] * r[3]) # maior rosto = quem fala
ultimo_x = x + w / 2
# Sem rosto no quadro, mantém a última posição conhecida.
amostras.append((quadro / fps, ultimo_x))
quadro += 1
cap.release()
if not amostras:
raise SystemExit("Nenhum quadro lido.")
# Suavização exponencial: 85% da posição anterior, 15% da nova.
suave, s = [], amostras[0][1]
for t, x in amostras:
s = 0.85 * s + 0.15 * x
suave.append((t, int(min(max(s - cw / 2, 0), W - cw))))
with CMDS.open("w", encoding="utf-8") as fh:
for t, x in suave:
fh.write(f"{t:.2f} crop x {x};\n")
filtro = (f"sendcmd=f={CMDS.as_posix()},crop={cw}:{H}:{suave[0][1]}:0,"
"scale=1080:1920:flags=lanczos,setsar=1,"
f"subtitles={SRT.as_posix()}:force_style='FontSize=14,Outline=2,Alignment=2,MarginV=180'")
subprocess.run(["ffmpeg", "-nostdin", "-y", "-i", ENT, "-vf", filtro,
"-c:v", "libx264", "-crf", "20", "-pix_fmt", "yuv420p",
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart", SAI], check=True)
# Quadros para revisão visual: o Claude lê imagens, não MP4.
subprocess.run(["ffmpeg", "-nostdin", "-y", "-i", SAI, "-vf", "fps=0.5,scale=540:-2",
str(QUADROS / "q_%03d.jpg")], check=True)
print("Vertical pronto:", SAI, "| quadros em", QUADROS)Os quadros extraídos servem à revisão visual pelo Claude, que lê imagens e não MP4, com a rubrica da aula de QA visual. Assista ao arquivo inteiro antes de aprovar, porque amostras não mostram áudio nem ritmo.
Acender a legenda palavra a palavra
A legenda queimada pelo reframe_rosto.py mostra uma palavra por vez. Para manter a frase na tela e acender só a palavra dita, troque o SRT pelo @remotion/captions. A função createTikTokStyleCaptions agrupa as palavras em páginas e devolve cada token com fromMs e toMs; a palavra acende enquanto o instante atual está entre os dois.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Use a versão do Remotion vigente na data, conferida com npm view remotion version (4.0.535, de 12/10/2026), com todos os pacotes @remotion/* no mesmo número, e alimente o componente com o legendas/palavras_cortado.json que o script da aula 17 gravou no tempo novo. O componente só acrescenta o espaço inicial e os dois campos nulos que o formato Caption exige.
// V=$(npm view remotion version) # 4.0.535 em 12/10/2026
// npm install remotion@$V @remotion/captions@$V --save-exact
import React from "react";
import { createTikTokStyleCaptions, type Caption } from "@remotion/captions";
import { AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig } from "remotion";
type Pagina = ReturnType<typeof createTikTokStyleCaptions>["pages"][number];
// Formato de legendas/palavras_cortado.json, gravado pelo script da aula 17
type Palavra = { text: string; startMs: number; endMs: number };
const PaginaLegenda: React.FC<{ page: Pagina }> = ({ page }) => {
const frame = useCurrentFrame(); // relativo ao início da Sequence
const { fps } = useVideoConfig();
const agoraMs = page.startMs + (frame / fps) * 1000;
return (
<div style={{ position: "absolute", bottom: 320, width: "100%", textAlign: "center",
fontSize: 72, fontWeight: 800, color: "white" }}>
{page.tokens.map((t) => (
<span key={t.fromMs}
style={{ color: agoraMs >= t.fromMs && agoraMs < t.toMs ? "#FFD400" : "white" }}>
{t.text}
</span>
))}
</div>
);
};
// palavras: o conteúdo de legendas/palavras_cortado.json (uma entrada por palavra, tempo novo)
export const Legendas: React.FC<{ palavras: Palavra[] }> = ({ palavras }) => {
const { fps } = useVideoConfig();
const captions: Caption[] = palavras.map((p) => ({
text: " " + p.text, startMs: p.startMs, endMs: p.endMs, timestampMs: null, confidence: null,
}));
const { pages } = createTikTokStyleCaptions({ captions, combineTokensWithinMilliseconds: 800 });
return (
<AbsoluteFill>
{pages.map((p, i) => (
<Sequence key={i} from={Math.round((p.startMs / 1000) * fps)}
durationInFrames={Math.max(1, Math.round((p.durationMs / 1000) * fps))}>
<PaginaLegenda page={p} />
</Sequence>
))}
</AbsoluteFill>
);
};O que mudou em outubro de 2026: vídeo longo e legenda sem componente
Cortar o vídeo longo inteiro antes de qualquer render
O agente edita sem assistir, pelos quatro sentidos descritos na aula 17: ouvidos na transcrição, olhos nos quadros amostrados, mãos no código da edição e revisão pela retranscrição. Num vídeo longo, o método do curso Vibe Viral, do Vibehub Academy, consultado em 12/10/2026, acrescenta uma ordem que poupa horas de máquina: faça o corte global do arquivo inteiro antes de montar qualquer cena ou renderizar qualquer trecho. O motivo está no próprio bruto: a retomada de uma frase errada costuma vir de 20 a 40 segundos depois do erro, e quem corta por trecho decide sem ter ouvido a segunda tentativa.
Antes do vídeo inteiro, rode um piloto com os 3 primeiros minutos: proponha, aprove, corte, reenquadre e legende só esse trecho, e confira ritmo e legenda. Se o piloto passa, o resto segue com os mesmos limiares; se não passa, você ajustou em 3 minutos o que custaria horas no arquivo todo. A prova final é a mesma da aula 17: retranscreva o renders/cortado.mp4 e compare com a transcrição original menos os cortes aprovados. Planeje a máquina pelos números que a página publica.
A escala de um vídeo longo editado por agente
11:09
final em 4K de um bruto de 25:48 com tela e câmera
cerca de 5 horas de máquina
43%
do bruto era conteúdo aproveitável
30 a 60 min
de máquina para cada Reels
16 GB
de memória no computador
e 20 a 30 GB livres em disco
Fonte: Vibehub Academy, curso Vibe Viral (consulta em 12/10/2026)
Legenda por palavra sem escrever componente
O Legendas.tsx dá controle total, mas há dois caminhos mais curtos. O primeiro roda na sua máquina, sem API: o transcribe do HyperFrames. Desde 14/09/2026, na 0.8.39, a transcrição em JSON traz um identificador estável por palavra, que a composição usa para acender uma de cada vez; desde 06/10/2026, com o Whisper, ele detecta o idioma e mostra o progresso; e desde 10/10/2026, na 0.8.144, --to grava SRT ou VTT direto. O Parakeet é opcional desde 28/09/2026, e npx hyperframes models list mostra os modelos de fala disponíveis. Desde 11/10/2026, na 0.8.149, a função transcribe também pode ser importada de hyperframes/api num script Node, sem chamar a CLI.
Transcreva o arquivo já cortado, nunca o bruto: em 01/10/2026 uma issue do Remotion mostrou legendas que perdiam o sincronismo depois do corte, e a orientação da skill oficial foi corrigida em 05/10/2026 para tirar os tempos do clipe editado. Para português, escolha um modelo multilíngue: os modelos do Whisper terminados em .en traduzem o áudio para o inglês em vez de transcrever. Deu certo quando o SRT abre no player junto do vídeo e a palavra dita coincide com a palavra na tela.
# Rode na raiz video-ia, sobre o vídeo JÁ cortado (renders/cortado.mp4), nunca sobre o bruto.
npx hyperframes models list # modelos de fala disponíveis; para português, nunca um terminado em .en
# SRT pronto para queimar com o filtro subtitles ou publicar como faixa
npx hyperframes transcribe renders/cortado.mp4 --to legendas/cortado.srt
# JSON com uma entrada por palavra e identificador estável, para a composição acender uma de cada vez
npx hyperframes transcribe renders/cortado.mp4 --json > legendas/cortado_hf.jsonSe a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O segundo caminho não pede código: o elemento Captions do Remotion Studio. Importe a transcrição (JSON de Caption, SRT, o JSON do ElevenLabs Speech-to-Text, desde que transcrito do vídeo cortado, ou o JSON verbose do Whisper), escolha o estilo e exporte. Importar exige o Studio 4.0.521 ou superior; escolher entre Basic, Rounded, Moving Pill, Popping Word e Word Highlight exige a 4.0.535, de 12/10/2026. Para a legenda palavra a palavra, escolha Word Highlight. Se o Studio não mostra a escolha de estilo, a versão instalada é anterior: atualize todos os pacotes juntos, como manda a aula 05. Prefira o componente quando a legenda precisa de regra própria, como a cor da marca na palavra-chave; prefira o Studio quando o estilo pronto basta.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O vídeo abaixo mostra o resultado esperado em 28 segundos: repare como a janela 9:16 acompanha o assunto sem saltos e como a palavra acesa coincide com a fala depois dos cortes.
Carregando a vídeo-aula: Reenquadrar do 16:9 para o 9:16
Do bruto ao vertical aprovado
Rode os passos na raiz video-ia, num bruto curto, de dois a três minutos, antes de usar no vídeo da semana.
Passo 1: Inspecione o bruto e escute um trecho
Rode ffprobe em midia/raw/bruto.mp4 e confirme uma faixa de vídeo e uma de áudio.
Deu certo quando: Vídeo e áudio existem e estão sincronizados.
Erro comum: Descobrir depois da transcrição paga que o arquivo não tem áudio.
Passo 2: Confira a transcrição gerada na aula 17
Deu certo quando: transcricoes/bruto.json tem palavras com start e end.
Erro comum: Usar uma transcrição de outra versão do bruto, com tempos que não batem.
Passo 3: Proponha os cortes com os limiares da montagem final
python scripts/cortar_por_palavra.py propor --pausa-max 1.0 --respiro 0.12
Deu certo quando: edicao/cortes.json lista pausas acima de 1 s, repetições e retakes, todos com aprovado false.
Erro comum: Chamar todo intervalo sem palavra de silêncio e cortar a demonstração.
Passo 4: Ouça e aprove cada corte, inclusive os retakes
Deu certo quando: Só os itens ouvidos estão com aprovado true.
Erro comum: Remover a primeira tentativa correta e manter a repetida.
Passo 5: Execute o corte
python scripts/cortar_por_palavra.py executar
Deu certo quando: renders/cortado.mp4 e legendas/palavras_cortado.json saem com o tempo novo.
Erro comum: Cortar só o áudio com silenceremove e perder o sincronismo.
Passo 6: Gere o vertical com python scripts/reframe_rosto.py
Deu certo quando: renders/final_9x16.mp4 tem 1080 por 1920, o rosto dentro do quadro e uma palavra acesa por vez.
Erro comum: Usar o SRT do bruto no vídeo já cortado.
Passo 7: Revise os quadros e assista ao arquivo inteiro
Deu certo quando: Mãos, prato e legenda permanecem visíveis do início ao fim.
Erro comum: Aprovar só pelas amostras, sem ouvir o áudio.
No fim você tem: Um vertical aprovado, com a lista de cortes, a transcrição original intacta e legenda sincronizada.
Guia prático: escolher os trechos dos cortes e entregar no padrão de cada rede
A aula limpa, reenquadra e legenda. Antes disso vem uma escolha que ela pressupõe: quais 30 a 60 segundos do vídeo longo viram cada corte. Depois vem a entrega: volume uniforme entre cortes, arquivo que começa a tocar rápido e especificação aceita pelo Reels e pelo Shorts. Você sai com o edicao/cortes_verticais.json aprovado e o scripts/entregar_vertical.py, que normaliza a loudness, a intensidade sonora percebida, em duas passadas e confere o arquivo.
Funções, casos de uso e aplicações
Funções
Recursos para escolher o trecho e entregar o corte
| Função ou recurso | O que faz | Quando usar | Data |
|---|---|---|---|
| Filtro loudnorm do FFmpeg | Normaliza pela norma EBU R128; padrão de -24 LUFS, pico real de -2 dB e faixa de 7; com print_format=json mede e devolve os números | Em todo corte, para que o volume não salte de um Reel para o seguinte | Em vigor em 12/10/2026 |
| Duas passadas com measured_I e linear=true | A primeira mede, a segunda aplica um ganho linear com os valores medidos; se não couber, volta ao modo dinâmico | Arquivo pronto, fora de transmissão ao vivo | Em vigor em 12/10/2026 |
| Especificação do Reels na API | De 3 s a 15 min, até 300 MB, de 23 a 60 quadros por segundo, largura até 1920, H.264 ou HEVC, AAC até 48 kHz | Conferência automática antes de criar o container | Em vigor em 12/10/2026 |
| YouTube Shorts de até três minutos | Vídeo vertical ou quadrado de até 3 min entra como Short | Corte acima de 60 s para o YouTube, sem trilha de terceiros | Desde 15/10/2024 |
| -movflags +faststart | Põe o índice do MP4 no começo do arquivo, e o vídeo começa a tocar antes de baixar inteiro | Toda entrega que vai para upload ou para página web | Em vigor em 12/10/2026 |
| Compreensão agêntica de vídeo do Gemini | Gemini 3.7 Flash, 3.6 Flash e 3.5 Flash-Lite analisam vídeo longo com até 88% menos tokens | Achar momentos visuais sem fala, como a massa enrolando, para abrir um corte | 01/09/2026 |
Casos de uso
Canal de uma nutricionista em Goiânia com um vídeo longo por semana
Marketing digital
Dor
Os dois cortes da semana saíam de trechos escolhidos no improviso, começando no meio de uma frase.
Solução
O Claude Code lê transcricoes/bruto.json e propõe três trechos com início em frase completa e gancho nos três primeiros segundos; ela aprova dois.
Resultado
Observar, em quatro semanas, se a retenção dos três primeiros segundos dos cortes aprovados fica acima da média dos cortes improvisados.
Cenário construído para fins didáticos. Números não correspondem a cliente específico.
Escola de música em São Paulo que publica aulas curtas em três redes
Educação
Dor
Um corte saía alto e o seguinte baixo, e quem rolava o feed tirava o som.
Solução
Todo corte passa pelo entregar_vertical.py com o mesmo alvo de loudness antes do upload.
Resultado
Conferir no relatório do script se os cortes da semana ficam a menos de 1 LU do alvo; ouvir dois seguidos no celular antes de publicar.
Cenário construído para fins didáticos. Números não correspondem a cliente específico.
Na prática
"""entregar_vertical.py: normaliza a loudness do corte em duas passadas e confere a entrega.
Uso (na raiz video-ia): python scripts/entregar_vertical.py renders/final_9x16.mp4 renders/entrega_9x16.mp4"""
import json
import subprocess
import sys
ent, sai = sys.argv[1], sys.argv[2]
ALVO = "loudnorm=I=-14:TP=-1.5:LRA=11" # escolha do projeto para celular, não número das redes
# 1a passada: só mede; o JSON do loudnorm sai no fim do stderr
log = subprocess.run(["ffmpeg", "-hide_banner", "-i", ent, "-af", ALVO + ":print_format=json",
"-f", "null", "-"], capture_output=True, text=True).stderr
m = json.loads(log[log.rindex("{"):log.rindex("}") + 1])
# 2a passada: aplica com os valores medidos; -ar 48000 porque o modo dinâmico sobe para 192 kHz
filtro = (f"{ALVO}:measured_I={m['input_i']}:measured_TP={m['input_tp']}:"
f"measured_LRA={m['input_lra']}:measured_thresh={m['input_thresh']}:"
f"offset={m['target_offset']}:linear=true")
subprocess.run(["ffmpeg", "-hide_banner", "-y", "-i", ent, "-af", filtro, "-ar", "48000",
"-c:v", "copy", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", sai],
check=True) # -c:v copy: o vídeo já saiu em H.264 do reframe_rosto.py
info = json.loads(subprocess.run(["ffprobe", "-v", "error", "-print_format", "json",
"-show_streams", "-show_format", sai],
capture_output=True, text=True).stdout)
v = next(s for s in info["streams"] if s["codec_type"] == "video")
n, d = map(int, v["r_frame_rate"].split("/"))
dur, mb = float(info["format"]["duration"]), int(info["format"]["size"]) / 1e6
erros = [t for ok, t in [(v["height"] > v["width"], "não está na vertical"),
(23 <= n / d <= 60, f"{n / d:.2f} quadros por segundo"),
(v["width"] <= 1920, "largura acima de 1920"),
(3 <= dur <= 180, f"{dur:.1f} s fora de 3 s a 3 min"),
(mb <= 300, f"{mb:.0f} MB acima de 300")] if not ok]
print(f"{sai}: {dur:.1f} s, {n / d:.2f} fps, {v['width']}x{v['height']}, {mb:.1f} MB, entrada {m['input_i']} LUFS")
sys.exit("REPROVADO: " + "; ".join(erros) if erros else 0)Do vídeo longo ao arquivo de entrega
Passo 1: Aprove dois dos três trechos em edicao/cortes_verticais.json
Deu certo quando: Cada trecho aprovado começa numa frase completa e tem um gancho que você leria em voz alta.
Erro comum: Aprovar pelo texto sem ouvir os três primeiros segundos.
Passo 2: Corte cada trecho e passe pelo ciclo da aula
Cortes de pausa, reframe_rosto.py e legenda por palavra no tempo novo.
Deu certo quando: Um renders/final_9x16.mp4 por trecho aprovado.
Passo 3: Rode o entregar_vertical.py em cada corte
Deu certo quando: A saída mostra duração, quadros por segundo, tamanho e nenhuma linha REPROVADO.
Erro comum: Normalizar o vídeo longo inteiro e depois cortar; cada corte precisa da própria medida.
Passo 4: Ouça dois cortes seguidos no celular
Deu certo quando: Nenhum dos dois pede para mexer no volume.
Armadilhas
Entrega conferida corte a corte
Meça, normalize e confira cada corte antes do upload.
- Loudness medida em cada corte, depois de separado do vídeo longo
- Sem -ar 48000, o modo dinâmico entrega áudio a 192 kHz
- Short acima de 1 min com trilha reivindicada fica bloqueado (aula 25)
- Reencodar o vídeo à toa perde qualidade; copie quando já é H.264
O teste: Rode o script em dois cortes e compare a linha de LUFS de entrada com o alvo.
alexandrecaramaschi.com/educacao/frontends-com-vibecoding
Quatro situações pedem decisão durante a revisão. Se a pausa contém uma ação importante, deixe aquele item com aprovado false e o intervalo fica no vídeo, mesmo sem palavras. Se o assunto é o prato e não o rosto, abandone o detector de rostos naquela cena e defina a posição do recorte à mão, por cena. Se a legenda mostra frases sem tempo por palavra, gere os tempos por palavra no Scribe em vez de dividir a duração da frase em partes iguais. Se o respiro corta consoantes como o p de panela, proponha de novo com um respiro maior e revise outra vez.
Na Cozinha da Rafa, canal de receitas regionais em Recife com três vídeos por semana, o vídeo longo 16:9 vira os dois cortes 9:16. A pausa em que a Rafa mostra o ponto da massa do bolo de rolo fica; a repetição de uma frase sai como retake aprovado na lista de cortes; a legenda "duas colheres" é comparada com a fala e com a quantidade que aparece na tela. Para quem publica também em áudio, o curso SEO e GEO para podcasts usa a mesma transcrição para publicar o episódio com capítulos revisados.
Rode o ciclo completo num bruto seu de três minutos e guarde edicao/cortes.json junto do projeto. Está pronto quando renders/final_9x16.mp4 tem o rosto dentro do quadro do início ao fim, cada palavra acende no instante em que é dita, a transcrição original segue intacta e você assistiu ao arquivo inteiro antes de aprovar.
Seu caderno neste capítulo
Abrir o caderno completoSelecione um trecho do capítulo para destacar ou anotar. Nos vídeos e áudios, use Anotar este momento. No teclado, selecione com Shift e as setas e use Alt+Shift+D para destacar ou Alt+Shift+N para anotar.
Salvo neste navegador. Entre na sua conta para levar o caderno a outros aparelhos.
Entre na sua conta para compartilhar o que aprendeu e convidar alguém para estudar com você.
Conexões deste capítulo
Veja o que estudar antes e para onde seguir depois, explore os conceitos e compare abordagens em outros cursos. Sugestões marcadas como “sugerido pelo grafo” vêm dos assuntos em comum, não do índice do curso.
Estude antes
- CursoClaude Code CLI: Programação com IA
- CursoAstra profundosugerido pelo grafoA quinta parte do Astra planeja o vídeo e edita o material bruto com o modelo dirigindo a ferramenta. O curso de vídeo com IA aprofunda esse trabalho com Claude Code, Codex e ElevenLabs, do roteiro à publicação. O grafo já sugeria esta ordem pela regra de entrada da trilha; com a ordem anterior curada, ela passa a ser declarada (Etapa 4, wave 3).
- CursoSetup Completo: do zero ao Claude Codesugerido pelo grafo7 capítulos de “Vídeo com IA: Claude Code, Codex e ElevenLabs” usam conceitos apresentados em “Setup Completo: do zero ao Claude Code”.
Depois, siga para
- CursoFrontends com Vibecodingsugerido pelo grafo21 capítulos de “Frontends com Vibecoding” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoDo Agile ao Agentic Operating Modelsugerido pelo grafo5 capítulos de “Do Agile ao Agentic Operating Model” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoAgentes de IA Autônomos: Do Conceito à Produçãosugerido pelo grafo7 capítulos de “Agentes de IA Autônomos: Do Conceito à Produção” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoLLM FinOps: o custo da IA para quem assina o orçamentosugerido pelo grafo4 capítulos de “LLM FinOps: o custo da IA para quem assina o orçamento” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
O mesmo assunto em outros cursos
- Frontends com VibecodingConstruir profundidade e dirigir a montagem de vídeoExaminar conexões de Construir profundidade e dirigir a montagem de vídeo
- Astra profundoMontar um filme a partir do material disponívelExaminar conexões de Montar um filme a partir do material disponível
- Meta Ads 2027Produzir Reels e Stories que seguram a atenção nos primeiros segundosExaminar conexões de Produzir Reels e Stories que seguram a atenção nos primeiros segundos
Leve uma ideia para a prática
Escolha uma atividade no painel e volte a este curso quando quiser.
Atividades e evolução ficam reunidas no painel da sua conta.
Laboratórios ao vivo deste curso
Cada laboratório monta a biblioteca real dentro da aula, com controles para você mexer e o código do exemplo para copiar. O texto de reserva descreve a demonstração para quem lê sem JavaScript. O link abre o capítulo já no ponto do laboratório.
MediaRecorder, Canvas 2D e Long Animation Frames (plataforma)
Grave a cena em tempo real e veja os quadros que se perdem
Suba as partículas, force um bloqueio de 300 ms e grave seis segundos; compare com o render quadro a quadro desta aula.
Pacote
Abrir no capítuloMontar vídeo em HTML com HyperFrames e escolher entre ele e o Remotionsem pacoteMotion
Ver agentes em paralelo trocando mensagens numa linha do tempo
Reproduza a cena, percorra o tempo pelo controle e compare o tempo de parede com a soma das barras de cada agente.
Pacote
Abrir no capítuloParalelizar cenas com subagentes sem estourar o limite das APIsmotionReact (sem dependência extra), com as APIs do Veo e do fal.ai
Prompt de vídeo em cinco camadas, pronto para a API
Escolha plano, câmera, luz, ação e áudio e copie o prompt e o JSON para o Veo ou para o fal.ai.
Pacote
Abrir no capítuloEscrever prompt de vídeo em cinco camadas e manter o personagem consistentereactReact (sem dependência extra), com o comando para o FFmpeg
Corte o vídeo apagando palavras da transcrição
Aperte as palavras para removê-las, ligue a remoção de pausas e copie o comando FFmpeg e a legenda SRT gerados.
Pacote
Abrir no capítuloRemover pausas e reenquadrar o vídeo de 16:9 para 9:16 com legenda por palavrareactReact (sem dependência extra)
Quanto custa cada etapa do seu pipeline de vídeo
Troque a ferramenta de cada etapa e o percentual de b-roll e veja qual etapa pesa no custo por minuto e por vídeo.
Pacote
Abrir no capítuloOrquestrar o pipeline com etapas idempotentes e teto de gastoreact
Capítulos vizinhos em Vídeo com IA: Claude Code, Codex e ElevenLabs
- 20Escrever prompt de vídeo em cinco camadas e manter o personagem consistente
- 21Chamar Veo, Kling, Seedance e Runway por API com polling e custo controlado
- 22Remover pausas e reenquadrar o vídeo de 16:9 para 9:16 com legenda por palavra
- 23Orquestrar o pipeline com etapas idempotentes e teto de gasto
- 24Publicar por API no YouTube, Reels e TikTok com OAuth e rótulo de IA