I have a mkv (h264) video that is 23.976 fps (24000/1001) but I want to convert it to 25fps without reencoding and loosing quality. I know mkvmerge can do it ( with option --default-duration '0:25fps') but I'd like to do it directly from ffmpeg if possible According to the docs this should work:

ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv

but when I execute it I only get the same video fps. What is the correct method to do it (if exists) in ffmpeg?

  • 1
    I believe that is not possible with FFmpeg at the moment. -r is not compatible with stream copy, and there are no bitstream filters to change the framerate. – Ely Jun 13 '16 at 14:36
  • 1
    too bad. I'll have to use mkvmerge every time. thanks – phate89 Jun 13 '16 at 14:42
  • 1
    There is a convoluted way to do this with regular ffmpeg and a direct way to do this with an old modded version of ffmpeg. If you're interested, I'll write it up as an answer. – Gyan Jun 13 '16 at 17:34
  • 1
    Yes thanks..I'd like to do it without extra tools (I already need ffmpeg) – phate89 Jun 13 '16 at 18:15
  • 1
    @Mulvya you're talking about this right ? I hesitated to link it, but it's old now.. Interested in the convoluted way to do it with regular FFmpeg though. – Ely Jun 17 '16 at 10:42

Here's the method using current versions of FFmpeg. It relies on the concat demuxer not rescaling the PTS of inputs after the first file, but simply applying a fixed offset. Let's say you have a 30 fps stream with a timescale of 15360 (typical of FFmpeg output). That means frame 0 has PTS 0 and frame 30 has PTS 15360. This would become a 45 fps stream if we could change the timescale to 23040 without affecting the PTS values.

Essentially, that's what the method below does.

1. Identify the source properties.

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
       30 fps, 30 tbr, 15360 tbn (default)

You want to note the source properties, especially resolution and tbn.

2a. (Optional) Change the timescale to something convenient, to make calculations simpler.

ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4

This gets us

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
       30 fps, 30 tbr, 30 tbn (default

If you do this step, the new timescale should be equal or an integral multiple of the original framerate.

2b. Calculate the timescale needed, so that for target framerate x, PTS of frame # x in the source should have the same value as the new tbn. If you carried out step 2a, this is very easy and it's simply the new framerate. So, for target fps 45, new tbn should be 45.

3. Generate dummy video.

ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4

All properties should be same like resolution, H.264 profile, pixel format, refs count..etc for best results.

4 Concat the videos.

First make a text file

file '0.mp4'
file 'in-v30.mp4'

Then, the concat

ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4

The output file will have the 2nd video playing at 45 fps.

5. Now, cleave off the dummy preroll

ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4

and you have

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
       45 fps, 45 tbr, 11520 tbn (default)

I did say this was convoluted!

| improve this answer | |
  • 1
    very clever, good answer. – Rowe Morehouse Dec 25 '17 at 0:14
  • What about the audio? – Freedo Jun 15 at 6:12
  • ulatekh's more recent answer is simpler, and also handles audio. – Camille Goudeseune Jul 20 at 18:56

Use -itsscale on the input video to achieve an effective framerate change. It works fine with -vcodec copy.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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