· 5 min read
ffmpeg trim video
How to cut a clip out of a video with FFmpeg — frame-accurate vs fast and lossless, where to put -ss, and why -c copy sometimes lands seconds off.
ffmpeg -i https://storage.rendi.dev/sample/big_buck_bunny_720p_16sec.mp4 -ss 00:00:03 -to 00:00:08 output.mp4
That takes a 5-second segment starting at 0:03 and ending at 0:08. It re-encodes with libx264/AAC defaults, the result is frame-accurate, and it works on any input FFmpeg can read. It’s the canonical trim recipe from our FFmpeg cheatsheet.
If you need it faster — orders of magnitude faster, since stream copy skips the encoder — or if you don’t want to re-encode at all, there’s a trade-off.
What -ss, -to, and -t actually mean
-ss is the seek time. -to is the end time. -t is the duration, an alternative to -to.
-ss placement matters more than any other detail in this workflow:
-ssbefore-i— input seeking. FFmpeg jumps to the nearest keyframe before your timestamp. Very fast on long files.-ssafter-i— output seeking. FFmpeg decodes the whole input from frame 0 and discards everything before your timestamp. Slow on long files, exact on short ones.
Since FFmpeg 2.1, input seeking is also frame-accurate when transcoding. The FFmpeg Seeking wiki calls this out:
As of FFmpeg 2.1, when transcoding with ffmpeg (i.e. not stream copying): -ss is also ‘frame-accurate’ even as input option. Previous behavior (seek only to nearest preceding keyframe, despite inaccuracy) can be restored with
-noaccurate_seek.
Unless you’re using -c copy, where -ss goes barely matters. The combined input-and-output -ss pattern older guides recommend is, in the wiki’s words, “mostly meaningless” today.
The keyframe-only behavior only survives under -c copy.
The fast, lossless cut
ffmpeg -ss 00:00:03 -to 00:00:08 -i https://storage.rendi.dev/sample/big_buck_bunny_720p_16sec.mp4 -c copy output.mp4
-c copy skips the encoder entirely. Packets are remuxed straight into the new container with no quality loss, and the runtime is dominated by I/O rather than CPU.
The catch: copy can only cut on keyframes. If you ask for 00:00:03 and the previous keyframe is at 00:00:00, FFmpeg has to include everything from 00:00:00 forward.
Run that command against the sample file above — h264, 24 fps, default keyint=250, so a keyframe every ~10 seconds. The 3 → 8 cut produces a 5.17s output (167 ms of slop). ffprobe -show_entries packet=pts_time,flags shows what’s inside:
-3.000000,KD_ ← keyframe, flagged "Discard"
-2.833333,_D_ ← 71 packets at negative timestamps, all flagged Discard
-2.875000,_D_
...
-0.041667,_D_
0.000000,___ ← playable content starts here
0.041667,___
FFmpeg keeps the keyframe at -3.0s because every frame between -3.0s and 0.0s depends on it for decode, and it marks the in-between frames as discard. A well-behaved player honors the flag and starts at PTS 0. A naive player decodes the lot, and you see what users keep reporting on the VideoHelp forums: black frames, garbled output, or the clip “starting at the wrong second.” VideoHelp regular pcspeak puts the bound at: “ffmpeg cuts at the nearest keyframe. Each cut could differ from your requirements by as much as 4 seconds.”
This isn’t a bug — it’s the price of -c copy. Three ways out if you need accuracy: re-encode (the canonical first command), round your timestamps to known keyframe positions, or use the trim filter (below).
The hybrid: fast and accurate
For long files where you need both speed and accuracy, place a coarse -ss before -i to jump close to the target via keyframe, then a fine -ss after -i to fine-tune frame-accurately, and let FFmpeg re-encode the result:
ffmpeg -ss 00:05:00 -i https://storage.rendi.dev/sample/big_buck_bunny_720p.mp4 -ss 00:00:02 -to 00:00:07 -c:v libx264 -c:a aac output.mp4
Input-seek lands you near 0:05:00 via the prior keyframe, output-seek trims another 2 seconds off the front, the output is a 5-second clip starting exactly where you asked. This is the pattern Carl Eugen Hoyos — 2,166 commits to FFmpeg — recommended on the user mailing list when you’re cutting a few seconds out of a multi-hour file and don’t want to wait for output-seek to decode from frame 0.
-to versus -t
-to is an end position, -t is a duration. With -ss after -i, they’re not interchangeable — -t measures from the seek point, -to measures absolute source time. Full mechanics in ffmpeg -t vs -to.
Multiple cuts in one pass: the trim filter
When you need to splice several segments out of one input — keep 0:03–0:08, drop 0:08–0:21, keep 0:21–0:25 — -ss and -to can’t help. Each invocation cuts one window. You use the trim filter inside -filter_complex:
ffmpeg -i https://storage.rendi.dev/sample/big_buck_bunny_720p_16sec.mp4 -filter_complex \
"[0:v]trim=start=3:end=8,setpts=PTS-STARTPTS[v1]; \
[0:a]atrim=start=3:end=8,asetpts=PTS-STARTPTS[a1]; \
[0:v]trim=start=11:end=15,setpts=PTS-STARTPTS[v2]; \
[0:a]atrim=start=11:end=15,asetpts=PTS-STARTPTS[a2]; \
[v1][a1][v2][a2]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" output.mp4
Three caveats:
trimis video,atrimis audio. Reusingtrimon an audio stream throws a media-type-mismatch error — a common stumble on first use.- You always need
setpts=PTS-STARTPTS(andasetptsfor audio). The trim filter gates frames through without rewriting timestamps. Without the reset, downstreamconcatsees gaps and the output freezes. setpts/asetptsmust come last in the chain. Anything after them re-introduces the gap.
The filter is by Anton Khirnov — 5,751 commits to FFmpeg — added in 2013 for frame-accurate selection inside a filtergraph.
Quick decision matrix
| You want… | Use |
|---|---|
| Frame-accurate, any input, simplest command | -ss and -to after -i, re-encode (default) |
| Fastest cut, no quality loss, OK with keyframe alignment | -ss and -to before -i, plus -c copy |
| Fast on a long file and accurate | Hybrid: coarse -ss before -i, fine -ss after -i, re-encode |
| Multiple segments stitched in one pass | trim + atrim + setpts/asetpts + concat |
The rest of FFmpeg’s trim and seek surface is commentary on those four cases — see the FFmpeg Seeking wiki for the full reference.
For more recipes — crop, concat, overlays, audio mixing — see the FFmpeg cheatsheet.