Pipeline ffmpeg
Les scripts d'aide se trouvent dans scripts/ à côté de ce fichier. Exécutez-les avec python <skill_dir>/scripts/<nom>.py.
probe.py IN...— résumé en une ligne par fichier (taille, fps, rotation, HDR, durée, audio).export.py IN OUT --preset instagram|tiktok|shorts|youtube|master|preview— encodage final avec les bons paramètres.frames.py IN OUTDIR --times 0.5,3,7.2ou--every 1— capture d'images de contrôle.
1. Sonder et normaliser d'abord
Pièges des vidéos de téléphone :
- Rotation : l'iPhone stocke les pixels en paysage + un indicateur de rotation. ffmpeg auto-rotate au décodage (
-noautorotatedésactive). OpenCVVideoCapturepeut ne pas le faire — vérifiezframe.shapeet effectuez la rotation vous-même. - VFR : les téléphones enregistrent en fréquence d'images variable. Convertissez en CFR lors de l'extraction :
-vf fps=30(ou-r 30en sortie) pour que les calculs d'images soient exacts. - HDR :
color_transfer=arib-std-b67(HLG) ousmpte2084(PQ). Non traité, il semble délavé/gris en SDR. Mappage de tons :
# HDR (HLG ou PQ) -> SDR Rec.709, chemin CPU zscale
-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 = filmique ; mobius conserve plus de saturation. Si zscale est manquant, utilisez libplacebo (-vf libplacebo=tonemapping=bt.2390:colorspace=bt709:color_primaries=bt709:color_trc=bt709).
- Apple Log / log caméra : appliquez d'abord la LUT Log → Rec.709 du fabricant (
lut3d=AppleLog_to_Rec709.cube), puis étalonnez. - Sources 10 bits : conservez le 10 bits dans les intermédiaires (
-pix_fmt yuv422p10leProRes ouyuv420p10lex265) lorsque vous étalonnez fortement ; convertissez en 8 bitsyuv420puniquement lors de l'export final pour les réseaux sociaux.
2. Architecture d'un script de construction
sources/ (jamais modifié)
edit/
build.py # regénère tout à partir des sources + fichiers de données
edl.json # [{src, in, out, speed, grade, transition}]
segments/ # un fichier normalisé et étalonné par entrée EDL (quasi sans perte)
overlays/ # PNG / séquence PNG / ProRes 4444 / WebM alpha
preview.mp4, final.mp4
Règles :
- Extraction par segment avec tout le travail par clip (coupe, vitesse, échelle/recadrage, étalonnage, fps) en un seul passage → intermédiaire à
-crf 10(x264) ou ProRes 422 HQ. - Concaténer les segments avec le démultiplexeur concat et
-c copylorsque aucune transition n'est nécessaire (tous les segments doivent partager codec, résolution, fps, pix_fmt, timebase). - Superpositions et texte en un seul passage final sur la vidéo concaténée, puis encoder une fois selon les spécifications de livraison.
- L'audio est construit séparément (musique + effets sonores + dialogue → un WAV), puis multiplexé dans le passage final.
Coupe précise
# -ss avant -i est rapide et précis à l'image lors du ré-encodage (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
N'utilisez jamais -c copy pour les coupes qui doivent être précises à l'image (cela s'aligne sur les images clés).
Remplir 9:16 à partir de 16:9
- Recadrage (cover) :
scale=-2:1920,crop=1080:1920:(iw-1080)*X:0avec X 0..1 pour choisir la position du sujet (suivez-le s'il bouge). - Arrière-plan flou (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
Concaténation
# list.txt: file 'segments/seg_001.mp4' (une par ligne)
ffmpeg -f concat -safe 0 -i list.txt -c copy joined.mp4
Transitions avec 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 = durée du premier clip − durée de la transition. Types : fade, fadeblack, fadewhite, wipeleft, slideup, smoothleft, circleopen, radial, zoomin, hblur, pixelize, dissolve… Enchaînez les offsets cumulativement pour plusieurs clips (chaque xfade raccourcit le total de sa durée).
Superpositions
# PNG statique (plein écran, transparent) pour toute la vidéo — pas besoin de -loop
[0:v][1:v]overlay=0:0:format=auto
# limitée dans le temps
overlay=0:0:enable='between(t,2.0,5.5)'
# superposition animée démarrant à T dans la sortie
[1:v]setpts=PTS-STARTPTS+T/TB[ov];[0:v][ov]overlay=0:0:eof_action=pass
Superpositions alpha : séquence 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'utilisez pas -loop 1 pour un PNG RGBA 4K sur des vidéos longues ; cela consomme de la RAM. Une entrée d'image unique est répétée automatiquement par la superposition.
Piping des images brutes depuis Python (composition PIL/OpenCV) vers 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, par image
Lisez les images sources de la même manière (-f rawvideo -pix_fmt rgb24 -), ce qui évite les problèmes de rotation et de codec OpenCV.
Vitesse
- Constante :
setpts=PTS/2(2x) +atempo=2.0(plage atempo 0.5-100 ; enchaînez pour plus). - Ralenti fluide à partir de 30 fps :
minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:vsbmc=1, puissetpts=2*PTS. Vérifiez les distorsions sur les bords/mains. - Variations de vitesse : rendu image par image à partir d'une carte temporelle (voir
beat-sync), n'empilez jamais plusieurs expressionssetpts.
Sous-titres (en dernier)
-vf "subtitles=captions.ass:fontsdir=fonts" # ASS donne un contrôle total du style
3. Préréglages d'export
| Cible | Vidéo | Audio | Notes |
|---|---|---|---|
| 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 ; les plateformes ré-encodent, donc fournissez un fichier de haute qualité |
| YouTube 16:9 | 3840x2160 ou 1920x1080, H.264 CRF 16 ou HEVC CRF 18, fps source | AAC 48 kHz 320k | uploadez en 4K même pour un contenu 1080p pour obtenir le meilleur encodage VP9/AV1 |
| Master | ProRes 422 HQ ou x264 CRF 10 / x265 CRF 14 -tag:v hvc1 | PCM 24 bits 48 kHz | archive |
| Preview | 720p, CRF 28, -preset veryfast | AAC 128k | itération rapide |
Ajoutez toujours -pix_fmt yuv420p pour les réseaux sociaux (le 10 bits ou le 4:2:2 peut échouer ou sembler incorrect après téléchargement) et -color_primaries bt709 -color_trc bt709 -colorspace bt709 pour marquer correctement le SDR.
4. Vérification
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 # où sont les coupes