Comment vérifier une vidéo après augmentation de la fréquence d’images dans Runway
Flux complet depuis la préparation et l’upload d’une vidéo locale jusqu’à enhance_frame_rate, au suivi de tâche, à la sauvegarde et aux contrôles FPS et audio.
Sommaire

Vous pouvez avoir une vidéo locale finalisée sans disposer encore de la sortie Runway à la nouvelle fréquence. Avant la recette, il faut préparer et téléverser le fichier, envoyer targetFramerate, attendre la tâche et enregistrer le résultat. Ce guide couvre toute cette chaîne REST, puis vérifie la cadence, les artefacts de mouvement, les coupes et la synchronisation audio.
Runway a ajouté enhance_frame_rate à Runway Dev le 17 septembre 2026. L’opération utilise le video upscale endpoint et accepte 24, 25, 30, 48, 50, 60, 120, 23_98 (23,98 fps), 29_97 (29,97 fps) et 59_94 (59,94 fps) ; chaque entrée est limitée à 300 secondes et la note de lancement indique 1 credit pour 2 secondes.
N’acceptez pas le résultat uniquement parce qu’il paraît plus fluide. Choisissez d’abord la cadence exacte exigée par la destination, puis contrôlez métadonnées, mouvements à risque, coupes et synchronisation au début, au milieu et à la fin, avant de tester sur la timeline et la plateforme réelles.
Choisissez d’abord la cadence de livraison ; arrêtez-vous sans spécification exacte
Choisissez la valeur exacte dans la timeline, les exigences du diffuseur, de la plateforme publicitaire ou du client avant tout envoi. 29_97 et 30, comme 59_94 et 60, semblent proches, mais une substitution incorrecte peut imposer un nouveau transcodage ou retraitement en long format, diffusion ou projet multi-source.
| Cible | Motif habituel du choix | À confirmer avant livraison |
|---|---|---|
23_98 / 24 | La timeline ou le client exige une cadence cinéma | S’il faut exactement 23,98 plutôt que 24 entier |
25 / 50 | Chaîne de production 25/50 fps ou norme régionale | Timeline, sous-titres, audio et autres plans utilisent la même cadence |
29_97 / 30 | Le système de destination nomme explicitement l’une des deux | Ne pas remplacer l’une par l’autre sans validation |
59_94 / 60 | Contenu très mobile ou plateforme exigeant une fréquence élevée | Une lecture plus fluide ne restaure pas nécessairement les détails perdus |
48 / 120 | Timeline particulière, ralenti ou livraison à fréquence élevée | À utiliser seulement si la destination l’exige ; plus élevé n’est pas toujours meilleur |
Si le brief dit seulement « rendre la vidéo plus fluide », demandez la spécification finale. Sinon, vous pouvez produire un fichier valide à 60 fps qui ne convient toujours pas à une timeline de 59,94 fps.
Conservez la référence source pour retrouver l’origine d’un défaut
Notez la fréquence, la durée, le codec et les pistes audio de la source avant traitement. Sans cette référence, il devient difficile d’attribuer une piste manquante, une durée modifiée ou une fin figée à la source, à la sortie Runway ou à un transcodage ultérieur.
- Nom du fichier, durée, dimensions, codec et fréquence d’origine.
- Si la source est à fréquence constante (CFR) ou variable (VFR).
- Nombre de pistes audio, sample rate, canaux et durée approximative.
- Fréquence cible et origine de l’exigence, par exemple « le client exige
59_94». - Trois à cinq timecodes à risque : panoramiques rapides, mains, lignes fines, bords d’occlusion, flashes, transitions, sous-titres ou UI.
- Au moins trois repères de synchronisation près du début, du milieu et de la fin.
Cette référence évite de réduire le contrôle à « c’est plus fluide » alors qu’un changement de durée, une piste manquante ou une incompatibilité de livraison passe inaperçu.
Vérifiez d’abord que la vidéo locale peut entrer dans ce flux
Contrôlez format, durée et taille avant l’appel API. Une entrée enhance_frame_rate ne peut pas dépasser 300 secondes, et un upload éphémère doit faire entre 512 octets et 200 MB. Privilégiez un conteneur et un codec pris en charge, par exemple MP4 avec H.264, H.265 ou AV1, et découpez les programmes plus longs sur des coupes naturelles.
Si la vidéo est déjà dans un stockage objet, son URL HTTPS peut être fournie directement dans videoUri. Elle doit utiliser un domaine et non une IP, accepter HEAD, retourner des en-têtes Content-Type et Content-Length corrects et ne pas dépendre de redirections ; la limite vidéo par URL est de 32 MB. Pour un master local classique, l’upload éphémère évite ces contraintes d’hébergement.
Vérifiez aussi que le compte possède des credits achetés. La note indique 1 credit pour 2 secondes, sans préciser l’arrondi des fractions ; conservez donc estimatedCost à l’envoi et le cost final de la tâche.
Utilisez ce script pour téléverser, envoyer, attendre et télécharger
Cet exemple REST garde toutes les étapes importantes visibles. Il demande la clé sans l’afficher, contrôle durée et taille, crée un upload éphémère, transfère le fichier, soumet la tâche, interroge toutes les cinq secondes et télécharge la sortie réussie.
Installez la dépendance Python et vérifiez que ffprobe est disponible :
python3 -m pip install requests
Enregistrez le code suivant dans 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()
Exemple pour convertir input.mp4 en 60 fps et enregistrer output-60fps.mp4 :
python3 runway_fps.py input.mp4 60 output-60fps.mp4
Saisissez la clé seulement lorsque RUNWAYML_API_SECRET: apparaît. Elle n’est ni affichée ni ajoutée à l’historique du shell, et le script ne l’écrit pas sur disque. Si la variable d’environnement existe déjà de façon sûre, elle est utilisée.
Comprenez les trois étapes API du script
La génération n’est terminée qu’après la réussite des trois étapes. Les noms sont sensibles à la casse : le JSON REST doit utiliser videoUri et targetFramerate.
| Étape | Requête | Contenu requis | Signal de réussite |
|---|---|---|---|
| Initialiser l’upload | POST https://api.dev.runwayml.com/v1/uploads | filename, type: "ephemeral" | Retour de uploadUrl, fields, runwayUri |
| Soumettre l’amélioration | POST https://api.dev.runwayml.com/v1/video_upscale | model: "enhance_frame_rate", videoUri, targetFramerate | Retour de l’id et de estimatedCost |
| Lire la tâche | GET https://api.dev.runwayml.com/v1/tasks/{id} | ID dans le chemin | status: "SUCCEEDED" et output non vide |
Après l’initialisation, envoyez un POST multipart à uploadUrl, conservez toutes les valeurs de fields et joignez la vidéo sous le champ file. runwayUri n’est utilisable qu’après ce transfert réussi et reste valable 24 heures.
Téléchargez et conservez le résultat dès le succès
Continuez à attendre avec PENDING, THROTTLED ou RUNNING ; Runway indique de ne pas attendre plus d’une mise à jour de la même tâche en moins de cinq secondes. Ne lisez output[0] qu’avec SUCCEEDED. FAILED et CANCELLED sont des états terminaux en échec.
Les URLs de sortie expirent généralement sous 24–48 heures. Téléchargez aussitôt le fichier dans votre stockage persistant et ne livrez pas l’URL temporaire. Si elle a expiré, relisez d’abord la même tâche pour obtenir une nouvelle URL au lieu de payer immédiatement une nouvelle génération. Le téléchargement valide seulement l’exécution API ; les contrôles ci-dessous restent obligatoires.
Traitez chaque erreur selon sa catégorie
Si le POST multipart vers uploadUrl échoue, ne réutilisez pas cet upload signé : rappelez /v1/uploads. Pour 400, 401, 404 ou 405, corrigez entrée, clé, ressource ou méthode. Le script ne réessaie que 429, 502, 503 et 504 avec backoff exponentiel et jitter.
Quand la tâche est FAILED, conservez failure, failureCode et cost : ne réessayez pas SAFETY.* ; corrigez le média avant de renvoyer ASSET.INVALID ; examinez l’entrée avant INTERNAL.BAD_OUTPUT.* ; attendez avant de réessayer INPUT_PREPROCESSING.INTERNAL, INTERNAL, un code absent ou THIRD_PARTY.UNAVAILABLE. N’exécutez jamais la même requête en boucle infinie.
Étape 1 : utilisez ffprobe pour confirmer que le fichier atteint la cible
Utilisez ffprobe pour vérifier fréquence moyenne, time base, nombre réel d’images, durée et pistes audio avant de juger l’image. Une seule étiquette FPS dans le système ou le lecteur ne suffit pas à accepter le fichier.
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
Vérifiez que :
avg_frame_ratecorrespond à la cible ou à une représentation rationnelle équivalente.r_frame_rateetavg_frame_ratene sont pas en conflit inexpliqué ; un écart important nécessite une investigation VFR.- Pour un fichier proche du CFR,
nb_read_framesest raisonnablement proche de la durée multipliée par le fps cible. - La durée de sortie correspond à la source, sans fin tronquée ni queue figée ajoutée.
- Les dimensions, le codec et les pistes audio répondent à la spécification de livraison.
- La durée audio ne diffère pas de manière inattendue de la durée vidéo.
Pour 29,97 et 59,94, les outils affichent souvent 30000/1001 et 60000/1001. Cette représentation fractionnaire n’est pas une erreur.
Étape 2 : contrôlez d’abord les plans les plus risqués
Commencez par les mouvements rapides, bords d’occlusion, petits textes et points de coupe, car ils révèlent vite les défauts d’interpolation. Contrôlez les scènes suivantes à 100 %, image par image ou au ralenti :
- panoramiques rapides, travellings et objets rapides ;
- mains, doigts, cheveux, montures de lunettes et lèvres ;
- grilles, stores, maillages, petits textes et fines lignes d’UI ;
- objets de premier plan qui traversent ou révèlent les bords du fond ;
- eau, fumée, particules, feuilles et textures à haute fréquence ;
- flashes, coupes franches, fondus et images autour d’un changement de plan.
Recherchez des défauts reproductibles plutôt qu’une impression vague de netteté : doubles contours, ghosting, bords tordus, objets disparaissant pendant une image, texture pulsante, membres déformés, images mélangées sur une coupe ou tremblement d’un texte statique.
Pour chaque problème, notez le timecode exact, la fréquence cible et les extraits source et résultat. Vous pourrez ainsi distinguer un défaut déjà présent d’un problème nouveau ou d’une différence de décodage.
Étape 3 : vérifiez la synchro au début, au milieu et à la fin
Une synchronisation correcte au début ne prouve pas que tout le fichier l’est ; vérifiez début, milieu et fin pour distinguer un décalage fixe d’une dérive progressive. Suivez cet ordre :
- Trouvez un repère clair au début : claquement, consonne occlusive, impact, atterrissage ou coupe visuelle.
- Répétez le contrôle au milieu et à la fin.
- Un décalage similaire aux trois endroits suggère un retard fixe.
- Une erreur qui augmente vers la fin indique plutôt un problème de durée, de time base ou d’interprétation de la fréquence.
- Pour le lip sync, contrôlez le début, le milieu et la fin d’une phrase continue, pas une seule syllabe.
La note de lancement ne décrit pas le traitement de l’audio. Ne supposez donc pas que la piste est toujours conservée à l’identique ou automatiquement synchronisée : jugez le fichier livré.
Étape 4 : retestez dans la timeline et la plateforme réelles
Testez toujours le fichier dans la timeline de montage et la plateforme finales, car un NLE ou un second transcodage peut réinterpréter la cadence, modifier la vitesse ou perdre une piste. Effectuez au moins ces deux contrôles :
- Placez le fichier sur la timeline prévue et vérifiez que le NLE ne le réinterprète pas, ne modifie pas sa vitesse et ne perd pas de piste.
- Testez-le sur la plateforme ou l’appareil final et vérifiez qu’un second transcodage n’a pas modifié cadence, sous-titres ou synchronisation.
Si la plateforme transcode de nouveau, conservez la sortie Runway et la version de la plateforme. Inspectez-les séparément avant d’attribuer un défaut au fichier amont.
Utilisez ce tableau pour accepter, retraiter ou conserver un segment source
Acceptez le fichier seulement si tous les points critiques respectent la spécification. Si un seul plan échoue, retraitez ce segment ou conservez la source avant de relancer tout le programme.
| Contrôle | Condition de réussite | Action en cas d’échec |
|---|---|---|
| Fréquence cible | Cadence exacte ; 29,97/59,94 ne sont pas étiquetés 30/60 | Corriger la cible ou l’interprétation de la timeline |
| Durée et images | Durée identique ; en CFR, nombre proche de l’estimation | Examiner VFR, troncature, queue figée et time base |
| Dimensions et codec | Conformes à l’éditeur ou au canal | Réencapsuler ou transcoder selon la spécification |
| Mouvement rapide | Pas de double image, déformation ou disparition inacceptable | Marquer les timecodes ; essayer une autre cible ou conserver le plan source |
| Coupes et flashes | Pas d’images mélangées, répétées ou de clignotement anormal | Découper sur une coupe naturelle, retraiter et contrôler le raccord |
| Texte et UI | Glyphes, lignes fines et overlays statiques restent stables | Réappliquer les éléments graphiques en postproduction |
| Synchronisation | Aucun décalage ni dérive visible au début, milieu et fin | Comparer durées/time base, puis réaligner ou transcoder |
| Intégrité | Décodage complet, fin intacte et toutes les pistes présentes | Retélécharger, réencapsuler ou relancer la tâche |
Ne passez pas de 60 à 120 fps dans ces cas
Restez à la fréquence la plus basse qui satisfait la livraison lorsque 120 fps n’ajoute que du volume et du traitement aval. N’augmentez pas la fréquence dans les cas suivants :
- la destination ne demande que 24, 25, 29,97 ou 30 fps ;
- la source contient déjà beaucoup de ghosting, de blocs de compression ou de motion blur ;
- les sous-titres, l’UI ou les lignes fines deviennent moins stables ;
- un problème de synchronisation reste inexpliqué ;
- la plateforme finale imposera un transcodage à fréquence inférieure ;
- aucun bénéfice visible n’apparaît, mais stockage, décodage ou transcodage aval augmentent.
La fréquence d’images est un paramètre de livraison, pas une note de qualité indépendante. Le critère est « respecte la cadence requise sans nouveau défaut inacceptable », pas « affiche le plus grand nombre ».
Terminez la livraison en dix étapes
L’ordre avec le moins de reprise est spécification et référence, upload et envoi, attente et stockage, puis validation technique, visuelle et réelle.
- Choisissez le
targetFramerateexact selon la destination. - Enregistrez cadence, durée, audio et timecodes à risque avec
ffprobe, et confirmez un maximum de 300 secondes. - Pour un fichier local, appelez
POST /v1/uploadset conservezuploadUrl,fields,runwayUri. - Envoyez le formulaire multipart à
uploadUrl; demandez un nouvel upload en cas d’échec. - Appelez
POST /v1/video_upscaleavecmodel,videoUri,targetFramerate. - Conservez l’
idde tâche etestimatedCost. - Appelez
GET /v1/tasks/{id}toutes les cinq secondes jusqu’à un état terminal. - Avec
SUCCEEDED, téléchargez et stockezoutput; traitezFAILEDouCANCELLEDpar type d’erreur. - Vérifiez cadence réelle, durée, artefacts, coupes et audio avec
ffprobeet un examen image par image. - Testez dans la timeline et sur la plateforme réelles avant livraison.
Références officielles : Models, Inputs, Uploads, Video upscale API Reference, Task API Reference, Outputs, HTTP errors, Task failures et API Changelog. Champs et étapes vérifiés le 26 septembre 2026.