Frame-exact trim
Give it a clip, a start frame and a frame count; get back a re-encoded clip with exactly
that many frames at the clip's own frame rate. Use it on every render: models run past
the shot (a 3–4 s floor for a 1 s shot), idle before acting, or come back padded to
another frame rate. To put picked clips into a source timeline, use
ffmpeg-splice-into-source-cut, which trims each clip itself.
Inputs
- The clip, untrimmed. Keep it in
raw/next to the trimmed file and never overwrite it. - Its frame rate and frame count (
ffprobe-inspect-media). Cut at that rate; never resample to fit. - The start frame (0 unless the take idles) and the frame count you want:
- review takes and per-shot delivery files: the shot's length plus a 0.5 s tail (12 frames at 24 fps), so a word or action that runs past the cut is still there;
- a cut: exactly the shot's length (the splice leaf does this for you).
- Upscale first. Run
topaz-upscaleon the raw render, then trim the upscaled file.
Run it
By frame (exact by construction; use it whenever you think in frames). Shot 01 is 25 frames at 24 fps; this take idles, so start 22 frames in and keep 25 + 12 frames:
exec ffmpeg -hide_banner -loglevel error -y
-i <job>/video/raw/shot01_kling_d.mp4
-vf "trim=start_frame=22:end_frame=59,setpts=PTS-STARTPTS"
-af "atrim=start=0.916667:end=2.458333,asetpts=PTS-STARTPTS"
-r 24
-c:v libx264 -crf 14 -preset slow -pix_fmt yuv420p
-c:a aac -b:a 192k -movflags +faststart
<job>/video/shot01_kling_d.mp4
end_frameis exclusive: start + count.atrimtakes the same span in seconds (frame / fps, six decimals). Drop-afand add-anfor a clip with no sound.-r 24on the output is required, not decoration (Tips and traps).- The encode settings are the house ones for intermediate and delivery files: CRF 14,
preset slow, yuv420p, AAC 192k,
+faststart.
By time (the same cut, written as seconds; decode-accurate because it re-encodes):
exec ffmpeg -hide_banner -loglevel error -y -i <raw clip>
-ss 0.9167 -t 1.5417 -r 24
-c:v libx264 -crf 14 -preset slow -pix_fmt yuv420p -c:a aac -b:a 192k
-movflags +faststart <out clip>
-ss after -i seeks by decoding, so the first frame is exact. Write times to four
decimals; frames / fps.
Picking a late start
When a take holds its opening pose before acting, skip the hold rather than speeding the clip up. Make a strip of every 4th frame and read the frame where the action starts (tile index × 4):
exec ffmpeg -v error -y -i <raw clip>
-vf "select='not(mod(n,4))',scale=320:-2,tile=8x4:padding=4"
-frames:v 1 -update 1 <job>/video/raw/shot01_kling_d_strip.jpg
Look at the strip, then trim from that frame.
Renders padded from 24 to 30 fps
Some providers return a 24 fps model padded to 30 fps by repeating every 5th frame (a visible stutter). Drop exactly the duplicates and get the native 24 back, then trim:
-vf "decimate=cycle=5,setpts=N/24/TB" -r 24
Never use fps=24 on a 30 fps file: it drops real frames evenly and judders.
What you get
A clip of exactly the frames asked for, at the source rate, H.264 with its own audio
trimmed to match. Record the trim in the take's notes (start frame, frame count), so a
later round can re-trim from raw/.
Tips and traps
- ffmpeg 7 drops the last frame without
-r. A trimmed segment encoded to MP4 without an explicit output rate comes out one frame short (25 in, 24 out). Always pass-r <fps>on the output. - No
fps=inside per-segment filter chains before a concat: it lost one frame per shot (472 of 484 frames). - Don't retime to fit. Sped-up action reads badly. Brief the model to play the
action in real time and then hold, and trim. Every model spreads the action over its
whole clip, so a trim may stop short of a pinned end pose: that is expected, and the
full render stays in
raw/. - Trimming the start of a take with a pinned first frame loses the pinned frame. Do it only when the opening is an idle hold.
Check it
Count the frames after every cut. framemd5 decodes every frame and prints one line per
frame; count the lines that don't start with #:
exec ffmpeg -v error -i <out clip> -map 0:v -f framemd5 -
It must equal the count you asked for. Then look at the first and last frame (a
select='eq(n,0)' still) against the shot's stills before the take goes on a review page.


