1001

How can I use ffmpeg to reduce the size of a video by lowering the quality (as minimally as possible, naturally, because I need it to run on a mobile device that doesn't have much available space)?

I forgot to mention that when the video can use subtitles (*.srt or *.sub), I'd like to convert them too to fit the parameters of the converted video file.

xralf
  • 14,393
  • 32
  • 119
  • 165
  • 30
    I haven't used it but the ffmpeg man page shows a -fs option to limit the output size, does something like ffmpeg -i in.avi -fs 100M out.avi work? – Kevin Jan 10 '12 at 23:41
  • 6
    The .avi is not the main issue.. avi is just a container. The main issue is which codecs you use.. Many (most?) .avi vids use older style codecs (eg XviD) which are fine, but are larger for the same quality when compared to the later generation of codecs .. You can typically get a tight encoding by using the H.264 video compression standard (eg. codecx264) and aac compression for audio.. The container and codecs you use is up to you and your phone... The .mp4 container is well accepted.. (but can your phone handle it: see this link – Peter.O Jan 11 '12 at 01:16
  • @Kevin This wants more parameters for conversion. – xralf Jan 11 '12 at 08:34
  • If it's too overwhelming, there is a fallback option of using Quicktime which has export presets of 480p etc. – Sridhar Sarnobat Aug 29 '18 at 20:10
  • @Kevin Best answer. Please, post as an official answer so that I can upvote. – Tarik Nov 23 '19 at 07:50
  • This answer appears on the top of search engines for the search term "reduce video resolution ffmpeg", and the accepted answer increases the size of videos. – Wolfpack'08 Mar 04 '20 at 02:11
  • 2
    @Wolfpack'08 - I have seen size increased with the second command of accepted answer when the input video was already the result of the same command. So: A->B->C, B was between 4 and 5 times smaller than A; but C was a bit larger than B. – cipricus Mar 04 '20 at 11:46
  • @cipricus thank you for explaining. I've seen the increases with both video formats. I'm using handbrake to reduce video size, right now, but I did find an article with beginner-level cookbook-style commands for reducing resolution and stuff; so, I hope I'll be off of the GUIs soon. – Wolfpack'08 Mar 07 '20 at 03:49
  • 8
    @Kevin, using the -fs option seems to crop the video for me, rather than keeping it the same length, but reducing quality. – Garrett Nov 26 '21 at 22:18
  • 1
    In addition to what Garrett mentions: -fs option will force the video to stay below the requested max-length. So, I would recommend against it. If your other settings are ok and reducing the output-video-size, you won't need the -fs option to crop. If using -fs anyway, beware of its danger and check your output, assuring it contains the end of the movie. – mistige Jan 29 '22 at 13:05

17 Answers17

1503

Update 2020: This answer was written in 2009. Since 2013 a video format much better than H.264 (AVC) is widely available, namely H.265 (HEVC; better in that it compresses more for the same quality, or gives higher quality for the same size). To use it, replace the libx264 codec with libx265, and push the compression lever further by increasing the CRF value — add, say, 4 or 6, since a reasonable range for H.265 may be 24 to 30. Note that lower CRF values correspond to higher bitrates, and hence produce higher quality videos.

ffmpeg -i input.mp4 -vcodec libx265 -crf 28 output.mp4

You can also specify a libx265 preset, which affects file size and quality.


To see this technique applied using the older H.264 format, see this answer, quoted below for convenience:

Calculate the bitrate you need by dividing your target size (in bits) by the video length (in seconds). For example for a target size of 1 GB (one gigabyte, which is 8 gigabits) and 10 000 seconds of video (2 h 46 min 40 s), use a bitrate of 800 000 bit/s (800 kbit/s):

ffmpeg -i input.mp4 -b 800k output.mp4

Additional options that might be worth considering is setting the Constant Rate Factor, which lowers the average bit rate, but retains better quality. Vary the CRF between around 18 and 24 — the lower, the higher the bitrate.

ffmpeg -i input.mp4 -vcodec libx264 -crf 20 output.mp4
Pablo A
  • 3,292
  • The video of size 338 MB was reduced on size 130 MB. The quality rapidly lowered. Is there some explanation for this process? The original author doesn't explain his guidline. – xralf May 11 '12 at 07:41
  • Another thing are the subtitles. I don't want them to embed, but I'd like the new srt or sub file to fit the new converted video. – xralf May 11 '12 at 07:42
  • You could try a higher bit-rate, or maybe experiment with the Constant Rate Factor the original author talks about. Other than that, I'm sorry, but I don't have much knowledge of ffmpeg. I was able to answer only because I've wanted to compress videos with ffmpeg in the past and had come across that answer. – Vicky Chijwani May 11 '12 at 09:16
  • Thanks, your answer shifts the question closer to the solution. I will leave it open for some time yet. – xralf May 11 '12 at 09:24
  • I found that the former made the first few seconds blocky and pixelized (a la this question on SuperUser) but adding the -crf option without changing anything else resolved this issue. – abeboparebop Aug 23 '16 at 15:59
  • The second command took a 101MB mp4 file I had, and created a 121MB output.mp4. – Martin Argerami Dec 28 '16 at 19:11
  • 129
    Second command, using -crf 24 took a 255.3MB video I had and reduced it to 72.7MB without lowering the quality noticeably. Have an upvote! – Patrick Roberts Mar 25 '17 at 18:14
  • 33
    Impressively reduced a ~2G video to 14MB, still looks good, this was the first search result and it's exactly what I was looking for, thanks! – Siôn le Roux Mar 30 '18 at 12:20
  • 36
    Might be good to note that you can now use libx265 for even more size reduction. – ZN13 Apr 13 '18 at 19:55
  • 42
    Used ffmpeg -i input.avi -vcodec libx264 -crf 24 output.avi. It reduced a 100mb video to 9mb.. Very little change in video quality. Thank you! – alpha_989 May 23 '18 at 22:02
  • 29
    The answer is good, but I want to point out another detail: Some devices (e.g. old tablets, a raspberry pi 1) may not only feature low memory, but also low processing power. In such cases, you may want to limit the complexity of the decompression by restraining the profile via -profile:v baseline (as shown at https://superuser.com/questions/371460/command-encoding-h264-baseline-profile-level-1-with-ffmpeg-and-libx264). Also -pix_fmt yuv420p is good for compatibility and -movflags +faststart allows an early start. – Hermann Jan 01 '19 at 21:30
  • Second option is awesome, original file was 376MB mp4, crf of 20 reduced it to about 49MB without considerable artifacts, worth an upvote! – Harshiv Jan 24 '19 at 12:08
  • @Hermann The second solution also solved problems I had with videos on a laptop with plenty of memory but limited CPU power. My cheap IdeaPad 100S with an Intel Celeron N3050 1.6GHz CPU choked on most of my downloaded Youtube videos but using the 2nd solution solved it completely. If I come across one that dosn't play, I'll try your solution. – John Pankowicz Jan 28 '19 at 01:14
  • 1
    This takes forever! Is this normal, the time it takes. I am trying to lower a 1G video. – Fadwa Apr 03 '19 at 09:29
  • 1
    Worked for me. Reduced 625MB to 140 with libx265 -crf 24. And the initial video was already cut at low quality in iMovie. Interestingly, running it again over the resulting file reduced it further to 123MB. – André Levy Jun 11 '19 at 08:02
  • 1
    Another success story here with ffmpeg -i output.mp4 -vcodec libx264 -crf 24 output_compressed.mp4 from 2.5 GB 1920x1080 20min video down to 287.9 MB, huge reduction, amazing! –  Mar 05 '20 at 19:39
  • Please note that the quoted answer included a dramatic miscalculation (mixing bytes and bits) which lead the “fixed bitrate” method to compress 8 times more than intended. This has been fixed. – Maëlan Mar 15 '20 at 22:58
  • 3
    The first method i.e. achieving a given constant bitrate is discouraged as it degrades quality much more than needed, because of its rigidness (the bitrate must never raise above the threshold). To control the size (rather than quality) of the output, the recommended method is to achieve a given average bitrate using two passes, as explained by this answer https://unix.stackexchange.com/a/289322. This is better because the 1st pass allows the codec to allocate bit usage based on needs, i.e. it can spare bits on easy frames and have more available to spend on more demanding frames. – Maëlan Mar 16 '20 at 01:37
  • Second command it's the best. And then convert mp4 to webm, – somenxavier Mar 19 '20 at 09:30
  • 1
    +1: This reduced a .mov file's to a fifth without reducing resolution or image quality: ffmpeg -i vid.mov -b 8507k out.mp4. The .mov file's format was MPEG-4 according to mediainfo. It used h264 as the video codec, judging from the output of ffmpeg -i vid.mov 2>&1. – Matthias Braun Mar 20 '20 at 12:24
  • 19
    don't use libx265 if you plan to use it on html5 video, see https://gist.github.com/Vestride/278e13915894821e1d6f#support – Lluís Apr 13 '20 at 11:02
  • 1
    For the first option my ffmpeg complains: Please use -b:a or -b:v, -b is ambiguous. BTW, Why not just decrease the original bitrate by a certain factor? get the bitrate with ffmpeg -i inputfile and then just use a lower number in the first option. May this avoid the issue of actually increasing the video size? – alexis May 05 '20 at 18:35
  • 3
    Reminder to everyone who upvotes this answer to also upvote the answer this one is quoted from. Gotta upvote the OP. – Joeytje50 May 10 '20 at 13:50
  • 3
    Here is a very well-written and informative article about CRF (Constant Rate Factor) and what it means: https://slhck.info/video/2017/02/24/crf-guide.html#:~:text=The%20Constant%20Rate%20Factor%20(CRF,expense%20of%20higher%20file%20sizes. – Gabriel Staples Jun 15 '20 at 03:13
  • 1
    Hi. After ffmpeg -i input.mp4 -vcodec libx265 -crf 28 output.mp4 ios devices cannot seem to play the video. AFAIK after ios11 HEVC is supported. Any idea on this? – Arka Prava Basu Jun 26 '20 at 08:19
  • I was able to reduce file size from 10 megabits to 5 megabits. My video was 1080p, so I added -s hd720 and it reduced it from 10 megabits to 1 megabit. – kethan1 Oct 05 '20 at 19:31
  • 2
    After using libx265 can not open video nor in Chrome nor in QuickTime Player. libx264 yields 15% larger file, but it's more compatible. – Shimon S Nov 19 '20 at 19:33
  • 2
    ffmpeg -i input.mp4 -vcodec libx264 -crf 24 output.mp4 and i got a video of size 80mb from 290 mb. No visible loss of quality(at least to my eyes). Thanks. – Abinash Dash Dec 13 '20 at 16:33
  • if h.265 is better than 264 (and you said that in 2013) why now in 2021 h.264 is still widely used over h.265? – vdegenne Jan 05 '21 at 22:55
  • 1
    Tried first method and cant use the file with ios. Instead I used ffmpeg -i input.mp4 -vcodec libx264 -crf 28 output.mp4 and worded fine. On converting some 58Mb file, libx265 gave a 10MB while using libx264 gave 13Mb, so not much difference and less headaches – West Feb 27 '21 at 05:12
  • 2
    @AkraPravaBasu: According to Comparing H.265 (HEVC) and H265 video file size, adding -tag:v hvc1 allowed QuickTime (on a Mac) to play converted H265 files. – Miles Mar 25 '21 at 01:57
  • It is also worth noting that libx265 is not the only H.265 encoder. By compiling ffmpeg with hardware acceleration options (which is available on recent Mac, Linux and Windows machines), faster encoders will be available (for example, -vcodec hevc_qsv on Linux/Windows). See: https://trac.ffmpeg.org/wiki/HWAccelIntro – Tianyi Shi Apr 01 '21 at 20:28
  • 4
    This answer should probably also mention that H.265 hardware decoding (or encoding!) may not be supported on all devices and that the encoding process is typically much slower than H.264. – Mateen Ulhaq May 01 '21 at 05:51
  • 2
    For @arka-prava-basu and @shimon-s , adding -tag:v hvc1 makes the result Quicktime compatible, with no change to file size! – Heath Raftery Jan 26 '22 at 11:14
  • 1
    Caveat Emptor: If I upload the 265 video to reddit, it gets refused, but the 264 is accepted. – Bram Mar 08 '22 at 18:23
  • could you also recommend the best free video format? ideally something made with agpl, even if ffmpeg still haven't done it themselves. – cregox Mar 19 '22 at 08:23
  • Works great ! got down to approx. 88 MB from 412 MB ! – code2be May 18 '22 at 18:18
  • 1
    To keep MacOs preview function ffmpeg -i i.mp4 -c:v libx265 -preset fast -crf 30 -tag:v hvc1 -c:a eac3 -b:a 224k o.mp4 – Ax_ Aug 15 '22 at 06:11
  • 3
    You may also be interested what -crf flag does (Constant Rate Factor). ⦿ For x264 and x265 the range of the quantized scale is 0-51: 0 is lossless, 23 is default, and 51 is the worst possible. ⦿ CRF 28 on x265 is approximately equivalent to CRF 23 on x264 ⦿ x265 compression is significantly better, but it's way much slower (in my test I got 16Mb mp4 video with libx264 -crf 24 and it becomes 4Mb with libx265 -crf 28 – Letfar Oct 18 '22 at 09:48
  • @VickyChijwani Thank you for this answer; it was useful. I am hoping to reduce the output size even further by making the video grayscale. I found this related question: https://stackoverflow.com/questions/32384057/ffmpeg-black-and-white-conversion Particularly this section: Grayscale. For some reason I could not figure out how to modify your latest version of the answer to merge it with the example provided in this comment. The ffmpeg tool has a vast number of arguments that I could supply and I find it confusing. Please help if you would. Do you observe a reduction in file size? Thank you. – Leonid Feb 22 '23 at 09:08
  • I am unable to find the -crf option in the ffmpeg man page. Could somebody please give a reference to its documentation, so that I can better understand it? Apart from that: the command worked beautifully. – packoman May 30 '23 at 12:39
  • 1
    I've been having issues when sharing H.265-encoded videos on Telegram. The videos play fine when opened, but the thumbnail displayed in the chat is a low-res static image, whereas with H.264, the video itself is played as a thumbnail, with good resolution. Apparently, 265 is not as universal as 264 yet. – ironcito Jun 05 '23 at 17:39
  • If you don't want the audio touched you need to add -c:a copy – Robert Koernke Sep 20 '23 at 13:14
  • interestingly, sometimes I get bigger output, sometimes 3x the size! Why? – John Smith Nov 09 '23 at 12:15
  • Does this answer still hold in 2023 or we have any updates with better options? – Raymond Dec 13 '23 at 18:39
  • This is a brilliant answer! It made my video go from 567Mb to 11Mb! – Dmitry Kamenetsky May 28 '24 at 04:45
  • When looping trough many videos some of them gives error maybe incorrect parameters such as bit_rate, rate, width or height. This version fix that: ffmpeg -i input.mp4 -vf crop='iw-mod(iw\,2):ih-mod(ih\,2)' -vcodec libx265 -crf 24 output.mp4' (Source: https://stackoverflow.com/a/50372936/1342186). Final loop: for i in *mp4; do ffmpeg -i $i -vf crop='iw-mod(iw\,2):ih-mod(ih\,2)' -vcodec libx265 -crf 24 ${i%mp4}_light.mp4; done – alexis Aug 16 '24 at 14:23
  • Great command line and amazing compacting video result! Note for WhatsApp app users: to import video (to a message) necessary to change codec to H.264. Command line: from -vcodec libx265

    to -vcodec h264

    – T77 Jan 11 '25 at 14:13
  • @packoman, years late, but one of the links posted shows how to use this for 265. It's not the man page, but it's still documentation. – ps2goat Jan 22 '26 at 01:31
  • While this encoding does seem to do a good job (115MB -> 13MB for my 3:26 long video), the codec is not free for playback in Windows anymore. – ps2goat Jan 22 '26 at 01:40
  • @ps2goat Get Linux Mint cinnamon and solve all your problems: https://linuxmint.com/edition.php?id=326 – Jack G Jan 23 '26 at 09:17
  • @JackG it's not about me, it's about others being able to view it – ps2goat Jan 23 '26 at 21:28
284

You mentioned wanting to reduce the file size to fit more videos on a mobile device, which is my use case as well.  All the answers here are for reducing the compression quality, but nobody has mentioned reducing the video frame size.  It's a lot quicker, up to multiple times faster depending on your source, and the amount of the resolution decrease, as there are fewer pixels to be encoded.  As a result, the file size can be reduced significantly.

The downside is you also lose a large amount of quality because fewer pixels means less image detail.  But when converting for a small device this may be acceptable.

See the FFmpeg docs on scaling for more info.

To scale to half size:

ffmpeg -i input.mkv -vf "scale=trunc(iw/4)*2:trunc(ih/4)*2" half_the_frame_size.mkv

One-third size:

ffmpeg -i input.mkv -vf "scale=trunc(iw/6)*2:trunc(ih/6)*2" a_third_the_frame_size.mkv

One-quarter size:

ffmpeg -i input.mkv -vf "scale=trunc(iw/8)*2:trunc(ih/8)*2" a_fourth_the_frame_size.mkv

One-fifth size:

ffmpeg -i input.mkv -vf "scale=trunc(iw/10)*2:trunc(ih/10)*2" a_fifth_the_frame_size.mkv

In these examples, the size is divided by twice the value and multiplied by two to ensure the pixel size is a multiple of two, which is required for some codecs, including H265.

You can also scale to a specific size in one parameter and automatically keep the same ratio using -2 for the other. For example 360 pixels in one direction would use:

-vf "scale=-2:360"

Be aware that changing the resolution always requires re-encoding, so all the ins and outs of the other answers apply here as well, and these examples are compatible with H265 options like -c:v libx265 -crf 28. 

Xen2050
  • 2,433
georgiecasey
  • 3,041
  • 41
    This answer is under-rated. It significantly reduced both the size and speed of the encoding. Thank you! – Ellery Familia Mar 03 '21 at 16:28
  • 6
    ...and reduces significantly the video quality, even with /2. – Déjà vu Aug 14 '21 at 04:48
  • could you also recommend the best free video format? ideally something made with agpl (even if ffmpeg still haven't done it themselves https://talk.ahoxus.org/notice/AHZ626fsOe14jNUcng#ffmpeg). – cregox Mar 19 '22 at 08:33
  • 1
    how about a fixed size but keep the aspect ratio? – cregox Mar 19 '22 at 08:40
  • 2
    Don't forget -preset veryslow. The slower the preset it, the more the compression is and the lesser is the file size. For one of my small video, -preset placebo created 2664250 Bytes file, -preset veryslow create of 2473329 Bytes file -preset slower created of 2589027 Bytes file, -preset slow created 3370628 Bytes file, -preset medium created 3410426 Bytes file, -preset ultrafast created 10708290 Bytes file. Ultrafast takes smallest amount of time to render the video, but video is biggest. So I will choose slow/veryslow/medium. More info: https://trac.ffmpeg.org/wiki/Encode/H.264 – 15 Volts Jun 06 '22 at 20:21
  • 2
    @déjà-vu: I reduced an MP4 from my phone camera to ⅓ the resolution, as described above, and the result is excellent, so clearly YMMV. – Michael Scheper Apr 07 '23 at 07:12
  • 1
    wow, this reduces size by more than 10x. truly underrated! – muon Apr 07 '23 at 22:26
131

I tested most of the other proposed answers to this question. The test data conclusions are below. These are the proposed answers that I tested:

(BR) Modify the bitrate, using:

ffmpeg -i $infile -b $bitrate $newoutfile 

(CR) Vary the Constant Rate Factor, using:

ffmpeg -i $infile -vcodec libx264 -crf 23 $outfile

(SZ) Change the video screen-size (for example to half its pixel size), using:

ffmpeg -i $infile -vf "scale=iw/2:ih/2" $outfile

(BL) Change the H.264 profile to "baseline", using:

ffmpeg -i $infile -profile:v baseline $outfile

(DF) Use the default ffmpeg processing, using:

ffmpeg -i $infile $outfile

DATA

  • "size" - percent pixel size of the converted video in relation to the original.
  • "bitrate" - bitrates of original and converted videos.
  • "definition" - pixel size of videos.
  • "convert" - time to convert the video in seconds.

I calculated the target bitrate for (BL)using the proposed method.

=== File A - How Node Is Helping To Propel Angular-Fnbixa7Ts6M.mkv ===

            original    BR         CR         SZ         BL         DF
            --------    ---        --         --         --         --
size        64152 kb    214%       76%        40%        83%        76%
bitrate     411 kb/s    883        313        165        342        313
definition  1920x1080   1920x1080  1920x1080  960x540    1920x1080  1920x1080
convert     --          648        509        225        427        510

=== File B - Using GraphQL with Angular _ By - Lee Costello-OGyFxqt5INw.mkv ===

            original    BR         CR         SZ         BL         DF
            --------    ---        --         --         --         --
size        410301 kb   33%        109%       28%        143%       109%
bitrate     2687 kb/s   880        2920       764        3843       2920
definition  3840x2160   3840x2160  3840x2160  1920x1080  3840x2160  3840x2160   
convert     --           2307       3188       1116       2646       3278

CONCLUSIONS

  • The (SZ) method is definitely the quickest method. It was 2X to 4X faster. This can be very much an issue on high-def videos, since all of the other methods took longer to convert than the actual length of the video! For example, The (CR) method took 53 minutes to convert the 21 minute video.

  • The (SZ) method is definitely the best method if the definition of the video is larger than the definition of the screen that will be displaying it. For example, if your phone can only display a 1080p picture, sending it a 3840x2160 video is just wasteful. It would be best to half its size to 1080p.

  • Some of the proposed answers actually INCREASED the size of some videos. For example, the (BR) method more than doubled the size of the 1080p sample. It did however make the 2160p size one-third. For the high-def sample, the (CR), (BL) and (DF) methods all INCREASED the size of the video.

Correct (or best) Answer

It is always best to first lower the resolution to the maximum supported by your target display.

If you want to reduce file size further, it will depend on personal choices. You can either reduce information content or increase compression.

  • You can lower the resolution more if that is not something that concerns you.

  • If the video doesn't include fast action scenes, you may want to lower the frame rate.

  • If you have a powerful processor and space is the only issue, you can increase the compression rate.

  • Bit rate is a combination of multiple factors. So just telling ffmpeg to lower the bit rate may not give you the results you want.

  • Another way of lower information content is to lower the color depth. How to do this was not yet discussed.

John Pankowicz
  • 1,449
  • 1
  • 9
  • 8
  • what if we mix 2 method for better result like starting by (SZ) then end with a (CR)? – Aleyam Jan 04 '20 at 14:20
  • 1
    @aleyam It depends entirely on which ones you are mixing and in what order. Starting with SZ and ending with CR makes sense. This is an example of what I mention in "Correct (or best) Answer". You are first lowering the resolution and then increasing compression. But different combos or different orders make no sense - for example doing SZ after CR. Also some methods would work against each other. For example, CR tries to dynamically vary the frame bitrate but BR tries to keep it constant. – John Pankowicz Jan 04 '20 at 18:24
  • 1
    Thanks for having gathered this data. Unfortunately, this benchmark compares apples to oranges.

    (1) Method DF adds no info to the picture since it is identical to Method CR, i.e. re-encoding with libx264 and CRF 23, i.e. ffmpeg’s default settings.

    (2) Method BL is not actually a compression method, and the output size varies (in either direction) because of other, uncontrolled factors. [Lowering the “profile” of H.264 to “baseline” means ensuring compatibility with some legacy hardware—which is largely unnecessary as of 2019 since H.264 has been around for more than 15 years.]

    – Maëlan Mar 16 '20 at 18:09
  • 2
    (3) BR literally applies a constant bitrate, so the resulting bitrate is obviously what you asked for (approx.). If you ask for twice that of the input, then it should come as no surprise that the output is twice as large! [Also, note that encoding to a constant bitrate is discouraged, you should instead do a 2-pass average bitrate encoding, see other answers.]

    (4) CR provides an interesting data-point but remains limited to one arbitrary value for the CRF. With the same shortcoming that if you pick a target quality higher than that of the input, then you get a larger output.

    – Maëlan Mar 16 '20 at 18:12
  • 1
    (5) The running time of encoding is proportional to the (unknown) number of frames of the input video, hence it would be better expressed as a ratio (running time of encoding) / (length of video) or better yet, as frames encoded per second (because different videos have a different number of frames per second). – Maëlan Mar 16 '20 at 18:14
  • 1
    It is wise words however that you should always start by shrinking your video to the resolution of the display you are targeting—a larger resolution is just a waste of everything. But it is independent from applying either BR or CR.

    Hence, a more relevant benchmark would compare BR with several target bitrates and CR with several CRFs, showing for each entry the three sides of the tradeoff: achieved bitrate, achieved quality (after encoding, libx265 reports the “average QP”—the lower the better) and running time of encoding. And have another benchmark about shrinking.

    – Maëlan Mar 16 '20 at 18:15
  • 2
    As a final note, nowadays there is no strong reason not to prefer the better and more recent H.265 format (or even AV1, if you are patient enough) over H.264. – Maëlan Mar 16 '20 at 18:18
  • could you also recommend the best free video format? ideally something made with agpl (even if ffmpeg still haven't done it themselves talk.ahoxus.org/notice/AHZ626fsOe14jNUcng#ffmpeg). – cregox Mar 19 '22 at 08:34
  • @cregox What's "best" depends on your application. Also, the video format (normally meaning "file format") is not what's most important. It's the type of video codec used. Different file formats can use the same codec, and visa-versa. I recommend you read: https://www.brid.tv/best-video-codecs-to-use-and-how-to-choose-the-right-one/ – John Pankowicz Mar 27 '22 at 13:43
115

Note that it seems that ffmpeg already performs some optimization when ran without options, so before trying to use settings you don't understand or deciding to explicitly lose information, give a try to a default conversion :

ffmpeg -i input.mp4 output.mp4

In my case it reduced the bitrate of both the video and audio (you can check and compare the input and output file by running ffprobe on them), transforming a 700 Mb video into a 60 Mb one of seemingly similar quality.

  • 4
    (from 10Mo to 1.2Mb, ffmpeg auto converted my video which was in VP8 to VP9) – sodimel Aug 16 '19 at 13:47
  • 3
    This increased the size of my video from 10.8MB to 14MB – pjtnt11 Sep 20 '19 at 23:16
  • 5
    By far the most simple answer so far. Used it on an 18s 4K video from Pixabay and the result was a 30% reduction in size without any noticeable reduction in quality. – vhs Nov 29 '19 at 08:01
  • 4
    @pjtnt11 Good to know it's not always efficient, thanks for reporting. – Skippy le Grand Gourou Nov 30 '19 at 10:55
  • 5
    While this might work, it is worth noting that it is not sustainable. In future, the FFMPEG defaults might change and produce different results. Perhaps, the defaults will make the changes better, but IMO it is risky since you are relying on the devs to optimise your file - are they creating smaller files, without worrying about quality, or are they trying to find a balance between the two? – HarryTheWulf Feb 19 '20 at 23:52
  • 6
    In my case, the command reduced video bitrate dramatically (without noticeable quality reduction) but increased audio bitrate a little (that is obviously not good). To keep original audio track run ffmpeg -i input.mp4 -acodec copy output.mp4 – Ilya Serbis Apr 18 '22 at 19:33
  • 3
    To @JustCarty's point, would be good to know what these defaults are. – Evan R Apr 05 '23 at 12:14
  • 3
    ffmpeg version 4.2.7-0ubuntu0.1 has defaults options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00 – elmazzun Jun 24 '23 at 16:33
  • 2
    @elmazzun Can you also provide the command you used to query the default options, please ? – Skippy le Grand Gourou Jun 26 '23 at 07:19
  • 2
    @SkippyleGrandGourou I just run the command ffmpeg -i input.mp4 output.mp4 as in the answer above but I did not get a miraculous size reduction as he got; in the end the best flags for me were ffmpeg -i input.mp4 -loglevel error -vcodec libx264 -crf 28 -preset faster -tune film output.mp4 – elmazzun Jun 26 '23 at 08:37
  • 2
    @elmazzun My question was rather which command did you use to get the options: output in your previous comment. – Skippy le Grand Gourou Jun 26 '23 at 08:40
  • 2
    I just looked in the output of the command ffmpeg -i input.mp4 output.mp4: don't use -loglevel error or you'll only get stderr from ffmpeg and you will not be able to see options:. – elmazzun Jun 26 '23 at 10:01
  • There are many optimizations that the MPEG 4 formats can do to a sequence of images. A filming of a scene is another name for need of speed: there's not much time to apply those optimizations. The mere recreation of an MPEG file maybe has the time to do that. – Hilton Fernandes Aug 24 '23 at 16:26
  • 1
    If I run this over and over the filesizes becomes smaller, I wish I could run a command to do this x factor – zyrup Dec 31 '23 at 01:03
69

Unless you're looking for a specific bit rate, I'd recommend the -crf option.

This is most commonly used for x264 encoding as described in this article.

In short: a constant rate factor (CRF) of 23 would make a DVD quality movie (~700MB - 1GB) and lower CRF values would be higher quality (larger files).

An example from the linked article:

ffmpeg -i input.mp4 -c:v libx264 -crf 28 output.mp4

Smaller sizes can be achieved by using libx265 instead of libx264 within the above command. However that causes issues with web browser compatibility and may take way longer.

Stephen Kitt
  • 495,615
23

I compressed a 40-minute HD video presentation from 505MB to 183MB
That's like going from 100MB → 36MB.
Original video was HD and output was almost zero noticeable difference.
It's a video file "I'd like to keep around, but HD is overkill."
Here's the command I used with reasons:

ffmpeg -n -loglevel error -i inputfile.mp4 -vcodec libx264 -crf 28 -preset faster -tune film outputfilename.mp4

  • -n : avoid overwriting output files (safer for testing then batching)
  • -loglevel error : show errors and hide the rows and rows of progress
  • -i inputfile.mp4 : input file name
  • -vcodec libx264 : swiped from the top answer above
  • -crf 28 : single-pass compression with minor noticeable difference ("0 = lossless, 23 = default, 51 = worst; subjectively sane range is 17–28") ref docs
  • -preset faster : looks 2x faster than default encoding time of 'medium' ref docs
  • -tune film : specify input is an HQ video (other options include 'cartoon', 'stillimage'..) ref docs
  • outputfilename.mp4 : output file name

For a directory of video files:

for i in *.{avi,flv,m4v,mov,wmv,mp4,MP4,TS,mkv}; do ffmpeg -n -loglevel error -i "$i" -vcodec libx264 -crf 28 -preset faster -tune film "cc${i}"; done

Issues:

  • a cleaner way of collecting "all video files" without having all the extensions in the command
  • a cleaner way to output the filename without "cc" prefix, AND being able to confirm video before deleting
  • .webm files don't work with the command. Had to swap "cc${i}" → "${i%.*}.mp4"

Handbrake is an open-source alternative with a UI

14

If you’re targeting a specific file size (or bitrate) rather than a specific quality, you'll need to use a two-pass average bitrate encoding (or “2-pass ABR” for short) to fit the video within this size without reducing the quality too drastically.

In short, for the same bitrate it achieves a better quality than a single-pass bitrate encoding (as demonstrated in this answer), because the first pass allows the codec to allocate bit usage based on needs, i.e. it can spare bits on easy frames and have more available to spend on more demanding frames. By contrast, in the single-pass mode, the codec cannot know ahead of time what is coming but still has to enforce a constrained bitrate.

See this blog post (or this archived one) for an enlightening explanation, and ffmpeg’s wiki about H.264 for a more synthetic how-to.

Typical command line:

ffmpeg -i <INPUT> -c:v libx264 -preset medium -b:v <BITRATE> -pass 1 -an -f <OUTPUT_CONTAINER> -y /dev/null && \
ffmpeg -i <INPUT> -c:v libx264 -preset medium -b:v <BITRATE> -pass 2 <AUDIO_OPTIONS> <OUTPUT>.<OUTPUT_CONTAINER>

You’ll have to pick a number of things.

  • Set a target bitrate, e.g. 1M (1 Mbit/s) or 500k (500 kbit/s). See this answer for how to compute a relevant bitrate.
  • Nowadays (2020), you’ll probably replace the libx264 video codec with the newer libx265 codec, which gives higher quality for the same size. See ffmpeg’s wiki for how to adapt the 2-pass options to that codec.
  • Set whatever options you want for audio encoding. For example, to re-encode with the Opus codec at 128 kbit/s, write -c:a libopus -b:a 128k (in 2020 I would recommend Opus over any other lossy audio codec, such as AAC). But, unless you know what you are doing, your best option may be not to re-encode audio at all, i.e. write -c:a copy. This way, you don’t even bother and your audio won’t lose in quality. Anyway, re-encoding audio often doesn’t save much (many audio streams in the wild are already bitrated at about 128kbit/s or less) and audio streams often have a negligible size when there is a video stream. See there for more guidelines on audio re-encoding.
  • Pick the container format of your liking. Most relevant ones (2020) are webm, mkv and mp4.
SaltySub2
  • 241
6

An option not mentioned yet is -r 30 to limit framerate to 30 fps, or whatever value you like.

4

I have a recipe I originally forged for myself in order to convert the Motion JPEG videos my old camera generates (they are very large videos, since each frame is an entire JPEG image) to h264. Here's an adaptation for other kinds of videos (courses, etc).

I'm not using ffmpeg, but mplayer and mencoder. First, We have to demux the audio with mplayer:

mplayer -vo null -ao pcm:fast:file=<audio_pcm.wav> <video>
  • The -vo null and -ao null parameters tells mplayer to not extract video.

In the next steps, we'll do a 3-pass compression with mencoder. At the first pass we'll choose a suitable Constant Quality Mode compression (crf parameter) as a start point:

mencoder <video> -ovc x264 \ 
         -x264encopts ratetol=100:preset=veryslow:crf=<value>:pass=1 \
         -nosound -o video1.h264
  • You can add slow_firstpass parameter to the -x264encopts if you are paranoid with the final quality of the video. Mencoder manual says that this option disable some parameters that “significantly improve encoding speed while having little or no impact on the quality of the final pass”. So, use it only at the last step.

  • You should try several values for crf — try starting from 25 and goes on increasing it until you note artifacts at the resulting video (higher values compresses more). Remember subsequent encoding passes will improve the quality you have choosed for crf.

  • Alternatives for the veryslow preset are slower, slow, medium etc. See mencoder manual for the complete list.

  • ratetol controls the bitrate variation — I'm not sure if I'm doing the right thing here, but I set it to the maximum value in order to let total freedom to mencoder to choose the right bitrate for each scene.

After the first pass, you'll note that the last line gives you the average bitrate you will use at the next steps:

(...)
x264 [info]: kb/s:526.43

Change the crf parameter, recommended at the first pass, to bitrate, required at the subsequent passes:

mencoder <video> -ovc x264 \
       -x264encopts slow_firstpass:ratetol=100:preset=veryslow:bitrate=526:pass=3 \
       -nosound -o video2.h264

This second pass encoding will read the statistics generated at the first pass (divx2pass.log and divx2pass.log.mbtree) in order to optimize the compression.

  • Note you'll use the same video input, not the generated by the first pass — first pass' output video is only useful to check the initial quality.

  • Note also that the pass=3 (not pass=2) will generate a new statistics file, so you can repeat the last step as many times you want. I usually do pass=3 twice, always paying attention to the result bitrate.

Meanwhile, you can compress the audio too, using lame or oggenc:

oggenc -q<n> <audio_pcm.wav>

Finally, we'll remux audio and video

mencoder -audiofile <audio>.ogg video2.h264 -oac copy -ovc copy \
         -of lavf -lavfopts format=mp4 -o <video>.mp4
  • The -of lavf -lavfopts format=mp4 generates mp4 file format using the lavopts muxers.
agc
  • 7,423
3

I wrote a bash script for reducing the size of the video and trying automatically different crf values.

Basically you will

  • choose a range of crf values
  • run the script
  • check the size of the generated videos and pick the one you want

This is really handy when you have a size limit you want to achieve and you don't know what is the crf value which will allow you to do it.

I hope this will help someone. I shared with my colleagues and everyone found it helpful.

#!/bin/bash

# bigger values of crf will lead to a bigger compression (smaller video size)
#for i in 1 2 3 4 5
for i in {25..28}
do
# you can remove the option -y if you want to be asked if to overwrite a file with the same name (leave the -y only if you understand what you are doing, otherwise you might rewrite an important file)
   ffmpeg -y -i NAMEOFTHEVIDEOTOCOMPRESS.mp4 -c:v libx264 -crf $i -preset veryfast -profile:v baseline -level 3.0 -strict -2 out_$i.mp4
   printf "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Done compression at crf=$i \n\n"
done
desmond13
  • 321
  • 1
  • 2
  • 6
3

Here is a 2 pass example: Pretty hard coded, but it really puts on the squeeze

#!/bin/bash

ffmpeg -y -i "$1"
-c:v libvpx-vp9
-pass 1
-deadline best
-crf 30 -b:v 664k -c:a libopus
-f webm /dev/null
&& ffmpeg
-i "$1"
-c:v libvpx-vp9
-pass 2
-crf 30 -b:v 664k -c:a libopus
-strict
-2 "$2"

Pablo A
  • 3,292
  • How is the data shared from pass 1 to pass 2? – user1133275 Jun 02 '21 at 20:39
  • Off the top of my head... i think it writes a tmp file derived from $1 – Max Robbertze Jun 04 '21 at 07:20
  • very curious to know: why libopus? disclaimer: i look for the best free video format... ideally something made with agpl (even if ffmpeg still haven't done it themselves talk.ahoxus.org/notice/AHZ626fsOe14jNUcng#ffmpeg). – cregox Mar 19 '22 at 08:36
3

Needed on macOS: to preserve compatibility encoding H.265/HEVC for QuickTime

ffmpeg -i i.mp4 -c:v libx265 -preset fast -crf 28 -tag:v hvc1 -c:a eac3 -b:a 224k o.mp4
Ax_
  • 171
  • 1
    If you are running this command on macOS, you can replace libx265 with hevc_videotoolbox for much faster and more efficient (hardware-accelerated) encoding. However, in my testing the resulting file size seems to be much larger than when using libx265. See this answer for more details: https://stackoverflow.com/questions/64924728/optimally-using-hevc-videotoolbox-and-ffmpeg-on-osx – jtbandes Mar 10 '24 at 04:47
3

I use the mpdecimate filter to reduce the size of my desktop captured videos because there are a lot of duplicated frames to ignore.

The command I use is:

ffmpeg -i original_file.mp4 -vf mpdecimate -vsync vfr -acodec copy mpdecimated.mp4
AdminBee
  • 23,716
  • 25
  • 56
  • 78
1

I want to point out on how you can use hardware acceleration to speed up the encoding process. I have took many parts of the upcoming command from the top answer. Without hardware acceleration, it takes a long time to make the small-sized file, so I used the command: ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi -i something.mp4 -c:a copy -c:v hevc_vaapi -crf 28 something_else.mp4. Users may need to change vaapi to one of these values: vdpau, dxva2, d3d11va, vaapi, qsv. The driver file for the graphics card may be in a different file too.

horsey_guy
  • 1,123
1

I generally use this on a mac. It has helped me convert a 4gb video under 40mb without hardly affecting the quality

ffmpeg -i ~/Desktop/demo.mov -c:v libx264 -crf 23 -c:a aac -strict experimental -b:a 128k ~/Desktop/output_video.mov 

Tip: I understand its hard to remember this command so I have saved this as a keyboard shortcut. Under Settings->Keyboard->text replacements

typing <ffmpeg in a textinput suggest me with this command

enter image description here

  • Must have been a completely uncompressed/raw video, and -crf 23 affects quality quite a bit in my experience. It's what I'd call medium to low quality, for high quality usually -crf 17 is recommended, but I've had to bring it down as low as 14 to be satisfied. However the preset also matters a lot, you're not defining a preset so you're using the default which if i recall correctly was one of the slower ones, this means higher quality at the cost of taking longer. – Cestarian Jun 13 '25 at 07:32
1

RTFM

There is semi-official documentation for exactly what we are trying to do: https://trac.ffmpeg.org/wiki#Encoding

Other's have linked to parts of the FFmpeg wiki but not this particular portion which is what you actually need.

There is also the official documentation, but it's less beginner-friendly.

From there, we're in business. The codec matters a lot, so we have to select the right one for us, the right one always depends on the person and their use case.

H265/HEVC and AV1 are the 'best' codecs, they have the best quality to size ratios. If your only goal is to make the file smaller they are usually the way to go, but support for them is not that great. I believe AV1 provides better compression (but worse overall quality) however it takes orders of magnitude more time to encode with, at least when encoding with the CPU.

H264 and VP9 are the second-best ones, they are the most commonly supported. If you want to upload your videos, if you want them to be playable in apps like discord or signal, you will likely want to use one of these two. The files will be bigger relative to their quality, but you'll be able to play them more or less everywhere.

No other codec than these 4 are really worth considering for our purposes at the time of writing.

Audio codecs also to a lesser degree matter for file size (the longer the file the more it starts to matter), setting the audio codec to libopus is a great option if you want to play with this, it's also necessary if you wish to encode to WebM.

You need to pick a codec based on your needs and follow the encoding guide for it.

Tip: H264 to an .mp4 container is the best configuration for maximum compatibility with everything, it's the closest you'll get to a file that can be played anywhere.

Obtain codec information

You can use

ffmpeg -v 0 -encoders

To list all possible audio and video codec options/all codec variants.

There is a way for ffmpeg to tell you the major options available for any codec, it's this command:

ffmpeg -v 0 -h encoder=codec 

where you replace codec with the codec you want to see options for for instance ffmpeg -v 0 -h encoder=av1 will list most options for all av1 encoders.

If you run:

ffmpeg -v 0 -h encoder='av1' | head -1

It will give you this output:

Encoder libaom-av1 [libaom AV1]:

Which tells us the default software encoder used when you use the -c:v av1 argument is libaom-av1.

Now knowing what encoder is actually being used for software encoding, we can do this:

ffmpeg -v 0 -h encoder='libaom-av1' 

And this will tell us all available options for the default av1 encoder.

If we want the options for some specific other variant, like say hevc_nvenc, we just type in the name of that encoder instead:

ffmpeg -v 0 -h encoder='hevc_nvenc' 

Example Commands

Here are a few generic commands that should work well for most people.

I have excluded AV1 examples because it just takes way too long to process on a CPU and I didn't have the patience for it. I left out hardware encoding VP9 and AV1 because neither of my GPUs support those, so I can't test it.

VP9 is normally supposed to encode in 2 passes but I only do one pass using -crf in conjunction with bv:0 to make it turn out better when done that way. This means there is room for improvement there if you want to use that codec.

All of these example commands are aiming for very high quality, there should be as little perceivable quality loss with these presets as reasonably possible. This means the files will be bigger, to make them smaller you need to adjust the quality value, which is always either -crf or -qp in those commands. A bigger number makes the file smaller at the cost of quality.

Example file 1: 82.1MB, H264/AAC 720p@60fps animation 1.5 minutes.

Example file 2: 96.6MB, H264/AAC 1080p@30fps recording 1.5 minutes.

# Cpu Encoding:

High Quality Hevc

Example file 1: 26.7MB in 39 seconds.

Example file 2: 47.1MB in 35 seconds.

ffmpeg -threads $(awk '/cores/{print $4;exit}' /proc/cpuinfo) -i INPUT_FILE -movflags +faststart -c:a libopus -c:v hevc -crf 17 -preset:v fast OUTPUT_FILE

High Quality Hevc 2D Animation (according to all docs, hevc doesn't support this, but as you can see from my test, it does.)

Example file 1: 21.3MB in 36 seconds.

ffmpeg -threads $(awk '/cores/{print $4;exit}' /proc/cpuinfo) -i INPUT_FILE -movflags +faststart -c:a libopus -c:v hevc -crf 17 -preset:v fast -tune:v animation OUTPUT_FILE

High Quality 264

Example file 1: 31.9MB in 14 seconds.

Example file 2: 88.9MB in 25s

ffmpeg -threads $(awk '/cores/{print $4;exit}' /proc/cpuinfo) -i INPUT_FILE -movflags +faststart -c:a copy -c:v hevc -crf 17 -preset:v fast OUTPUT_FILE

High Quality 264 2D Animation

Example file 1: 31MB in 17 seconds.

ffmpeg -threads $(awk '/cores/{print $4;exit}' /proc/cpuinfo) -i INPUT_FILE -movflags +faststart -c:a copy -c:v h264 -crf 17 -preset:v fast -tune:v animation OUTPUT_FILE

High Quality VP9

Example file 1 (-cpu-used 4): 42.7MB in 2 minutes 12 seconds.

Example file 1 (-cpu-used 1): 39.4MB in 3 minutes 42 seconds.

Example file 2 (-cpu-used 4): 58.7MB in 2 minutes 44 seconds.

Example file 2 (-cpu-used 1): 53.2MB in 5 minutes 21 seconds.

ffmpeg -i INPUT_FILE -cpu-used 1 -movflags +faststart -c:a libopus -c:s webvtt -c:v vp9 -crf 17 -preset 5 -aq-mode 2 -b:v 0 OUTPUT_FILE.webm

Hardware Encoding:

High Quality HEVC Nvenc

Example file 1: 32.3MB in 19s

Example file 2: 62.5MB in 21s

ffmpeg -i INPUT_FILE -movflags +faststart -c:a libopus -c:v hevc_nvenc -spatial-aq 1 -multipass fullres -tune:v hq -rc:v constqp -qp 19 -preset:v p7 OUTPUT_FILE

High Quality H264 Nvenc

Example file 1: 39.4MB in 20s

Example file 2: 91.6MB in 19s

ffmpeg -i INPUT_FILE -movflags +faststart -c:a libopus -c:v hevc_nvenc -spatial-aq 1 -multipass fullres -tune:v hq -rc:v constqp -qp 19 -preset:v p7 OUTPUT_FILE

High Quality HEVC VAAPI

Example file 1: 41MB in 9s

Example file 2: 59.6 in 8s

ffmpeg -i INPUT_FILE -movflags +faststart -vf format=nv12,hwupload -pix_fmt vaapi -c:a libopus -c:v hevc_vaapi -vaapi_device /dev/dri/renderD128 -rc_mode CQP -qp 20 OUTPUT_FILE

High Quality H264 VAAPI

Example file 1: 39.4MB in 15s

Example file 2: 53.7 in 14s

ffmpeg -i INPUT_FILE -movflags +faststart -vf format=nv12,hwupload -pix_fmt vaapi -c:a libopus -c:v h264_vaapi -vaapi_device /dev/dri/renderD128 -rc_mode CQP -qp 20 OUTPUT_FILE

Setting the audio codec to libopus saves me about 300-500KB on filesize, but it is also lowering the bitrate and quality of the audio.

My example files are already very poorly optimized, so for you, you might not see as much reduction when running these commands, in fact it might get bigger instead worst case scenario (this would mean your file is already quite optimized).

For -crf ideally stay within a range of 18-28 with 18-20 still being very high quality and 21-23 still very decent.

For vaapi I used one of the worst available methods (CQP), it's better to use QVBR or ICQ, which you can use instead of CQP by replacing -rc_mode CQP -qp 20 with -rc_mode ICQ -qp 20 where 20 is the quality setting (lower number = higher quality). The reason I didn't include those methods in the example is that QVBR is a lot more complicated than the other methods in use, and ICQ isn't supported by my drivers.

If you're confused on how to use vaapi, here is a short guide to get vaapi working if you have problems with it.

Option Cheatsheet

It's time to learn how to fish. Every option matters. Every option here is something that could potentially reduce filesize for you.

  • -c:v Allows you to select the video codec, run ffmpeg -v 0 -encoders | grep "^ V" to find possible options.
  • -c:a Allows you to select the audio codec, run ffmpeg -v 0 -encoders | grep "^ A" to find possible options. Common options include aac, libopus, ac3, libvorbis, flac and libmp3lame. You can also set it to copy to copy the audio directly from the source (no re-encoding it).
  • -qp Is a setting that directly controls frame quality, and it sticks to that setting from start to finish (every frame will have the same quality). It's only recommended to use this option for codecs that do not support -crf. If -crf is unavailable you can sometimes use this in conjunction with -rc:v (Rate Control) setting to get decent results, ICQ and QVBR tend to give the best results if available, with them -qp may even be better than -cfr.
  • -crf does the same thing as -qp except that it adjusts the frame quality based on the frame content, this leads to better compression at no perceivable loss of quality. If you want very high quality results, you set it to 17-18, if you want good quality you set it to 21-23 if you don't care all that much about quality you can go up to 28. This is usually recommended over -qp if it is available (it's not always available)
  • -preset is an option that controls how meticulously the quality to size ratio will be handled (this does not mean slower presets always produce smaller files, they can be bigger, but their quality will be higher relative to size). So you will have a higher quality and better compression ratios with the slower presets, whereas the faster presets will encode faster. for CPU H264 and HEVC the options are 1-7 or: veryslow,slower,slow,medium,fast,faster,veryfast. Some codecs and some variants either do not support this option or have different available options.
  • -tune is an option for H264,HEVC (and av1 but less useful for that) that can dramatically reduce filesize at no perceivable quality loss when correctly configured, however each codec supports a different set of options for this setting. You can read the ffmpeg docs for encoding with each codec on what these options are, or you can use x264 --fullhelp or x265 --fullhelp, Some tune options also have additional settings specifically to tweak them further.
  • -b:v Allows you to manually define the video bitrate, it's an older method compared to qp and crf and usually inferior too. For VP9 this setting controls average bitrate instead, which is slightly better. To use -crf this way with VP9 -b:v 0 must be set, otherwise it works a little different.
  • -b:a Allows you to manually define the audio bitrate, it can be useful to reduce filesize, a value of 128k is a good compromise
  • -r Sets the framerate (fps), if you have a 60fps video and you use -r 30 it will shave down the video size by half, the standard framerate for film and tv is 24, this is the lowest I can recommend setting this unless you're dealing with animation, animation is usually done at far lower framerates, such as anime for instance tends to be done at 8-12fps, for animation you can experiment with such lower values.
  • -vf/-lavfi/-filter_complex are 3 separate commands for roughly the same thing, it's to allow you to build a filter graph, this is a tool for pre and post-processing. The format is -vf "filter,filter2=stuff,filter3" although the quotation marks are not typically necessary (only if there are spaces or weird symbols in an argument do you need them).
  • -vf scale=N:N allows you to rescale the video, for example -vf scale=-1:720 rescales video to 720p. You can also scale a video down based on it's size, it was explained in another answer.
  • -vf mpdecimate removes duplicate frames, it can in some cases lead to a dramatic filesize reduction with no perceivable visual quality loss, for instance if I use this while converting a clip to a GIF with ffmpeg, it produces a smoother GIF that is significantly smaller than otherwise, an actual quality upgrade. In most cases though it will lead to a mild perceivable drop in quality. For more advanced application you can read a guide on mpdecimate.
  • -max_frame_size sets the maximum size per frame in bytes, this is a rather niche setting I don't believe very many people use it but it can be used to set a hard limit for the filesize (e.g. you count the frames in the file, divide the desired maximum size in bytes between those frames, and then set the result as -max_frame_size) I haven't personally tested this option yet, there might be untapped potential here.
  • -aq-mode 2 improves quality and compression rate at the cost of time for AV1 and VP9.
  • -spatial-aq 1 improves quality by adjusting -qp based on spatial characteristics, it also tends to mildly improve the overall compression for AV1 and HEVC_NVENC and H264_NVENC.
  • -multipass fullres increases quality at the cost of time for AV1 and HEVC_NVENC and H264_NVENC.

Some other important options:

  • -threads controls the number of threads used for CPU encoding with H264 and HEVC. I recommend setting this value to the number of physical cores you have or less. awk '/cores/{print $4;exit}' /proc/cpuinfo tells you the number of physical cores you have. Increasing the number of threads increases the speed, but once you pass a certain number of threads the speed starts decreasing instead due to the overhead of managing the extra threads. The number of physical cores is usually the cutoff point.
  • -cpu-used is what VP9 and AV1 use instead of -threads, however increasing the number reduces quality and only supports a maximum value of 8, provides diminishing returns beyond 4 and reduces compression too. Setting this to 8 was slower than 4, even on a machine with 16 physical cores.
  • -profile:v is a setting for H264 and HEVC that controls the profile used by the codec, by default this is automatically configured but you can use it if you want to encode in a specific profile like high10 or baseline. Setting specific profiles can affect filesize but it's somewhat situational.
  • -pfmt is a universal setting for the pixel format, it functions somewhat similar to profile. And is usually best left unconfigured or set to + for auto. Like with profiles it's situational.

Helpful options (these don't affect filesize):

  • -movflags +faststart this places the moov atom at the start of the file instead of the end which is great for compatibility, especially for web-based video players.
  • -c:s subtitle codec. .webm only supports webvtt, .mp4 only supports mov_text, this is a necessary setting to carry softsubs over to those formats.
  • -vf "subtitles=INPUT_FILE:force_style=Fontsize=24" convert softsubs to hardsubs, this way you can make the subtitles show up in your .mp4 files when uploading them online.
Cestarian
  • 2,760
-1

Edit: Sorry first time on this platform, seems like i've posted this in the Unix/Linux subdomain, this post can be deleted or left here since the math still applies to the solution the same anywhere.

I've been working on a Powershell script that automatically reduces filesize of a given video to a specific size entered in MB.

It works by reencoding the given video to a calculated size using ffprobe and ffmpeg and a lot of finetuning to get it just right and save on encoding time if possible and wanted by the user or it'll leave the encoder at VP9 which is the default to increase quality instead.

The Link to it here which includes a lot of other nice to have stuff: github.com/xMorioh/Script-Collection

but the current code posted here to provide it in this thread for preservation:

param (

#-----Dependencies-----

This Script requires ffmpeg as well as ffprobe, both can be found here: https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip

[string]$ffmpegPath = "$PSScriptRoot\ffmpeg.exe", [string]$ffprobePath = "$PSScriptRoot\ffprobe.exe",

#-----User Settings----- [string]$inputVideo = "A:\input.*", #No need to specify the file extension [string]$outputPath = "A:\output.mp4", [int]$targetVideoSize_megabytes = 50, [string]$encoder = "libvpx-vp9" #Choose your prefered Encoder Library, for example x264=libx264, VP9=libvpx-vp9, AV1=libsvtav1 | libaom-av1

)

#-----Script internals----- $inputVideo = Get-ChildItem -Path $inputVideo #search for file extension via input name

$probedFileDuration = Invoke-Expression "&'$ffprobePath' -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 '$inputVideo'"

#Try to get Video Stream bitrate, catch if containers like MKV are in use which do not support stream metadata embedding #To mitigate streamless container size overflow of the final size, we subtract 10% here which worked best so far. try { [float]$probedFileVideoBitrate = Invoke-Expression "&'$ffprobePath' -v error -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" } catch { [float]$probedFileVideoBitrate = Invoke-Expression "&'$ffprobePath' -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" $targetVideoSize_megabytes = ($targetVideoSize_megabytes * 0.9) } [float]$probedFileVideoBitrate_kbit = $probedFileVideoBitrate * 0.001

#Try to get Audio Stream bitrate, catch if containers like MKV are in use which do not support stream metadata embedding #If Stream could not be found then Audio is embedded in the Video Stream or not present at all try { [float]$probedFileAudioBitrate = Invoke-Expression "&'$ffprobePath' -v error -select_streams a -show_entries stream=bit_rate -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" } catch { [float]$probedFileAudioBitrate = 0 } [float]$AudioSize_megabytes = (($probedFileAudioBitrate * $probedFileDuration) / 8388608)

[float]$targetFileSize_kbit = (($targetVideoSize_megabytes - $AudioSize_megabytes - 1) * 8192) [float]$OutputBitrate_kbit = $targetFileSize_kbit / $probedFileDuration

Write-Host "-----DEBUGGING START-----" Write-Host if ($probedFileAudioBitrate -eq 0) { Write-Host "Audio is embedded in the Video Bitrate due to a format being used that does not support stream based metadata embedding" } Write-Host Write-Host "probedFileDuration :" $probedFileDuration Write-Host "probedFileVideoBitrate :" $probedFileVideoBitrate Write-Host "probedFileVideoBitrate_kbit :" $probedFileVideoBitrate_kbit Write-Host "probedFileAudioBitrate :" $probedFileAudioBitrate Write-Host "AudioSize_megabytes :" $AudioSize_megabytes Write-Host "targetFileSize_kbit :" $targetFileSize_kbit Write-Host "OutputBitrate_kbit before Optimization:" $OutputBitrate_kbit

#-----Optimizations----- #Limit kbit to the original videos kbit if it's lower than our calculation since wizards do not actually exist and the gain would be too small if any. if ($OutputBitrate_kbit -gt $probedFileVideoBitrate_kbit) { $OutputBitrate_kbit = $probedFileVideoBitrate_kbit }

#--Check if we can use libx264 instead to save on encoding time if the exact same quality can be achieved in our targetVideoSize, we do not change $OutputBitrate_kbit here we still use target max kbit. [int]$probedFileVideoWidth = Invoke-Expression "&'$ffprobePath' -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" [int]$probedFileVideoHeight = Invoke-Expression "&'$ffprobePath' -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" [int]$probedFileVideoPixels = ($probedFileVideoWidth * $probedFileVideoHeight) [string]$probedFileVideoFPS = Invoke-Expression "&'$ffprobePath' -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1 '$inputVideo'" $probedFileVideoFPSMultiplier = [Data.DataTable]::New().Compute($probedFileVideoFPS, $null) / 30 #Info: 0.00125 = ((((Baseline kbit (500kbit) / 1280*720 assuming the 500Kkbit are good for a 30fps video at that resolution) * 2) + 10%) to get a base quality number meaning kbit for each pixel per second @30fps #*2 + 10% = x2 is for the x264 codec uplift since VP9 has around 50% better quality compared and then just 10% on top for good measure + rounding up, $probedFileVideoFPS right below here is for 30fps to 60fps when 500kbit look good at 30fps. $x264Check_kbit = (0.00125 * $probedFileVideoFPSMultiplier) * $probedFileVideoPixels $x264Check_megabytes = (((($x264Check_kbit * $probedFileDuration) / 8192) + 1) + $AudioSize_megabytes) if ($x264Check_megabytes -lt $targetVideoSize_megabytes) { $encoder = "libx264" } #--Check end

#Limit kbit for user set encoder, this way we can get away with a much lower filesize in some scenarios elseif ($OutputBitrate_kbit -gt 10000) { $OutputBitrate_kbit = 10000 } #-----------------------

Write-Host "OutputBitrate_kbit after Optimization :" $OutputBitrate_kbit Write-Host "-----DEBUGGING END-----"

Invoke-Expression " $ffmpegPath -i '$inputVideo' -c:v $encoder -b:v $($OutputBitrate_kbit)k -pass 1 -an -row-mt 1 -f null NUL $ffmpegPath -i '$inputVideo' -c:v $encoder -b:v $($OutputBitrate_kbit)k -pass 2 -c:a copy -row-mt 1 '$outputPath' "

xMorioh
  • 1
  • 3