رفتن به محتوا

---

title: کات زدن یه ریل فریم به فریم با FFmpeg

slug: ghost-reel-ffmpeg

lang: fa

published: 2026-10-31

words: 487

lines: 84

size: 4.0 KB

read: 3 min read

reel: فتوشوت ارواح

---

کات زدن یه ریل فریم به فریم با FFmpeg

فتوشوت ارواح

واسه هالووین توی شرکت یه فتوشوت ارواح گرفتیم. ریل ۱۵ ثانیه‌ست و پشتش هیچ پروژه‌ی ادیتوری نیست، فقط یه اسکریپت bash دور FFmpeg. هدف این بود که موزیک یه ریل مرجع دست‌نخورده بمونه و هر کات دقیقاً روی همون فریم مرجع بیفته.

خروجی مورد نظر

output
1080x1920, 30 fps, 448 frames, 14.93 s
H.264 High, 14 Mbps, 2 pass, BT.709
AAC 44.1 kHz stereo, copied from the reference
-14.1 LUFS integrated

۱. کات‌ها رو از مرجع دربیار

bash
ffmpeg -hide_banner -i ref.mp4 -vf "select='gt(scene,0.3)',showinfo" \
  -an -f null - 2>&1 | grep -o 'pts_time:[0-9.]*'
output
1.2  2.8  5.5667  6.6667  8.1  9.1  10.0667  11.1333  11.9  13.0333  14.0667

این ۱۱ تا کات می‌شن ۱۲ تا شات. ضرب در ۳۰ می‌شه تعداد فریم و جمعشون باید ۴۴۸ بشه:

output
36 48 83 33 43 30 29 32 23 34 31 26

۲. هر شات رو دقیقاً به تعداد فریمش ببر

اول سه تا ویدیو، بعد هفت تا عکس، یه فریم از ویدیو و یه عکس آخر، هرکدوم به اندازه‌ی فریم خودش. ویدیوهای iPhone، HDR هستن و اول باید tone map بشن:

bash
HDR="zscale=t=linear:npl=203,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv,format=yuv444p10le,"
FRAME="scale=1080:1920:force_original_aspect_ratio=increase:flags=lanczos,crop=1080:1920,setsar=1,fps=30,"

ffmpeg -ss 5.0 -i footage/IMG_0224.MOV -map 0:v:0 -an -frames:v 48 \
  -vf "setpts=PTS-STARTPTS,$HDR$FRAME$GRADE" \
  -c:v prores_ks -profile:v 4 seg02.mov

مستر ProRes 4444 هست تا قبل از خروجی نهایی هیچی دوبار فشرده نشه.

۳. گرید رنگ

یه زنجیره‌ی ثابت روی همه‌ی شات‌ها می‌ره تا عکس و ویدیو یکی بشن: کنتراست بیشتر، سایه‌ی سردتر، هایلایت گرم‌تر، vignette و یه کم grain.

bash
GRADE="eq=contrast=1.18:brightness=-0.07:saturation=1.12:gamma=0.92,\
colorbalance=rs=-0.06:gs=-0.01:bs=0.10:rm=0.05:gm=0:bm=-0.04:rh=0.12:gh=0.04:bh=-0.10,\
curves=all='0/0.02 0.25/0.16 0.6/0.6 1/0.95',vignette=PI/4.5,noise=alls=6:allf=t"

۴. موزیک رو کپی کن، encode نکن

bash
ffmpeg -i master.mov -i ref.mp4 -map 0:v:0 -c:v libx264 -b:v 14M -pass 2 \
  -map 1:a:0 -c:a copy -movflags +faststart final.mp4

کل ترفند همین -c:a copy هست. منبع فقط 61 kbps AAC بود و encode دوباره بدترش می‌کرد. یه hash ثابت می‌کنه صدا یکیه:

bash
ffmpeg -i final.mp4 -map 0:a -c copy -f md5 -
ffmpeg -i ref.mp4 -map 0:a -c copy -f md5 -
output
MD5=83a5ab8a4bdbe164b11af5a516e95e44
MD5=83a5ab8a4bdbe164b11af5a516e95e44

چیزایی که خراب شد

یه فریم دیرتر. ویدیوی سوم به جای 0 از PTS 0.0333 شروع می‌شد. همین کات‌های ۴ تا ۱۱ رو یه فریم عقب انداخت و فریم آخر عکس آخر رو هم برد. setpts=PTS-STARTPTS اول زنجیره درستش کرد.

رنج رنگ اشتباه. overlay تیتر که RGBA بود کل تصویر رو برد روی full range و فایل yuvj420p تگ می‌خورد. خروجی نهایی به زور limited range BT.709 می‌شه.

کاتی که انگار نبود. scene detection با 0.3 فقط ۱۰ تا از ۱۱ کات رو پیدا کرد. کات آخر بین دو تا شات شبیه به همه و امتیازش کمتر شد. با 0.05 پیدا می‌شه و اختلاف فریم‌ها هم نشون می‌ده کات دقیقاً سر جاشه.

چرا با اسکریپت

خود اسکریپت همون ادیته. اگه یه شات عوض بشه، یه خط رو عوض می‌کنم و دوباره رندر می‌گیرم و هر بار همون چک‌ها اجرا می‌شن: زمان کات‌ها، تعداد فریم، hash صدا و loudness.

fa3 min read