FFMPEG / libx264: How to specify a variable frame rate but with a maximum

compressionffmpegframeratevideo

Instead of providing a fixed frame rate to FFMPEG/libx264 (-r/-framerate), I would like to specify a variable frame rate with a MAXIMUM value, and allow libx264 to down the frame rate as it sees fit. The idea here is to get extra compression when there is something like an extended still frame (which happens A LOT in my source videos).

I realize that a predictive or bidirectional MPEG frame will compress really well, but it's also possible that the source frame rate is smaller than the one I intend to transcode to (possibly resulting in a BIGGER stream!).

Best Answer

Frustrated that you hadn't found an answer either, I was going to at least answer other people's questions about how to enable VFR (not VBR) output from FFMPEG.

The answer to that is the oddly named -vsync option. You can set it to a few different options, but the one you want is '2' or vfr. From the man page:

-vsync parameter
Video sync method. For compatibility reasons old values can be specified as numbers. Newly added values will have to be specified as strings always.

  • 0, passthrough

    • Each frame is passed with its timestamp from the demuxer to the muxer.
  • 1, cfr

    • Frames will be duplicated and dropped to achieve exactly the requested constant frame rate.
  • 2, vfr

    • Frames are passed through with their timestamp or dropped so as to prevent 2 frames from having the same timestamp.
  • drop

    • As passthrough but destroys all timestamps, making the muxer generate fresh timestamps based on frame-rate.
  • -1, auto

    • Chooses between 1 and 2 depending on muxer capabilities. This is the default method.

Note that the timestamps may be further modified by the muxer, after this. For example, in the case that the format option avoid_negative_ts is enabled.

With -map you can select from which stream the timestamps should be taken. You can leave either video or audio unchanged and sync the remaining stream(s) to the unchanged one.

However, I don't quite have enough reputation to post a comment to just answer that 'sub-question' that everyone seems to be having. But I did have a few ideas that I wasn't honestly very optimistic about... But the first one I tried actually worked. So.

You simply need to combine the -vsync 2 option with the -r $maxfps option, of course where you replace $maxfps with the maximum framerate you want! And it WORKS! It doesn't duplicate frames from a source file, but it will drop frames that cause the file to go over the maximum framerate!

By default it seems that -r $maxfps by itself just causes it to duplicate/drop frames to achieve a constant framerate, and -vsync 2 by itself causes it to pull the frames in directly without really affecting the PTS values.

I wasn't optimistic about this because I already knew that -r $maxfps puts it at a constant framerate. I honestly expected an error or for it to just obey whichever came first or last or whatever. The fact that it does exactly what I wanted makes me quite pleased with the FFMPEG developers.

I hope this helps you, or someone else later on if you no longer need to know this.