2

I'm trying to convert a WebM file to MP4, using the following parameters:

ffmpeg -loglevel info -i 2017-05-01-122851.webm -c:v libx264 -preset slower -crf 20 -bf 2 -trellis 2 -cmp 2 -subcmp 2 -g 45 -c:a aac -strict experimental -ab 32k ../renamed-video/2017-05-01-122851.mp4

ffmpeg version 3.0.7-0ubuntu0.16.10.1 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 6.2.0 (Ubuntu 6.2.0-5ubuntu12) 20161005
  configuration: --prefix=/usr --extra-version=0ubuntu0.16.10.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librubberband --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-chromaprint --enable-libx264
  libavutil      55. 17.103 / 55. 17.103
  libavcodec     57. 24.102 / 57. 24.102
  libavformat    57. 25.100 / 57. 25.100
  libavdevice    57.  0.101 / 57.  0.101
  libavfilter     6. 31.100 /  6. 31.100
  libavresample   3.  0.  0 /  3.  0.  0
  libswscale      4.  0.100 /  4.  0.100
  libswresample   2.  0.101 /  2.  0.101
  libpostproc    54.  0.100 / 54.  0.100
Input #0, matroska,webm, from '2017-05-01-122851.webm':
  Metadata:
    encoder         : GStreamer matroskamux version 1.8.3
    creation_time   : 2017-05-01 10:28:51
  Duration: 00:04:32.88, start: 0.000000, bitrate: 3160 kb/s
    Stream #0:0(eng): Video: vp8, yuv420p, 1280x720, SAR 1:1 DAR 16:9, 250 tbr, 1k tbn, 1k tbc (default)
    Metadata:
      title           : Video
    Stream #0:1(eng): Audio: vorbis, 44100 Hz, mono, fltp (default)
    Metadata:
      title           : Audio
File '../renamed-video/2017-05-01-122851.mp4' already exists. Overwrite ? [y/N] y
[libx264 @ 0x55d5e57dde80] using SAR=1/1
[libx264 @ 0x55d5e57dde80] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2
[libx264 @ 0x55d5e57dde80] profile High, level 5.1
[libx264 @ 0x55d5e57dde80] 264 - core 148 r2699 a5e06b9 - H.264/MPEG-4 AVC codec - Copyleft 2003-2016 - http://www.videolan.org/x264.html - options: cabac=1 ref=8 deblock=1:0:0 analyse=0x3:0x133 me=umh subme=9 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=0 trellis=2 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=2 b_pyramid=2 b_adapt=2 b_bias=0 direct=3 weightb=1 open_gop=0 weightp=2 keyint=45 keyint_min=4 scenecut=40 intra_refresh=0 rc_lookahead=45 rc=crf mbtree=1 crf=20.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to '../renamed-video/2017-05-01-122851.mp4':
  Metadata:
    encoder         : Lavf57.25.100
    Stream #0:0(eng): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 250 fps, 16k tbn, 250 tbc (default)
    Metadata:
      title           : Video
      encoder         : Lavc57.24.102 libx264
    Side data:
      unknown side data type 10 (24 bytes)
    Stream #0:1(eng): Audio: aac (LC) ([64][0][0][0] / 0x0040), 44100 Hz, mono, fltp, 32 kb/s (default)
    Metadata:
      title           : Audio
      encoder         : Lavc57.24.102 aac
Stream mapping:
  Stream #0:0 -> #0:0 (vp8 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (vorbis (native) -> aac (native))
Press [q] to stop, [?] for help
frame=  768 fps= 32 q=-1.0 Lsize=    2573kB time=00:00:03.20 bitrate=6576.7kbits/s dup=676 drop=0 speed=0.136x    

The problem is that the output file is much bigger than the source one. How can I tune the size so that the two files have the same size/definition? Moreover, I see this warning, and I don't know if I have to be scared of it:

unknown side data type 10 (24 bytes).

6
  • 2
    if you want to achieve the same size, then you should use two passes and ABR -c:v libx264 -preset slower -b:v <YOUR_BITRATE>k -pass <PASS_#>. CRF (Constant Rate Factor) is used for achieving a certain visual quality with no way (other than experience or trial&error) to know the average bitrate before.
    – flolilo
    Jul 2, 2017 at 17:42
  • 1
    @liv913 -loglevel info is the default, so it doesn't do anything. What's the reason for adding -bf 2 -trellis 2 -cmp 2 -subcmp 2? These options are covered by the preset.
    – llogan
    Jul 2, 2017 at 17:48
  • @flolilolilo You should post that (with a little more explanation) as an answer.
    – slhck
    Jul 2, 2017 at 17:50
  • @slhck done - I hope I haven't forgotten anything. @liv913 does it state anything beyond unknown side data type 10 (24 bytes)? like [libx264 0x123456] unknown side data type 10 (24 bytes) (this is of course is a made up example).
    – flolilo
    Jul 2, 2017 at 18:57
  • Thanks guys. @LordNeckbeard: how can I see the list of options covered by preset slower? Using help command ffmpeg -hide_banner -f lavfi -i nullsrc -c:v libx264 -preset help -f mp4 - I can only see the list of presets, not what they're supposed to do.
    – liv913
    Jan 8, 2018 at 19:29

