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.
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