We may set the desired PTS timestamp of each video frame, using setpts filter with elaborated nested if expression.
The following answer, is just a "proof of concept", assuming you have the knowledge creating the more complex expression you need programmatically.
The solution assumes that the input video has a fixed framerate, and that it's simple to compute the input timestamp from the input frame number.
Note:
Accelerating parts of the video by setting the timestamps creates a variable framerate (VFR) video, which may not play nicely in all video players.
It may be better to create fixed framerate video by speeding up all the video by a factor of x10, and duplicate each frame 10 times in the parts of the original rate.
Note that using a Python script that reads frame with OpenCV, and writes using OpenCV (or better pipe to FFmpeg), it is relatively simple to duplicate the frames.
Assume we have the following input file (40 frames at 1fps):
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=40 -c:v libx264 -g 1 input.mp4
The timestamps of the input video are:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
Assume we want to speed in range [0, 19] and [30, 39], and get the following output timestamps:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 21.1, 21.2, 21.3, 21.4, 21.5, 21.6, 21.7, 21.8, 21.9,
We may use the following FFmpeg command:
ffmpeg -y -r 100 -i input.mp4 -fps_mode:v passthrough -vf "setpts='(if(lt(N,10), N, if(lt(N,20), ((N-10)/10)+10, if(lt(N,30), 11+(N-20), 21+(N-30)/10))))/TB'" -c:v libx264 -g 1 output.mp4
-r 100 is used for setting packet duration to 10msec (short packet duration allows higher resolution of timestamps).
-fps_mode:v passthrough - passes each input frame from input to output (without duplicating the frames due to the 100 fps rate).
-vf "setpts='(if(lt(N,10), N, if(lt(N,20), ((N-10)/10)+10, if(lt(N,30), 11+(N-20), 21+(N-30)/10))))/TB'" - Uses if expressions and some math for adjusting the timestamps.
The if expression applies if(condition, value if true, value if false).
N is the input frame number, start counting from zero.
if(lt(N,10), N, ... sets PTS to N for the first 10 frames...
/TB divides by the time in seconds by the time-base.
-g 1 - Set GOP size to 1 for testing (remove it). It keeps the PTS sorted when using FFprobe.
Verifying the output timestamps using FFprobe:
ffprobe -select_streams v:0 -of default=noprint_wrappers=1 -show_entries packet=pts_time output.mp4
Output is as expected:
pts_time=0.000000
pts_time=1.000000
pts_time=2.000000
pts_time=3.000000
pts_time=4.000000
pts_time=5.000000
pts_time=6.000000
pts_time=7.000000
pts_time=8.000000
pts_time=9.000000
pts_time=10.000000
pts_time=10.100000
pts_time=10.190000
pts_time=10.300000
pts_time=10.400000
pts_time=10.500000
pts_time=10.600000
pts_time=10.700000
pts_time=10.800000
pts_time=10.900000
pts_time=11.000000
pts_time=12.000000
pts_time=13.000000
pts_time=14.000000
pts_time=15.000000
pts_time=16.000000
pts_time=17.000000
pts_time=18.000000
pts_time=19.000000
pts_time=20.000000
pts_time=21.000000
pts_time=21.100000
pts_time=21.200000
pts_time=21.300000
pts_time=21.400000
pts_time=21.500000
pts_time=21.600000
pts_time=21.700000
pts_time=21.800000
pts_time=21.900000
Animated GIF that demonstrates the output:

(Created using ffmpeg -y -i output.mp4 -filter_complex "[0:v]split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -r 1 output.gif)
setptsfilter accepts expressions. It looks like the expression is going to be quite complicated in this case. – Rotem Feb 27 '24 at 21:32The command for splitting which I use is:
ffmpeg.exe -i input-mp4 -ss 00:00:50.355 -to 00:01:17.892 -c copy -strict -2 clip_1.mp4- it seems depending on ss and to it does not always EXACTLY as I want, but rather at specific keyframes...?!And for accelerating clips I use:
ffmpeg.exe -i clip_1.mp4 -filter:v 'setpts=0.1*PTS' -strict -2 clip_1_acc.mp4.But I get a Filter error. Do you think those commands are really correct?
– tim Feb 29 '24 at 09:58