2 Answers 2

3

With -crf, you can't really achieve the same size. CRF means "Constant Rate Factor" - it is a unit-less (meaning no %, no kb/s,...) value between 0 (lossless) and 51 (worst quality) (default-value for x264: 23). The purpose of CRF is to achieve a certain visual quality without having to define bit-rates. FFmpeg's H.264-Guide states:

[...] a subjectively sane range is 18-28. Consider 18 to be visually lossless or nearly so: it should look the same [...] as the input but it isn't technically lossless. The range is exponential, so increasing the CRF value +6 is roughly half the bitrate while -6 is roughly twice the bitrate.

(Note that different encoders might have different ranges of CRF-values: for example, x265's default CRF-value is 28, which should be visually equal to x264's 23.)

You can of course fiddle around with -crf until you get the desired file-size - but this usually means a lot of trial & error.


Now one might ask "but if the quality is the same and x264 is supposed to be a good encoder, then why will CRF not make the file smaller?"

The answer to that question is easy: x264 (like virtually every other encoder on the market) has no way to detect how efficient the input-file was encoded. It might know that it has an ABR (Average Bit Rate) of 2 Mb/s, but this does not tell it anything about the quality. And if your input-file was encoded poorly (low bitrate and/or very fast encoder-settings), then it might have artifacts (blocks,...). You can see them, but x264 can't (really) do so[1] - so by stating -crf, it assumes that you want to conserve all the artifacts in your new file to the extent your value (0-51) is able to, which then leads to higher bit rates because artifacts (like noise) are not easy to predict.

[1] Imagine it like seeing a brick wall - while you can recognize it as such because of the red colour of the sqaure-shaped bricks and the grey mortar between them, all the encoder sees are red and grey pixels.


So the way to achieve a certain file-size is via ABR, which is the -b:v-parameter in FFmpeg's libx264. ABR works with variable bit rates (VBR), but it tries to achieve an average bit rate as specified over the whole file. To make that principle work well, one should use two passes so FFmpeg can at first look at the file and calculate the bit rates and then encode it in a second step.

If your goal is to have a resulting file with the same size as the input-file, you can calculate your bit rate this way:

(<FILESIZE_INPUT-FILE> [MiB] * 8192) / <DURATION_INPUT-FILE> [seconds] = ~XYZ [kBit/s total bitrate]
XYZ [kBit/s total bitrate] - <DESIRED_AUDIO_BITRATE> [kBit/s] = ___ [kBit/s video bitrate]

Annotations in [brackets], values for you to fill in in <ANGLE_BRACKETS>, and your result as ___ row of underscores.

With the resulting syntax:

ffmpeg -y -i <INPUT-FILE-PATH> -c:v libx264 -b:v ___k -preset slower <OTHER_COMMANDS_LIKE_AUDIO> -pass 1 -f <OUTPUT-FILE-FORMAT> /dev/null && \
ffmpeg -y -i <INPUT-FILE-PATH> -c:v libx264 -b:v ___k -preset slower <OTHER_COMMANDS_LIKE_AUDIO> -pass 2 <OUTPUT-FILE-PATH>

In x264, CRF and ABR are mutually exclusive - you can either use one or the other, so your choice with x264 will always be guaranteed quality vs. guaranteed file size. As far as I know, other encoders (like x265) can use a combination of ABR and CRF, so you can specify a bit rate and a quality-range that the encoder tries to achieve. But you can always have only one highest priority: either visual quality or target file size (or encoding speed). With a lot of experience, one can achieve "the perfect balance" for any given job, but it still would be a compromise.


A last comment about parameters in ffmpeg:

If you don't need parameters (and there are plenty of occasions where one would need them), don't specify them. -loglevel info is the default, so why would you specify it? The best it can do is to break your code if you have a typo in it (same goes for -trellis). Also, libx264 does a good job at specifying GOPs, so -g might not be necessary unless you need it for a specific playback-engine/... (same goes for -cmp and -csubcmp).

The encoders' developers usually try to make the usage of their products easy, and therefore, default values most of the time are much better than blindly typing in random parameters that someone on the internet found 10 years ago in the first documentation available.

Though of course, if you have done your research and you have the experience (or you simply want to try it out), there's no way I would want to stop you from going to the character-limit of the command console with ffmpeg!

-1

simplest solution, and fastest, requires no re-encoding or guessing about final filesize: just copy both audio and video to matroska container, ie

ffmpeg -i 2017-05-01-122851.webm -vcodec copy -acodec copy 2017-05-01-122851.mkv

that way only filesize diff can be container overhead, which is usually marginal.

2
  • 1
    Please edit your answer to include a bit of explanation as to how this answers the question. Sep 10, 2019 at 22:30
  • another example of superuser mods overmoderating...how can you mod something youre unfamiliar with? it's the simplest solution, and fastest, requres no re-encoding or guessing about final filesize as this thread veered off into... Sep 12, 2019 at 9:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .