137

I am trying to convert a video clip (MP4, yuv420p) from 30 fps to 24 fps. The number of frames is correct so my output should change from 20 minutes at 30fps to 25 minutes at 24fps. Everything else should remain the same.

Try as I might everything I try with ffmpeg converts the frame rate but changes the number of frames to keep the same duration or changes the duration without altering the framerate.

So I have been typically trying things like;

ffmpeg -y -r 30 -i seeing_noaudio.mp4 -r 24 seeing.mp4

(I'm doing this on windows but normally would be on linux). That converts the framerate but drops frames so the total duration is unaltered.

Or I have tried

ffmpeg -y -i seeing_noaudio.mp4 -filter:v "setpts=1.25*PTS" seeing.mp4

Which changes the duration but not the framerate.

Surely I should be able to do this with a single ffmpeg command without having to reencode or even as some people suggested going back to the original raw frames.

Help please

3
  • 4
    -y flag indicates "Overwrite output files without asking". I had to go and find what that meant, to ensure I wasn't misunderstanding something here. I think it's immaterial to the question at hand, so should be removed to simplify things for everyone.
    – osullic
    Commented Sep 12, 2021 at 23:38
  • For -r, documentation says, "As an input option, ignore any timestamps stored in the file and instead generate timestamps assuming constant frame rate fps. As an output option, duplicate or drop input frames to achieve constant output frame rate fps."
    – osullic
    Commented Sep 12, 2021 at 23:41
  • For me in 2023 only last answer worked stackoverflow.com/a/68973714/5499118. TL;DL it uses -itsscale input option to rescale input timestamps. For some reason -r option mentioned by several answers did not work for me (ffmpeg version 4.4.2) (and one upvoted comment mentioned it btw). Commented Aug 3, 2023 at 7:27

7 Answers 7

123

With re-encoding:

ffmpeg -y -i seeing_noaudio.mp4 -vf "setpts=1.25*PTS" -r 24 seeing.mp4

Without re-encoding:

First step - extract video to raw bitstream

ffmpeg -y -i seeing_noaudio.mp4 -c copy -f h264 seeing_noaudio.h264

Remux with new framerate

ffmpeg -y -r 24 -i seeing_noaudio.h264 -c copy seeing.mp4
18
  • 2
    It looks promising, but I'm getting "Timestamps are unset in a packet for stream 0." and "pts has no value" warnings, and the resulting video is very choppy, as if it was playing two steps forward and one step back.
    – Kornel
    Commented May 13, 2019 at 14:58
  • 3
    Note about setpts: To double the speed of the video: setpts=0.5*PTS, To slow down your video, you have to use a multiplier greater than 1: setpts=2.0*PTS (half the speed) reference
    – lepe
    Commented May 16, 2019 at 6:33
  • 5
    It depends on the input framerate. Basic formula for PTS coefficient is (old rate)/(new rate). -r is simply the new framerate. The exact values for 23.976, 29.97 and 59.94 are 24000/1001, 3000/1001 and 60000/1001.
    – Gyan
    Commented Jul 9, 2019 at 11:04
  • 3
    If you get that error, upgrade ffmpeg. That flag is not required since 2016.
    – Gyan
    Commented Apr 12, 2020 at 6:45
  • 2
    When I do the remux, the resulting video is corrupted, smearing out artifacts all over the place. Also: "Timestamps are unset in a packet for stream 0"
    – Bram
    Commented Feb 12, 2021 at 1:26
100

You may consider using fps filter. It won't change the video playback speed:

ffmpeg -i <input> -filter:v fps=30 <output>

Worked nice for reducing fps from 59.6 to 30.

7
  • 6
    This works to keep the audio in sync with the video, thanks Commented May 23, 2020 at 5:18
  • 2
    Nice btw it turns out you can use this command to reduce OBS recordings size by a factor ~2 without sacrificing neither quality or smoothness (if using 60fps change to fps=fps=60). All other commands, including reencoding with best quality possible with x264/nvenc gave visible artifacts
    – leavittx
    Commented Jan 17, 2021 at 19:19
  • 5
    why use fps=fps= specifying the keywords twice? thanks
    – ch271828n
    Commented Aug 22, 2021 at 3:57
  • 5
    @ch271828n, I took it from the example. Basically, the first fps is the name of the filter and the second fps is the input parameter, also looks like you can skip it.
    – kelin
    Commented Aug 25, 2021 at 17:15
  • 2
    ffmpeg -i <input-video> -filter:v fps=15 <output-video>
    – Coreus
    Commented Sep 13, 2021 at 17:37
37

Simply specify the desired framerate in "-r " option before the input file:

ffmpeg -y -r 24 -i seeing_noaudio.mp4 seeing.mp4

Options affect the next file AFTER them. "-r" before an input file forces to reinterpret its header as if the video was encoded at the given framerate. No recompression is necessary. There was a small utility avifrate.exe to patch avi file headers directly to change the framerate. ffmpeg command above essentially does the same, but has to copy the entire file.

7
  • Neither answer works for me with a VP8 WebM file (which I'm trying to slow down from 25 fps to 6fps without re-encoding). Output video is same as input.
    – 7vujy0f0hy
    Commented Jul 5, 2018 at 20:00
  • 14
    This doesn't change the frame rate of the output file at all for me.
    – Elder Geek
    Commented Aug 12, 2018 at 19:18
  • This works for me reducing the frame rate but it puts the audio out of sync. Commented Oct 2, 2020 at 17:53
  • This changes the speed of the film in my case
    – Dev.Jaap
    Commented Dec 24, 2021 at 14:34
  • This re-encodes the video to the output format..
    – LAamanni
    Commented Oct 2, 2022 at 9:38
6

In general, to set a video's FPS to 24, almost always you can do:

With Audio and without re-encoding:

# Extract video stream
ffmpeg -y -i input_video.mp4 -c copy -f h264 output_raw_bitstream.h264
# Extract audio stream
ffmpeg -y -i input_video.mp4 -vn -acodec copy output_audio.aac
# Remux with new FPS 
ffmpeg -y -r 24 -i output_raw_bitstream.h264 -i output_audio.aac -c copy output.mp4

If you want to find the video format (H264 in this case), you can use FFprobe, like this

ffprobe -loglevel error -select_streams v -show_entries stream=codec_name -of default=nw=1:nk=1 input_video.mp4

which will output:

h264

Read more in How can I analyze file and detect if the file is in H.264 video format?


With re-encoding:

ffmpeg -y -i input_video.mp4 -vf -r 24 output.mp4
4
  • 2
    I had video encoded in h265. Extracted video stream with ffmpeg -y -i input_video.mp4 -c copy -f hevc output_raw_bitstream.h264 (hevc instead of h264) Then remuxed that stream successfully with desired framerate. Thanks! Commented Jan 20, 2021 at 9:45
  • If the audio stream has timestamp gaps, your first set of commands will destroy those gaps. Also, the audio will be out of sync as it hasn't been retimed.
    – Gyan
    Commented May 27, 2021 at 5:30
  • Your re-encoding command doesn't conform the video like the OP wants ("my output should change from 20 minutes at 30fps to 25 minutes at 24fps"), it will instead change the 'density' of frames per-second to 24. But the bigger issue is that the syntax is wrong - ffmpeg will abort with an error. You have added -vf but it has no argument so ffmpeg's parser will consume -r as its arg and error out.
    – Gyan
    Commented May 27, 2021 at 5:34
  • Can confirm the audio is completely messed up by running the above commands as the stream is not re-encoded at the correct timings.
    – jjisnow
    Commented Sep 13, 2021 at 6:59
4

To the best of my knowledge you can't do this with ffmpeg without re-encoding. I had a 24fps file I wanted at 25fps to match some other material I was working with. I used the command ffmpeg -i inputfile -r 25 outputfile which worked perfectly with a webm,matroska input and resulted in an h264, matroska output utilizing encoder: Lavc56.60.100

You can accomplish the same thing at 6fps but as you noted the duration will not change (which in most cases is a good thing as otherwise you will lose audio sync). If this doesn't fit your requirements I suggest that you try this answer although my experience has been that it still re-encodes the output file.

For the best frame accuracy you are still better off decoding to raw streams as previously suggested. I use a script for this as reproduced below:

#!/bin/bash
#This script will decompress all files in the current directory, video to huffyuv and audio to PCM
#unsigned 8-bit and place the output #in an avi container to ease frame accurate editing.
for f in *
do
ffmpeg -i "$f" -c:v huffyuv -c:a pcm_u8 "$f".avi
done

Clearly this script expects all files in the current directory to be media files but can easily be changed to restrict processing to a specific extension of your choosing. Be aware that your file size will increase by a rather large factor when you decompress into raw streams.

3

You can use this command and the video duration is still unaltered.

ffmpeg -i input.mp4 -r 24 output.mp4
3
  • If you do this your video will be cut off. You should specify the -r parameter before the input file.
    – zenw0lf
    Commented Oct 18, 2018 at 16:48
  • 2
    @zenw0lf cut off how? I tested this and it worked fine. It's documented on ffmpeg's website.
    – Shayan
    Commented Nov 6, 2019 at 0:26
  • 2
    After using this option, my video was just stretched out from 5 minutes to 10.
    – ashrasmun
    Commented Jan 2, 2020 at 12:34
3

You can try for example (to convert from 25 fps to 24 fps)

ffmpeg -itsscale 1.0416667 -i "your input file" -vcodec copy "output file"

the itsscale value of 1.0416667 is 25/24 as a float variable for ffmpeg (0.1234567 is the float values format - don't use 1.04166666666666666667 or a double value : note that you can't use the expression/formula "25/24" here) This will scale the frame rate times from 25 to 24 fps, keeping the same number of frames, but lengthening the video by 1.0416667 .

going from 23.976 to 24 or going from 29.97 to 30 this value would be 0.999)

If you have audio you would include an audio filter to rescale the audio without a pitch change by changing the tempo using the atempo settings, you should include a compressor and bit rate as well. For subtitles, you just need to include the -codec:s

so we would have

ffmpeg -itsscale 1.0416667 -i "input file" -filter:a atempo="24/25" -codec:a ac3 -b:a 640k -vcodec copy -codec:s copy "output file"

where I used the ac3 audio codec at a bitrate of 640K and the expression "24/25" which is allowed here :NOTE: the 24/25 is the inverse of the itsscale value of 25/24(=1.0416667)

Note: if you have more than 1 stream, (video or audio or subtitle) you may need to include the -map i:v:o (or 0:a:0 or 0:s:0 where 'i' is the input stream number and 'o' is the stream track to output

4
  • 1
    Should probably add a note that the atempo="value" ; value is the rate at which the audio changes.
    – Georg
    Commented Sep 3, 2021 at 19:21
  • That is the only answer which worked for me in 2023 (ffmpeg version 4.4.2). It works as OP asked - w/out reencoding, takes couple of seconds. Thanks a lot! P.S. btw I needed to change duration of a video only to march audio so I did it on extracted video stream. Commented Aug 3, 2023 at 7:21
  • 1
    For some reason file plays correctly in mpv but (specified) fps doubled whereas I extended duration by less than 1%. Commented Aug 3, 2023 at 7:32
  • float would be python -c 'print("%f" % (25/24))' = 1.041667. or does ffmpeg parse it as %.7f with a precision of 7? maybe itsscale will accept expressions like 25/24 in the future
    – milahu
    Commented Apr 29 at 8:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.