Como verificar um vídeo após aumentar a taxa de quadros no Runway
Fluxo completo para preparar e enviar um vídeo local, executar enhance_frame_rate, consultar a tarefa, salvar a saída e validar FPS, artefatos e áudio.
Conteúdo

Você pode ter um vídeo local finalizado, mas ainda não ter a saída da Runway com a nova taxa de quadros. Antes da validação, é preciso preparar e enviar o arquivo, informar targetFramerate, aguardar a tarefa e salvar o resultado. Este guia cobre todo esse fluxo REST e depois verifica FPS, artefatos de movimento, cortes e sincronia de áudio.
A Runway adicionou enhance_frame_rate ao Runway Dev em 17 de setembro de 2026. A operação usa o video upscale endpoint e aceita 24, 25, 30, 48, 50, 60, 120, 23_98 (23,98 fps), 29_97 (29,97 fps) e 59_94 (59,94 fps); cada entrada é limitada a 300 segundos, e a nota de lançamento informa 1 credit a cada 2 segundos.
Não aprove o resultado apenas porque ele parece mais suave. Escolha primeiro a cadência exata exigida pelo destino, depois confira metadados, movimentos de risco, cortes e sincronia no início, meio e fim, e finalize testando na timeline e plataforma reais.
Escolha primeiro a cadência de entrega; pare se não houver especificação exata
Escolha o valor exato da timeline, emissora, plataforma de anúncios ou especificação do cliente antes de enviar. 29_97 e 30, assim como 59_94 e 60, parecem quase iguais, mas uma troca incorreta pode exigir novo transcode ou a repetição do processo em conteúdo longo, broadcast e projetos com fontes mistas.
| Alvo | Base comum para a escolha | Confirme antes da entrega |
|---|---|---|
23_98 / 24 | A timeline ou o cliente exige cadência cinematográfica | Se a exigência é exatamente 23,98, não 24 inteiro |
25 / 50 | Cadeia de produção 25/50 fps ou especificação regional | Timeline, legendas, áudio e demais mídias usam a mesma cadência |
29_97 / 30 | O destino nomeia explicitamente um dos dois | Não substituir um pelo outro sem validação |
59_94 / 60 | Conteúdo de muito movimento ou plataforma que pede alta taxa | Movimento mais suave não significa recuperação de detalhes perdidos |
48 / 120 | Timeline específica, câmera lenta ou entrega em alta taxa | Usar apenas quando o destino exigir; maior não é sempre melhor |
Se o briefing disser apenas “deixe mais fluido”, peça a especificação final. Caso contrário, você pode gerar um arquivo válido a 60 fps que ainda não se encaixa corretamente em uma timeline de 59,94 fps.
Registre a linha de base para localizar qualquer falha
Registre taxa, duração, codec e faixas de áudio do original antes do processamento. Sem essa referência, fica difícil saber se uma faixa ausente, duração alterada ou cauda congelada veio da origem, da saída da Runway ou de um transcode posterior.
- Nome do arquivo, duração, dimensões, codec e taxa original.
- Se a origem tem taxa constante (CFR) ou variável (VFR).
- Quantidade de faixas de áudio, sample rate, canais e duração aproximada.
- Taxa alvo e origem do requisito, por exemplo: “o cliente exige
59_94”. - De três a cinco timecodes de risco: panorâmicas rápidas, mãos, linhas finas, bordas de oclusão, flashes, transições, legendas ou UI.
- Pelo menos três âncoras de sincronia perto do início, meio e fim.
Essa linha de base evita que a revisão se resuma a “ficou mais suave” enquanto uma mudança de duração, faixa ausente ou incompatibilidade de entrega passa despercebida.
Confirme primeiro que o vídeo local pode entrar no fluxo
Verifique formato, duração e tamanho antes de chamar a API. Uma entrada de enhance_frame_rate não pode passar de 300 segundos, e um upload efêmero deve ter entre 512 bytes e 200 MB. Prefira contêineres e codecs suportados, como MP4 com H.264, H.265 ou AV1, e divida conteúdos longos em cortes naturais.
Se o vídeo já estiver em object storage, a URL HTTPS pode ser usada diretamente como videoUri. Ela precisa usar domínio em vez de IP, aceitar HEAD, devolver Content-Type e Content-Length corretos e não depender de redirecionamento; o limite de vídeo por URL é 32 MB. Para um master local comum, o upload efêmero evita essas exigências de hospedagem.
Confirme também que a conta possui credits comprados. A nota informa 1 credit a cada 2 segundos, mas não explica o arredondamento de frações; portanto, guarde estimatedCost no envio e o cost final da tarefa.
Use este script para enviar, submeter, esperar e baixar
O exemplo REST mantém todos os passos críticos visíveis. Ele solicita a API Key sem exibi-la, valida duração e tamanho locais, cria um upload efêmero, transfere o arquivo, envia a tarefa, consulta a cada cinco segundos e baixa a saída bem-sucedida.
Instale a dependência Python e confirme que ffprobe está disponível:
python3 -m pip install requests
Salve o conteúdo abaixo como runway_fps.py:
from __future__ import annotations
import getpass, json, os, random, subprocess, sys, time
from pathlib import Path
import requests
API = "https://api.dev.runwayml.com"
FPS = {"24", "25", "30", "48", "50", "60", "120", "23_98", "29_97", "59_94"}
RETRYABLE = {429, 502, 503, 504}
def api(session, method, path, body=None):
for attempt in range(6):
response = session.request(method, API + path, json=body, timeout=60)
if response.status_code < 400:
return response
if response.status_code in RETRYABLE and attempt < 5:
time.sleep((2**attempt) * (1 + random.random() * 0.5))
continue
raise RuntimeError(f"HTTP {response.status_code}: {response.text}")
raise RuntimeError("RETRY_LIMIT_REACHED")
def main():
if len(sys.argv) not in {3, 4}:
raise SystemExit("python runway_fps.py INPUT_VIDEO TARGET_FPS [OUTPUT_VIDEO]")
source = Path(sys.argv[1])
target = sys.argv[2]
output = Path(sys.argv[3]) if len(sys.argv) == 4 else Path(f"runway-{target}fps.mp4")
if target not in FPS:
raise SystemExit(f"UNSUPPORTED_TARGET_FRAMERATE: {target}")
if not source.is_file():
raise SystemExit(f"INPUT_NOT_FOUND: {source}")
if not 512 <= source.stat().st_size <= 200 * 1024 * 1024:
raise SystemExit(f"INVALID_UPLOAD_SIZE_BYTES: {source.stat().st_size}")
duration = float(subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", str(source)],
check=True, capture_output=True, text=True,
).stdout.strip())
if not 0 < duration <= 300:
raise SystemExit(f"INVALID_DURATION_SECONDS: {duration}")
key = os.getenv("RUNWAYML_API_SECRET") or getpass.getpass("RUNWAYML_API_SECRET: ")
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {key}",
"X-Runway-Version": "2024-11-06",
"Content-Type": "application/json",
})
upload_init = api(session, "POST", "/v1/uploads", {
"filename": source.name,
"type": "ephemeral",
}).json()
with source.open("rb") as handle:
upload = requests.post(
upload_init["uploadUrl"],
data=upload_init["fields"],
files={"file": (source.name, handle)},
timeout=300,
)
if upload.status_code >= 400:
raise RuntimeError(
f"UPLOAD_FAILED_REQUEST_NEW_UPLOAD: HTTP {upload.status_code}: {upload.text}"
)
created = api(session, "POST", "/v1/video_upscale", {
"model": "enhance_frame_rate",
"videoUri": upload_init["runwayUri"],
"targetFramerate": target,
}).json()
task_id = created["id"]
print(json.dumps({"id": task_id, "estimatedCost": created.get("estimatedCost")}, indent=2))
while True:
task = api(session, "GET", f"/v1/tasks/{task_id}").json()
status = task["status"]
if status in {"PENDING", "THROTTLED", "RUNNING"}:
time.sleep(5)
continue
if status == "SUCCEEDED":
urls = task.get("output") or []
if not urls:
raise RuntimeError("SUCCEEDED_WITHOUT_OUTPUT")
with requests.get(urls[0], stream=True, timeout=300) as download:
download.raise_for_status()
with output.open("wb") as saved:
for chunk in download.iter_content(1024 * 1024):
if chunk:
saved.write(chunk)
break
if status == "FAILED":
raise RuntimeError(json.dumps({
"status": status,
"failure": task.get("failure"),
"failureCode": task.get("failureCode"),
"cost": task.get("cost"),
}, ensure_ascii=False))
if status == "CANCELLED":
raise RuntimeError(json.dumps({"status": status, "cost": task.get("cost")}))
raise RuntimeError(f"UNKNOWN_TASK_STATUS: {status}")
subprocess.run([
"ffprobe", "-v", "error", "-show_entries",
"stream=codec_name,width,height,r_frame_rate,avg_frame_rate,time_base,duration:format=duration",
"-of", "json", str(output),
], check=True)
print(output.resolve())
if __name__ == "__main__":
main()
Exemplo para converter input.mp4 em 60 fps e salvar como output-60fps.mp4:
python3 runway_fps.py input.mp4 60 output-60fps.mp4
Digite a chave somente quando aparecer RUNWAYML_API_SECRET:. O valor não é exibido nem entra no histórico do shell, e o script não o grava. Se a variável de ambiente já estiver configurada com segurança, ela será usada.
Entenda as três etapas de API usadas pelo script
A geração termina apenas quando as três etapas têm sucesso. Os campos diferenciam maiúsculas de minúsculas: no JSON REST, use videoUri e targetFramerate.
| Etapa | Requisição | Conteúdo obrigatório | Sinal de sucesso |
|---|---|---|---|
| Inicializar upload | POST https://api.dev.runwayml.com/v1/uploads | filename, type: "ephemeral" | Retorna uploadUrl, fields, runwayUri |
| Enviar melhoria | POST https://api.dev.runwayml.com/v1/video_upscale | model: "enhance_frame_rate", videoUri, targetFramerate | Retorna id e estimatedCost |
| Consultar tarefa | GET https://api.dev.runwayml.com/v1/tasks/{id} | ID no caminho | status: "SUCCEEDED" e output não vazio |
Depois da inicialização, faça um POST multipart para uploadUrl, preserve todos os itens de fields e envie o vídeo no campo file. runwayUri só fica pronto após a transferência, e vale por 24 horas.
Baixe e armazene o resultado imediatamente após o sucesso
Continue aguardando em PENDING, THROTTLED ou RUNNING; a Runway diz que não se deve esperar atualização da mesma tarefa em intervalo menor que cinco segundos. Leia output[0] somente em SUCCEEDED. FAILED e CANCELLED são estados terminais sem sucesso.
As URLs de saída costumam expirar em 24–48 horas. Baixe o arquivo logo para armazenamento persistente e não entregue a URL temporária ao usuário final. Se ela expirar, consulte a mesma tarefa para obter uma nova URL antes de pagar por outra geração. O download confirma apenas o término da tarefa de API; as verificações abaixo ainda são necessárias.
Trate as falhas por tipo, sem repetir tudo cegamente
Se o POST multipart para uploadUrl falhar, não reutilize o mesmo upload assinado; chame /v1/uploads novamente. Em 400, 401, 404 ou 405, corrija entrada, chave, recurso ou método. O exemplo só repete 429, 502, 503 e 504 com backoff exponencial e jitter.
Quando a tarefa estiver FAILED, guarde failure, failureCode e cost: não repita SAFETY.*; corrija a mídia antes de reenviar ASSET.INVALID; investigue a entrada antes de repetir INTERNAL.BAD_OUTPUT.*; aguarde antes de repetir INPUT_PREPROCESSING.INTERNAL, INTERNAL, código ausente ou THIRD_PARTY.UNAVAILABLE. Não entre em loop infinito.
Etapa 1: use ffprobe para confirmar que o arquivo atende ao alvo
Use ffprobe para validar taxa média, time base, contagem real, duração e faixas de áudio antes de julgar a imagem. Uma única etiqueta de FPS no sistema ou player não basta para aprovar o arquivo.
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate,time_base,duration \
-of json output.mp4
ffprobe -v error -select_streams v:0 -count_frames \
-show_entries stream=nb_read_frames,avg_frame_rate,r_frame_rate,duration \
-of json output.mp4
ffprobe -v error \
-show_entries format=duration:stream=index,codec_type,codec_name,sample_rate,channels,duration \
-of json output.mp4
Confira:
avg_frame_ratecorresponde ao alvo ou a uma representação racional equivalente.r_frame_rateeavg_frame_ratenão apresentam conflito inexplicado; diferença grande exige investigar VFR.- Em arquivo próximo de CFR,
nb_read_framesfica razoavelmente próximo de duração multiplicada pelo fps alvo. - A duração de saída coincide com a origem, sem corte no final nem cauda congelada adicionada.
- Dimensões, codec e faixas de áudio atendem à especificação de entrega.
- A duração do áudio não difere inesperadamente da duração do vídeo.
Para 29,97 e 59,94, ferramentas costumam exibir 30000/1001 e 60000/1001. A fração não é falha.
Etapa 2: revise primeiro os planos de maior risco
Comece por movimento rápido, bordas de oclusão, texto fino e pontos de corte, pois esses trechos expõem falhas de interpolação mais cedo. Revise os itens abaixo em 100%, quadro a quadro ou em velocidade reduzida:
- panorâmicas, tracking e objetos rápidos;
- mãos, dedos, cabelo, armações de óculos e lábios;
- grades, persianas, malhas, texto pequeno e linhas finas de UI;
- objetos em primeiro plano cruzando ou revelando bordas do fundo;
- água, fumaça, partículas, folhas e texturas de alta frequência;
- flashes, cortes secos, dissolvências e quadros ao redor da mudança de plano.
Procure defeitos reproduzíveis, não uma impressão vaga de nitidez: contornos duplos, ghosting, bordas tortas, objetos que somem por um quadro, textura pulsante, membros deformados, quadros misturados no corte ou tremor em texto estático.
Ao encontrar um problema, registre o timecode exato, a taxa alvo e os trechos de origem e saída. Isso ajuda a separar um defeito preexistente de um novo problema ou de uma diferença do decodificador.
Etapa 3: verifique a sincronia no início, meio e fim
O início sincronizado não prova que o arquivo inteiro está correto; confira início, meio e fim para separar atraso fixo de drift progressivo. Siga esta ordem:
- Ache uma âncora clara no início, como palma, consoante plosiva, impacto, aterrissagem ou corte visual.
- Repita a verificação no meio e no fim.
- Deslocamento parecido nos três pontos sugere atraso fixo.
- Erro que cresce até o fim aponta para duração, time base ou interpretação da taxa.
- Para lip sync, confira início, meio e fim de uma fala contínua, não uma única sílaba.
A nota de lançamento não descreve o tratamento do áudio. Portanto, não suponha que a faixa sempre seja preservada sem alterações ou sincronizada automaticamente. Avalie o arquivo entregue.
Etapa 4: teste novamente na timeline e plataforma reais
Sempre teste na timeline de edição e na plataforma finais, porque um NLE ou segundo transcode pode reinterpretar a cadência, alterar a velocidade ou perder uma faixa. Faça pelo menos estas duas verificações:
- Coloque o arquivo na timeline prevista e confirme que o NLE não o reinterpreta, altera a velocidade nem perde uma faixa.
- Teste na plataforma ou dispositivo final e confirme que uma segunda transcodificação não mudou cadência, legendas ou sincronia.
Se a plataforma transcodificar novamente, preserve tanto a saída da Runway quanto a versão da plataforma. Inspecione cada uma separadamente antes de atribuir o problema ao arquivo anterior.
Use esta tabela para aprovar, refazer ou manter um trecho original
Aprove o arquivo somente quando todos os itens críticos atenderem à especificação. Se apenas um plano falhar, refaça esse segmento ou mantenha o original antes de processar o programa inteiro novamente.
| Verificação | Condição de aprovação | Ação em caso de falha |
|---|---|---|
| Taxa alvo | Cadência exata; 29,97/59,94 não são rotulados como 30/60 | Corrigir o alvo ou a interpretação da timeline |
| Duração e quadros | Duração igual à origem; em CFR a contagem fica perto do esperado | Investigar VFR, truncamento, cauda congelada e time base |
| Dimensões e codec | Atendem ao editor ou canal | Reempacotar ou transcodificar conforme a especificação |
| Movimento rápido | Sem imagem dupla, deformação ou desaparecimento inaceitável | Marcar timecodes; tentar outro alvo ou manter o trecho original |
| Cortes e flashes | Sem quadros misturados, repetidos ou piscadas anômalas | Dividir em corte natural, reprocessar e revisar a emenda |
| Texto e UI | Glifos, linhas finas e overlays estáticos permanecem estáveis | Reaplicar gráficos na pós-produção |
| Sincronia de áudio | Sem deslocamento ou drift visível no início, meio e fim | Comparar durações/time base e depois realinhar ou transcodificar |
| Integridade | Decodificação completa, final intacto e faixas presentes | Baixar novamente, reempacotar ou repetir a tarefa |
Não passe de 60 para 120 fps nestes casos
Fique na menor taxa que atende à entrega quando 120 fps só aumentar tamanho e trabalho posterior. Não eleve a taxa nos seguintes casos:
- o destino só exige 24, 25, 29,97 ou 30 fps;
- a origem já tem ghosting forte, blocos de compressão ou motion blur;
- legendas, UI ou linhas finas ficam menos estáveis;
- um problema de sincronia ainda não foi explicado;
- a plataforma final forçará transcodificação para taxa menor;
- não há benefício visível, mas aumentam armazenamento, decodificação ou transcode posterior.
Taxa de quadros é parâmetro de entrega, não uma nota de qualidade isolada. O critério é “cumpre a cadência exigida sem novos defeitos inaceitáveis”, não “tem o maior número”.
Conclua a entrega em dez etapas
A ordem com menos retrabalho é especificação e referência, upload e envio, espera e armazenamento, seguida de validação técnica, visual e no ambiente real.
- Escolha o
targetFramerateexato pela especificação de destino. - Registre taxa, duração, áudio e timecodes de risco com
ffprobe; confirme o limite de 300 segundos. - Para arquivo local, chame
POST /v1/uploadse guardeuploadUrl,fields,runwayUri. - Envie o formulário multipart para
uploadUrl; peça novo upload se falhar. - Chame
POST /v1/video_upscalecommodel,videoUri,targetFramerate. - Guarde o
idda tarefa eestimatedCost. - Consulte
GET /v1/tasks/{id}a cada cinco segundos até um estado terminal. - Em
SUCCEEDED, baixe e persistaoutput; trateFAILEDeCANCELLEDpor tipo de erro. - Verifique cadência, duração, artefatos, cortes e áudio com
ffprobee revisão quadro a quadro. - Teste na timeline e plataforma finais antes da entrega.
Referências oficiais: Models, Inputs, Uploads, Video upscale API Reference, Task API Reference, Outputs, HTTP errors, Task failures e API Changelog. Campos e passos verificados em 26 de setembro de 2026.