Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
770 views
in Technique[技术] by (71.8m points)

video - ffmpeg: reduce fps if over threshold but do not increase if under threshold

The ffmpeg CLI allows your to specify -r <fps> or -vf fps=<fps> to set a fixed FPS for your output.

The behavior I would like to get is:

  • Input can have arbitrary FPS (likely 20-120).
  • If input FPS is >30, reduce it to 30 FPS.
  • If input FPS is <=30, do not modify FPS.

More context:

Aforementioned options are great to normalize videos to a fixed FPS but not so good to make sure videos do not get over a certain FPS threshold efficiently. Indeed, if I set -r 30 and the video is 29 fps, ffmpeg is going to us a lot of CPU to raise it to 30 when it wasn't needed for my use-case.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This can be done on constant frame rate video - my method below may work on VFR video, but I don't have a suitable video to test.

Basic command:

ffmpeg -i 120.mp4 -vf "select='eq(n,0)+if(gt(t-prev_selected_t,1/30.01),1,0)'" -vsync 0 out.mp4

On a 120 fps stream,

Video: h264 (High) ... 120 fps, 120 tbr, 15360 tbn, 240 tbc

this produces

Video: h264 (High) ... 30.08 fps, 30 tbr, 15360 tbn, 240 tbc

What the select filter does, after selecting the first frame, is only select further frames if their interval from the previously selected frame is 1/30th of a second or more. In practice, this decimates videos to below 30 fps.

Two things to note:

1) the duration of the last frame is truncated, so the fps value will be slightly greater than the target. But all other frames cycle at 30 fps.

2) The output framerate is the highest number obtained by dividing the source rate by an integer. So, if in the command, 30.01 is replaced by 26, the result will be 24 tbr since neither 25 nor 26 can be obtained by dividing 120 by an integer. This has the benefit of avoiding motion stutter in the output. The divisor in the command should be slightly greater than the ceiling i.e. 30.01 instead of 30. Else, the results are wobbly.

If the input is 30 fps or less, then all frames are selected.


Addendum:

If the output of the above command is piped to another ffmpeg instance, the output fps is exact and the codec rate tbc is reset as well.

ffmpeg -i 120.mp4 -vf "select='eq(n,0)+if(gt(t-prev_selected_t,1/30.01),1,0)'" -vsync 0
        -c:v rawvideo -c:a pcm_s16le -f nut - | ffmpeg -f nut -i - out.mp4

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...