Skip to content
ffmpegcommand linelossless

FFmpeg: trim a video without re-encoding

The exact -ss, -t and -c copy commands for a lossless trim, why the flag order matters, and how to cut a section out of the middle with the concat demuxer.

By Kyllian · Published

If you want to shorten a video and keep the compressed bytes untouched, FFmpeg will do it in one command. The catch is that the command is order-sensitive in a way nothing warns you about, and three of the most commonly copy-pasted variants are subtly wrong.

This is the command, then everything you need to know about why it is written that way.

The command

ffmpeg -ss 00:01:30 -i input.mp4 -t 00:00:45 -c copy -avoid_negative_ts make_zero output.mp4

That takes 45 seconds starting from 1:30, copies both streams without decoding them, and writes a file whose timestamps begin at zero.

Piece by piece:

Why -t and not -to

-t takes a duration. -to takes an end position — but which timeline that position is measured against depends on whether your seek was an input option and whether -copyts is in play. That ambiguity is the single most common reason a copy-pasted trim command produces a clip of the wrong length.

Duration arithmetic is trivial to do in your head, and -t means the same thing in every FFmpeg version. Use -t.

Why the position of -ss matters

-ss before -i and -ss after -i are different operations that happen to share a name.

Before -i (input seeking). FFmpeg uses the container index to jump. With -c copy, it can only begin at a keyframe, so it lands on the nearest keyframe at or before the time you asked for. Your clip may therefore start slightly early. It will play correctly.

After -i (output seeking). FFmpeg reads the file from the beginning and discards everything before your timestamp. When you are re-encoding, this is exact. When you are using -c copy, it is a trap: it can start you on a P-frame that references data that was just discarded, and you get a few seconds of smeared, half-drawn picture at the head of the file — or, depending on the player, nothing at all until the next keyframe.

There is a second, historical wrinkle worth knowing if you are reading old Stack Overflow answers. Very old FFmpeg builds seeked inaccurately when re-encoding with -ss before -i, which is why so much advice insists on putting -ss after -i. That has not been true for many years — input seeking is accurate when re-encoding on any remotely modern build. The advice outlived the bug.

Finding the keyframes so the cut is not a surprise

Since a lossless cut can only land on a keyframe, it is worth knowing where they are before you complain about where the cut went:

ffprobe -v error -select_streams v:0 -skip_frame nokey \
  -show_entries frame=pts_time -of csv=print_section=0 input.mp4

That prints one timestamp per keyframe. If they come every couple of seconds, a lossless trim will feel accurate enough. If your screen recording only has a keyframe every twelve seconds, you now know why -ss 00:01:30 gave you a clip that starts at 1:24 — and that this is the codec’s doing, not FFmpeg’s.

Cutting a section out of the middle, losslessly

There is no single-command lossless way to remove a middle section, because the two surviving pieces have to be produced separately and then joined. The three-step version:

1. Cut the two keepers.

ffmpeg -i input.mp4 -t 00:02:14 -c copy -avoid_negative_ts make_zero part1.mp4
ffmpeg -ss 00:02:19 -i input.mp4 -c copy -avoid_negative_ts make_zero part2.mp4

2. Write a list file.

file 'part1.mp4'
file 'part2.mp4'

3. Join with the concat demuxer.

ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4

The concat demuxer (as opposed to the concat filter) works at the packet level, so -c copy stays lossless through the join. The requirement is that every input shares the same codecs, resolution, pixel format and timebase — which is automatic here, because all the parts came out of one source file.

Two things will bite you:

If either of those matters — and for removing a fumbled sentence from the middle of a recording, they usually do — the honest answer is that you want a frame-accurate cut, which means re-encoding. The trade between the two is written up here, and the browser editor on this site does the frame-accurate version with a trim + concat filter graph rather than a packet copy.

A few flags that save trouble

-movflags +faststart (MP4/MOV only) rewrites the file so the index sits at the front. Do this on anything that will be played over HTTP; without it a browser has to fetch the tail of the file before it can start.

ffmpeg -ss 00:00:30 -i input.mp4 -t 60 -c copy -movflags +faststart output.mp4

-c:v copy -c:a aac copies the video losslessly while re-encoding only the audio. Useful when the source audio is in a codec the destination container will not accept — trimming an MKV with FLAC audio into an MP4, for instance. The picture is still untouched.

-map 0 copies every stream, including subtitles, extra audio tracks and attachments, which plain -c copy will silently drop for anything beyond the first stream of each type.

-fflags +genpts helps when a source has broken or missing presentation timestamps and the output ends up with a wildly wrong duration.

Choosing between the two approaches

Use -c copy when you are trimming the head or tail of a clip, a second of imprecision is acceptable, and you want the operation to be instant and mathematically lossless.

Re-encode when the cut has to land on a specific frame, when you are removing something from the middle, or when you are also changing speed or resolution — because all of those require the decoded frames anyway.

If you would rather not install anything, the same frame-accurate cut runs in a browser tab here using FFmpeg compiled to WebAssembly. It is the same library, the same filters and the same trade-offs; the file simply never leaves your machine. How that works is documented in full.

The tools these guides are about

Try it on your own file — nothing is uploaded and there is no account.

Open the free online video editor