67

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

Gyan
  • 63,018
  • 7
  • 100
  • 141
J Brand
  • 671
  • 1
  • 5
  • 4

6 Answers6

75

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
Gyan
  • 63,018
  • 7
  • 100
  • 141
  • 1
    Thanks for that. The first of those worked but the second didn't returning an error message along the lines of "Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument". I've no idea what that meant but the first one worked. – J Brand Aug 02 '17 at 17:59
  • Looks like the video codec is not H.264. Which is it? – Gyan Aug 02 '17 at 18:19
  • `-filter:v fps` OR `-r fps`. which one is preferred? – Mehran Dec 14 '18 at 05:58
  • 1
    Generally, `-r`. Use the filter when you need to change framerate before applying further filters. – Gyan Dec 14 '18 at 06:59
  • 1
    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 May 13 '19 at 14:58
  • 2
    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](https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video) – lepe May 16 '19 at 06:33
  • Can you add the PTS and -r values for other commons FPS like 23.976, 60, etc? – Freedo Jul 09 '19 at 06:06
  • 3
    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 Jul 09 '19 at 11:04
  • If you get this error: "The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it." just add `-strict -2` before `seeing.mp4`. – totymedli Apr 11 '20 at 22:17
  • 1
    If you get that error, **upgrade** ffmpeg. That flag is not required since 2016. – Gyan Apr 12 '20 at 06:45
  • What about the audio? your no reencoding command creates a file without audio – Freedo Jun 15 '20 at 03:46
  • Add the original MP4 as 2nd input in the remux command. – Gyan Jun 15 '20 at 06:03
  • 1
    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 Feb 12 '21 at 01:26
25

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

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

Worked nice for reducing fps from 59.6 to 30.

kelin
  • 9,553
  • 6
  • 63
  • 92
  • 2
    This works to keep the audio in sync with the video, thanks – Giovanni G. PY May 23 '20 at 05:18
  • 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 Jan 17 '21 at 19:19
19

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.

MKaama
  • 1,368
  • 2
  • 15
  • 24
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
gsamaras
  • 66,800
  • 33
  • 152
  • 256
  • 1
    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! – Sergey P. aka azure Jan 20 '21 at 09: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 May 27 '21 at 05: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 May 27 '21 at 05:34
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.

Elder Geek
  • 767
  • 1
  • 8
  • 15
2

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

ffmpeg -i input.mp4 -r 24 output.mp4
Tim Le
  • 213
  • 3
  • 5