The problem is that ffmpeg chooses the default for -vsync
based on the output muxer. Its mp4 muxer defaults to vsync 1, but it chooses a very high framerate so that it can put a frame at the exact right time for every input frame.
(The input frame timing isn't constant. It averages 14.97 fps, according to ffmpeg. Probably from a phone camera? They do variable FPS. I think they slow down to get more light for each frame, but it might be another reason.)
So ffmpeg will duplicate frames up to the 30k fps that it's chosen, or something. h.264 is pretty efficient at storing duplicate frames, but that's ridiculous.
Anyway, the solution is to use -vsync 2
on your ffmpeg command line. Or output to mkv, and then remux to mp4, but the reason that works is that mkv defaults to -vsync 2
. It's really that simple. You don't need to make your output CFR. Youtube handles arbitrary frame rates just fine, as long as they're <= 60
, and so do most other players. I assume phones are fine, since they make variable FPS videos in the first place. You don't need to use -r something
to force frame duplication to hit exactly 30fps or anything.
UPDATE ON 3 AUGUST 2017: According to a newer answer by user 林正浩, ffmpeg now has support for VP9 encoding through VAAPI. I still don't have the hardware required to test this though so my answer will be of limited help. I'll leave my original answer on how to encode VP9 in software below.
For some reason FFmpeg doesn't support VP9 encoding on Intel's QuickSync hardware encoder, even though they support H.264 and HEVC. A search through the FFmpeg source code repository shows it's not even a matter of it being disabled, the feature just hasn't been implemented yet. But if it does become available at some point in the future, it should be usable in a manner similar to the other QuickSync encoders: a switch like -c:v vp9_qsv
instead of -c:v libvpx-vp9
should do the job.
FFmpeg command line usage is the same on all platforms, with the one notable exception I know of being Windows users having to use NUL
instead of /dev/null
for output during the first pass of a 2-pass encode. But since you're doing 1-pass and lossless this shouldn't affect you.
If you want to speed up your encodes the most obvious thing you should try is setting an encoding speed value with the -speed
switch. Recommended values are numbers from 0 to 4, with 0 being really, really slow (think -preset placebo
in x264 but worse) but high quality and 4 being fast while being lower quality. ffmpeg uses -speed 1
by default which is a good speed-for-quality tradeoff for lossy encoding. However, I just did a quick lossless encoding test with different speed values and noticed a 32% reduction in file size when going from -speed 1
to -speed 0
with lossless encoding. The encoding time tripled though, so whether using 0 is worth it is up to you. The file produced by -speed 4
was only 1.1% larger than the one produced by -speed 1
though, and it was encoded 43% faster. So I'd say that if you're doing lossless and -speed 0
is too slow you might as well use -speed 4
.
Another important encoding performance increase is turning on multi-threading with the -threads
switch; libvpx doesn't automatically use multiple threads so this must be set manually by the user. You should also set the number of tile columns with the -tile-columns
switch. This option makes libvpx divide the video into multiple tiles and encode these tiles in parallel for better multi-threading. You can find recommended numbers for the amount of tile columns and threads in the "Tiling and Threading Recommendations" section of Google's VP9 encoding guide. As you can see, the number of threads used goes up with the number of tiles, which means that depending on the number of CPU cores available your processor might not be fully saturated while encoding sub-HD-resolution video. If you mainly encode low-resolution videos you might want to consider encoding multiple files at the same time.
However, there is yet another way to speed up VP9 encoding: multi-threading within a single column tile that can by turned on with -row mt 1
. As of April 4 (2017, hello future people), it isn't part of a released version of libvpx but will most likely be in libvpx 1.6.2. If you want to try it out before the next release you need to compile recent git versions of libvpx and ffmpeg from source. Just follow FFmpeg's compilation guide for your distro of choice but instead of downloading and extracting a release tarball do git pull https://chromium.googlesource.com/webm/libvpx
instead.
As for the veryslow
preset, that's only used in x264 and x265. libvpx uses the -speed
switch and additionally the -quality best
, -quality good
, or -quality realtime
options to define how much time the encoder is allowed to spend encoding a frame. The default is -quality good
because -quality best
is so slow it's unusable and -quality realtime
is meant to be used for time-critical applications like video calls and livestreaming.
Best Answer
If you want highest quality and minimal file size, you should use a compression-efficient codec. However, you first have to think about whether you want to do a (mathematically) lossless, visually lossless, or lossy encode:
Lossless encodes will of course result in higher file sizes, but they offer the benefit of preserving the original data. You can then extract individual frames from the video without any quality loss. Here, codecs such as HuffYUV or FFV1 could be used. These are often used for archival purposes, where the original material needs to be preserved, and generation loss is to be avoided.
libx264
(H.264),libx265
(HEVC) andlibvpx-vp9
can also be used in lossless mode:Visually lossless encodes throw away some data but preserve quality in such a way that humans are likely not to notice any difference between the original and the encoded video. So-called "intermediate" codecs such as ProRes (see here) are often used for this purpose. You can also do visually lossless encodes with
libx265
orlibx264
by specifying a low enough CRF value (explanation of CRF here), e.g. between 10 and 18 for x264.Lossy codecs, there are plenty. If you can sacrifice quality, you can reduce the file size drastically. VP9 and HEVC are more efficient than H.264 in terms of how much space they require, but they may take longer to encode. Forget about Theora and VP8. With VP9, HEVC and H.264 (at least with the encoders available in ffmpeg) you can set a CRF parameter that gives you the quality you want. You need to visually check your output to make sure that the amount of loss is not too severe for your purpose. For
libx264
, CRF values between 18 and 23 look "good".Generally, if you are aiming for archiving content, you should not just specify a target bitrate. Instead, you want to use a constant quality mode, where the encoder is free to use as many bits as it needs to retain the quality of the image (within certain bounds). Especially with the H.264 or HEVC encoders
libx264
andlibx265
, using-b:v
for a single-pass encode is not recommended, as it might lead to huge quality variations over time. (I wrote an article on different rate control modes here.) To summarize, if you're archiving, simply go for a constant CRF value.It's also very important that you do not resize the video, since that either introduces blurring (when upscaling), or throws away data (when downscaling). Simply leave out the
-s:v
option unless your images are much too big for video.Finally, when performing visually lossless or lossy encodes, you can trade off speed against compression efficiency. In other words, if you wait longer, you can make the file size smaller. Here, the
preset
options come into play: If you choose a preset likeultraslow
, the encoding process is going to take a long time, but the resulting file – assuming a given CRF – will be smaller, even though it has the same quality. VP9 has different options to control the speed, see here.One more thing: With
libx264
, you can use the-tune stillimage
option to optimize the encode for images. Also, choosing the lowest frame rate needed will help save file size, of course.Some documentation: