Optimum parameters for FFMPEG to keep file size

0

so the idea is: I want to convert every.single.video I have in a 1 terabyte drive to a format that my Chromecast can play (https://developers.google.com/cast/docs/media) and I'll happily leaving my not so new PC working for a few days (weeks?) to do it. Great, FFMPEG to the salvation.

a little background: I'm an Android/Java developer (very active in the StackOverflow) and I know at some point I'll just need to code some batch script, or quick java command line (because I'm more comfortable with Java) to do the job, but I'm still not sure on which arguments to use in my FFMPEG command line.

So my needs are: - video codec: H.264 High Profile Level 5 - audio codec: any of HE-AAC, LC-AAC, MP3 (honestly to god I don't know the difference, maybe some AAC will be better?) keep same - I certainly want to use the slow or slower (for better quality) - I certainly want to use the 2-pass with variable bitrate (for better quality)

all the above I saw on the tutorials and manuals how to achieve, now the part that I'm really not sure how to accomplish:

I want the final file size to be approximately the same as the original. That's because the drive is roughly 80% full and I don't want to fill it up by simply converting stuff. On the script I'll re-encode the file and delete the original for things will keep moving without issues.

so how do I get from filesize to video_bitrate and audio_bitrate to put into the script?

ps.: I'm equally happy on using handbrake if anyone knows how to accomplish the same job.

thanks in advance for the help.

Budius

Posted 2014-03-03T23:51:15.373

Reputation: 147

Answers

2

The ffmpeg command you need is something like:

ffmpeg -i input.foo -c:v libx264 -profile:v high -level 5 -preset slow -b:v $videobitrate -an -pass 1 output.bar;
ffmpeg -y -i input.foo -c:v libx264 -profile:v high -level 5 -preset slow -b:v $videobitrate -b:a $audiobitrate -pass 2 output.bar

libx264 is the h264 encoder, and all of the options get passed from ffmpeg to it, they're pretty self explanatorah. The first pass doesn't need audio, hence the -an. You can pipe the output to /dev/null if you want, but whateverrr. I just use the -y switch in the second pass so it overwrites the temp file without asking. The audio codec by default is aac so you don't have to specify it.

ffprobe can help you to get the value of $videobitrate and $audiobitrate (I'm assuming a posix environment, otherwise it's going to be %videobitrate% and %audiobitrate%). You're going to have to do some sed, awk or perl voodoo to get the values into a form that you can use. Here's the output of ffprobe on a random mp4 on my machine:

ffprobe version 2.1.3 Copyright (c) 2007-2013 the FFmpeg developers
  built on Feb 12 2014 22:10:38 with Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.1.3 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-nonfree --enable-hardcoded-tables --enable-avresample --enable-vda --cc=clang --host-cflags= --host-ldflags= --enable-libx264 --enable-libfaac --enable-libmp3lame --enable-libxvid --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-libvpx --enable-librtmp --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libvo-aacenc --enable-libass --enable-ffplay --enable-libspeex --enable-libschroedinger --enable-libfdk-aac --enable-openssl --enable-libopus --enable-frei0r --enable-libcaca --enable-libopenjpeg --extra-cflags='-I/usr/local/Cellar/openjpeg/1.5.1/include/openjpeg-1.5 '
  libavutil      52. 48.101 / 52. 48.101
  libavcodec     55. 39.101 / 55. 39.101
  libavformat    55. 19.104 / 55. 19.104
  libavdevice    55.  5.100 / 55.  5.100
  libavfilter     3. 90.100 /  3. 90.100
  libavresample   1.  1.  0 /  1.  1.  0
  libswscale      2.  5.101 /  2.  5.101
  libswresample   0. 17.104 /  0. 17.104
  libpostproc    52.  3.100 / 52.  3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'loop.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf55.19.104
  Duration: 00:00:19.56, start: 0.000000, bitrate: 201 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 400x226 [SAR 226:225 DAR 16:9], 129 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 75 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

annoyingly ffprobe dumps everything to stderr, so you need to redirect it to stdout. I won't tell you how to suck eggs, your sed-fu might be better than mine, but for Those Who Came in Late, to get the bitrate for the whole shebang in the example above you could to do something like:

videobitrate=$(ffprobe input.foo 2>&1|grep bitrate |sed "s/.*bitrate: \([0-9]*\) \([km]*\).*/\1\2/")

and for the audio:

audiobitrate=$(ffprobe input.foo 2>&1|grep Audio|sed "s/.* \([0-9]*\) \([km]*\)b\/s.*/\1\2/")

not sure why the audio's bitrate was specified but not the video's, could be because I usually compress with a constant rate factor (constant quality rather than constant bitrate). You'll have to check the ffprobe output on your movies. From there it's up to you turning that into the variables you need. Note that ffmpeg will parse numbers like 100k or 3m.

stib

Posted 2014-03-03T23:51:15.373

Reputation: 3 320

I didn't even read and process the whole reply, but I already upvote it just for the time and effort to elaborate a complete answer. I'll be digging on this little project this or next weekend and we'll see how it goes. Thanks a lot! Sincerely! – Budius – 2014-03-08T17:33:36.320

I have a few follow up questions: 1) so it's just copy the original bitrate and stuff should have the same size at the end? I honestly expected something a bit more complex, happy to know it isn't. 2) If it is that simple, isn't there any option on the FFMPEG that says "same_bitrate"? 3) Isn't the 1st-pass bitrate supposed to be a higher than the final pass? Thanks again. – Budius – 2014-03-08T17:59:39.040

Well size = bitrate x length, so yes, they'll have the same file size. However, bitrate isn't really the main concern, it's quality that counts. The complicating factor is that quality is dependent on the efficiency of the compression, as well as the nature of the content. If the originals are encoded with a less efficient codec - for example if they're standard definition MPEG2 video (like you'd get on a DVD) encoded at around 5MB/s, the space needed for similar quality copies encoded with h264 will be much smaller; 1Mb/s might do it. – stib – 2014-03-09T11:25:56.580

... There's not any easy way to judge this without testing it, but there is a better approach, which is to encode at constant quality rather than focussing on bitrate. The ffmpeg libx264 constant quality setting is invoked with -crf (stands for Constant Rate Factor, whatever that means). An integer value determines the quality, starting at 1 for near lossless, and scaling up to around 31 for crap. To encode at a quality similar to DVD you'd use -crf 20. – stib – 2014-03-09T11:27:14.383

Oh, and re: the two pass thing. Two passes basically means that it goes through the footage to determine where the parts that need the most bitrate are, before actually doing the encoding. It can then divide up the available bitrate accordingly. So if you've got a static shot with not much action it will use less b/s than the chase scene. You'll notice it will produce a log file, this is what is used to determine the bitrate. – stib – 2014-03-09T11:30:09.990

hi. Thanks for all the tips. Yeah, at the end I just ditch any batch thing and will just do a simple Java UI (I'll probably put on GitHub at some point, I'm sure more ppl will be interested). Regarding the probe I found this out http://stackoverflow.com/questions/7708373/get-ffmpeg-information-in-friendly-way to grab it as a nice easy to use JSON object. That even give me the chance to proper check the current codec to only re-encode if needed.

– Budius – 2014-03-10T16:38:34.247