-ss before -i is not enough because it only shifts the input time; the output container still references the original file’s timeline. You need to combine an early seek for speed with the -copyts and -start_at_zero flags, or use the setpts filter if re-encoding. This ensures the new video starts at zero rather than inheriting negative offsets from the cut point.
Understanding Negative Presentation Timestamps
When you trim a video by specifying a start time, FFmpeg reads frames starting from that offset. However, the internal Presentation Timestamps (PTS) are absolute values relative to the original source file. If your source video has a PTS of 0 at its beginning, and you start reading at 10 seconds, the first frame you process might have a PTS of 10. When FFmpeg writes this to a new container, it expects timestamps to start at 0. If the container format or codec requires strictly non-negative timestamps starting from zero, FFmpeg may generate errors or invalid files if the PTS values are not adjusted.
This issue often manifests as playback glitches, audio desynchronization, or errors like "negative pts" during muxing. It is particularly common when working with formats that do not support negative timestamps natively or when the output stream needs to be compatible with strict players. The core problem is that the output stream inherits the absolute timing of the input stream without normalization.
Method 1: Using -ss Before -i with Copyts and Startatzero
The most efficient method avoids re-encoding by performing a fast seek. Placing -ss before -i tells FFmpeg to seek to the specified time immediately upon opening the file, which is much faster than seeking after decoding begins. To fix the timestamp issue, you must explicitly tell FFmpeg to copy the original timestamps and then reset them to start at zero.
- Use Early Seek: Place
-ssimmediately before the input file argument. This triggers a keyframe-based seek, which is significantly faster than post-decoding seeks. - Copy Timestamps: Add the
-copytsflag. This preserves the original PTS values from the input stream in the output stream initially. - Reset to Zero: Add the
-start_at_zeroflag. This instructs FFmpeg to subtract the initial PTS value from all subsequent timestamps, effectively shifting the timeline so the first frame is at 0. - Stream Copy: Use
-c copyto avoid re-encoding, ensuring lossless quality and fast processing.
ffmpeg -ss 10 -i input.mp4 -c copy -copyts -start_at_zero output.mp4
In this command, -ss 10 seeks to the 10-second mark. -copyts ensures the PTS values are carried over exactly as they appear in the input. -start_at_zero then adjusts these values so that the first frame of the output has a PTS of 0. This combination is robust and works with most modern FFmpeg versions. For more details on extracting specific segments, see Extract Video Segment Timestamps with FFmpeg: Command and Options.
Method 2: Re-encoding with Setpts Filter
If Method 1 fails due to container limitations or if you are already re-encoding, you can use the setpts video filter. This method explicitly manipulates the timestamp expression for each frame. While slower than stream copying, it offers precise control over the timeline and guarantees that the output timestamps are mathematically normalized.
The setpts filter accepts an expression that calculates the new PTS based on the current PTS. By dividing the current PTS by the stream's timebase and multiplying it back, or simply resetting the base, you can force the timestamps to start at zero. The standard expression for resetting timestamps is PTS-STARTPTS. This subtracts the PTS of the first frame from every subsequent frame, resulting in a new timeline that starts at 0.
ffmpeg -i input.mp4 -ss 10 -to 20 -vf "setpts=PTS-STARTPTS" -c:v libx264 -c:a aac output.mp4
Here, -ss 10 and -to 20 define the segment boundaries. The -vf "setpts=PTS-STARTPTS" applies the filter to the video stream. Note that if you also have an audio stream, you should apply a similar filter to it, such as -af "asetpts=PTS-STARTPTS", to prevent audio-video sync issues. This approach is essential when dealing with complex filters or when the output format does not support the -start_at_zero flag.
Common Pitfalls and Best Practices
Avoid placing -ss after -i if you want fast trimming. A post-decoding seek forces FFmpeg to decode all frames from the beginning of the file up to the cut point, which is extremely slow for long videos. Always prefer -ss before -i for keyframe-accurate cuts.
Another common mistake is assuming that -c copy will always preserve the correct timing. Without -copyts and -start_at_zero, the output file may retain the original absolute timestamps, leading to negative values if the start time was trimmed. Always verify the output with a media player or by checking the stream metadata using ffprobe.
Additionally, be aware that some older containers or codecs may not handle negative timestamps gracefully even if FFmpeg generates them. In such cases, the setpts filter is the safest bet because it modifies the data directly before muxing. For related techniques on manipulating video properties, you might find Extracting Specific Frame Index using FFmpeg's Timestamp (PTS) useful for understanding how PTS values are structured.
Frequently Asked Questions
Why does my video play too fast after trimming?
This usually happens if the duration metadata is not updated correctly while the frame rate remains unchanged. Ensure you are using -copyts and -start_at_zero together, or applying setpts=PTS-STARTPTS. These methods adjust both the timestamps and the perceived duration of the stream.
Can I use -ss after -i to avoid negative timestamps?
No, placing -ss after -i performs a precise seek but does not inherently reset timestamps. It still results in absolute PTS values that may be negative relative to the new start time if not handled by additional flags like -start_at_zero. It is also significantly slower for long videos.
How do I fix negative timestamps in the audio stream?
You must apply the asetpts filter to the audio stream just as you do for video. Use -af "asetpts=PTS-STARTPTS" alongside your video filter. Failure to synchronize both streams will result in audio drift or silence.
Is -start_at_zero supported in all FFmpeg versions?
The -start_at_zero flag was introduced in FFmpeg 4.2. If you are using an older version, you must use the setpts filter method instead. Check your version with ffmpeg -version to determine compatibility.
What if I get a "non-monotonous DTS" error?
This error indicates that the decoded timestamps are not increasing monotonically, often due to imperfect seeking or B-frames. Using -ss before -i helps minimize this, but you may also need to add -framedrop or use the setpts filter to enforce monotonicity.