Back to blog

MKMP4: An automated H264 / HE-AAC (aacPlus) encoder script for Mac and Linux / Unix

September 26, 2008 - Posted in Computers & Tech , Video Encoding Posted by:

Tags: , , , ,

I have been nothing but amazed about the feedback that i got on my prior tutorials on how to automate the whole chain of free and open source tools to create amazing quality video at really low bitrates (great for flash streaming, etcetera).

I usually write such tools and scripts for myself or for clients, but due to the fact that it seems a lot of people is interested in the process, I’m releasing here the new version of my encoding tools for the Mac and any Linux / Unix variant.

Needed tools and applications

For all this to work, you will need the following things

Extra pre-requisites for Mac OS X

Mac OS X users need to install the Apple Developer Tools (aka XCode) in order to compile software, also, for building the x264 encoder, you will need to get and install YASM.

Now, you have to build and install all the tools, AND they have to be on any directory in your executable path. My scripts assume that all the tools are in your executable path.

Building x264

Building x264 is usually a matter of just typing:

./configure

in the source directory, and then the usual:

make

and

make install

If you happen to have any problem with the building process, usually a simple google search with the error string is all you need.

Building mplayer

The process of building mplayer is certainly similar, BUT if you plan to use the video thumbnail and poster frame generation on my scripts, you will have to get and compile the jpeg libraries (if you don’t have them already) BEFORE building mplayer, this should be also a really easy thing to do: go get the libjpeg sources from HERE, and then configure, build and install.

Now, you have to download the latest SVN version of mplayer, assuming that you have the subversion client enabled, you can get the whole source tree by typing:

svn checkout svn://svn.mplayerhq.hu/mplayer/trunk mplayer

Now, you need to enable some things at configure time for mplayer and mencoder to work nice with the scripts, so our configure line will look something like this

./configure --disable-gui --enable-png --enable-jpeg

Note: there should be no reason at all for the –enable-jpeg flag, but for some reason that I ignore, mplayer wont detect my libjpeg install if i don’t force it like that, also, the –disable-gui option is to save some compile time, since we don’t plan to use any of the GUI features on mplayer.

Now, its time to build, so type:

make

and then, if you get no errors, you are safe to type:

make install

At this point you should have a working install, but since we want to be 100% sure that you REALLY have a working install, in any terminal type this:

mencoder -ovc help

and you should get something like this (please notice the x264 part, that is fundamental for everything to work)

Available codecs:

   copy     - frame copy, without re-encoding. Doesn't work with filters.
   frameno  - special audio-only file for 3-pass encoding, see DOCS.
   raw      - uncompressed video. Use fourcc option to set format explicitly.
   lavc     - libavcodec codecs - best quality!
   vfw      - VfW DLLs, read DOCS/HTML/en/encoding-guide.html.
   qtvideo  - QuickTime DLLs, currently only SVQ1/3 are supported.
   x264     - H.264 encoding

now let’s verify the mplayer part, again, in any terminal type:

mplayer -vo help

and you should get something like this (here, notice the PNG and JPEG bits, this is fundamental if you plan to use the thumbnail and poster generation features).

Available video output drivers:
	macosx	Mac OSX Core Video
	quartz	Mac OSX (Quartz)
	xv	X11/Xv
	x11	X11 ( XImage/Shm )
	xover	General X11 driver for overlay capable video output drivers
	gl	X11 (OpenGL)
	gl2	X11 (OpenGL) - multiple textures version
	null	Null video output
	mpegpes	MPEG-PES file
	yuv4mpeg	yuv4mpeg output for mjpegtools
	png	PNG file
	jpeg	JPEG file
	tga	Targa output
	pnm	PPM/PGM/PGMYUV file
	md5sum	md5sum of each frame

Very Important!

After you compile and install mplayer, you need to copy the midentify.sh script into any directory inside your executable path, for example /usr/local/bin. This is mandatory, because the MKMP4 script will NOT be able to correctly identify the source video files without this tool. The midentify script (called midentify.sh) is located inside the TOOLS directory in the mplayer source tree.

At this point, almost everything is ready, now it is time for the next step.

Building and configuring the GPAC utilities

If you are on Linux / Unix, building the GPAC utilities is a really simple process.

Get the latest stable release from here, and then do the usual ./configure, make and make install.

For Mac OS users there is an extra step that you have to do if you want to be able to build and install the GPAC utilities without headaches, and it involves the editing of a source file in the package.

So, after you have downloaded the sources, open the file src/utils/os_net.c , line 78 reads:

#ifndef __DARWIN__

right AFTER it, add the following:

typedef unsigned long u_long;

save the file, and you can now configure, build and install the GPAC utilities (MP4Box) on Mac OS without any problem.

Installing the HE-AAC (aacPlus) audio encoder.

This is a pretty simple step, if you are on a Mac, get this encoder that i have built myself from the 3GPP sources and copy it to /usr/local/bin, and if you are on linux, get the Nero Digital Audio Encoder from HERE and again, copy it to /usr/local/bin. You can actually move the encoders to any directory that IS inside your executable path, /usr/local/bin is a good choice though ;).

Putting it all togheter.

At this point, you have pretty much everything you would need to encode amazing quality H264 video and aacPlus audio, the only one remaining thing is some tool to put all this togheter in an *easy to use* way, so i have created the following bash script which pretty much simplifies the whole process.

#! /bin/bash

# This bash script has been created by Diego Massanti
# www.massanti.com
#
# set -x # uncomment this for debugging
shopt -s nullglob # so the glob expands to nothing when there are no .mov files.

# User settings:

fileext=".mov" # Set this variable to the video file extension that you want to search for when encoding whole directories.

# End user settings
# You should not change anything below this line unless you know what you are doing.

usage()
{
cat << EOF
usage: $0 -f  [-w ] -b  [-q][-k][-t][-o ]

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* h264 video with he-aac audio encoding script by Diego Massanti. *
*                September 2008, Made in Argentina.               *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

OPTIONS:
   	-h	Show this message
-f	Path to the file to encode < REQUIRED
   	-w	Resize video to fit inside this width while keeping the aspect ratio < OPTIONAL
   	-b	Desired video bitrate < REQUIRED
-o	Output directory for compressed media < OPTIONAL (will use same as source if ommited)
   	-q 	Better quality encoding using 2 passes (slower) < OPTIONAL
-k	Do not delete (keep) temporary files < OPTIONAL
EOF
}
audiobitrate=48000
platform=""
uname=`uname`
if [ $uname == "Darwin" ]; then
## 99% of chances that this is a Mac
platform="Mac"
else
platform="Linux"
fi

width=""
bitrate=
deltemp=TRUE
quality=FALSE
poster=FALSE
filename=""
rsize=""
outdir=`pwd`
outFileName=""
itemVideoBitrate=""

while getopts ":f:w:o:b:qktv" OPTION; do
case $OPTION in
w ) width=$OPTARG;;
f ) filename=$OPTARG;;
b ) bitrate=$OPTARG;;
k ) deltemp=FALSE;;
q ) quality=TRUE;;
t ) poster=TRUE;;
o ) outdir=`cd "$OPTARG"; pwd`;;
h ) usage;;
\? ) usage
exit 1;;
* ) echo $usage
exit 1;;
esac
done

if [ "$1" == "" ]; then
usage
exit 0
fi

if [ "$filename" == "" ]; then
echo
echo you MUST supply a file to encode!, use the -f parameter. i.e: -f mymovie.avi
echo
usage
exit 1
fi
if [ "$bitrate" == "" ]; then
echo
echo you MUST specify a target bitrate!, use the -b parameter. i.e: -b 512
echo
usage
exit 1
fi

if [ "$width" != "" ]; then
rsize="-vf scale=$width:-3"
rsizemsg="fit into $width pixels wide"
else
rsize=""
rsizemsg="Movie is not being resized."
fi

# Check if output directory exists
if [ -d "$outdir" ]; then
echo "Target directory set to: $outdir"

else
echo "Error: '$outdir' does not exist!!"
echo "Can not continue, please make sure that the specified output directory exists. Exiting..."
exit 1
fi

# Start the magic...

showMessage() {
echo "MKMP4 : "$1""
}

initialize() {
if [ -d "$filename" ]; then
$filename = `cd $filename; pwd`
showMessage "Output directory set to: $outdir."
showMessage "Triggering the encoder with all media files in directory: $filename."
for file in "$filename"*$fileext; do
if [ -f "$file" ]; then
showMessage "Will encode file: $file"
startEncoders "$file"

fi
done
elif [ -f "$filename" ]; then
showMessage "Triggering the encoder with source media: $filename."
startEncoders "$filename"

else
# input is no file and no directory ? WTF ?
echo "Something really weird has happened here: $filename is not valid, exiting..."
exit 1
fi
}

startEncoders() {
firstPass "$1"
if [ "$quality" == "TRUE" ]; then
secondPass "$1"
fi
extractAudio "$1"
encodeAAC "$1"
mux "$1"
if [ $poster == "TRUE" ]; then
createThumbnail "$1"
fi
if [ $deltemp == "TRUE" ]; then
cleanTemporaryFiles "$1"
fi
}
firstPass() {
local iFile="${1##*/}"	# remove directory and keep only file name.
# First, lets get some basical information about this file, as FPS.
MOVIE_FPS=`midentify "$1" | grep FPS | cut -d = -f 2`
# Lets print some info to stdout.
showMessage "Encoding: $1"
showMessage "Resizing to: $rsizemsg."
showMessage "Total Bitrate: $bitrate kbps."
let "caudiobitrate = $audiobitrate / 1000"
let "itemVideoBitrate = $bitrate - $caudiobitrate"
showMessage "Video Bitrate: $itemVideoBitrate kbps."
showMessage "Audio Bitrate: $caudiobitrate kbps."
showMessage "Platform: $platform."
# Lets start the encoding for the first pass.
showMessage "Starting video encoding pass 1..."
mencoder "$1" -o "$outdir/${iFile%%.*}_temp.264" -passlogfile "$outdir/${iFile%%.*}"_temp.log $rsize -ovc x264 -x264encopts bitrate=$itemVideoBitrate:frameref=8:bframes=3:b_adapt:b_pyramid:weight_b:partitions=all:8x8dct:me=umh:subq=6:trellis=2:brdo:threads=auto:pass=1:analyse=all -of rawvideo -nosound

}

secondPass() {
local iFile="${1##*/}"	# remove directory and keep only file name.
showMessage "Starting video encoding pass 2..."
mencoder "$1" -o "$outdir/${iFile%%.*}_temp.264" -passlogfile "$outdir/${iFile%%.*}"_temp.log $rsize -ovc x264 -x264encopts bitrate=$itemVideoBitrate:frameref=8:bframes=3:b_adapt:b_pyramid:weight_b:partitions=all:8x8dct:me=umh:subq=6:trellis=2:brdo:threads=auto:pass=2:analyse=all -of rawvideo -nosound
}

extractAudio() {
local iFile="${1##*/}"	# remove directory and keep only file name.
showMessage "Extracting Audio..."
mplayer "$1" -af resample=48000:0:2,channels=2,volnorm=1:0.25,format=s16le -ao pcm:file="$outdir/${iFile%.*}_temp.wav" -vc dummy -vo null
}

encodeAAC() {
local iFile="${1##*/}"	# remove directory and keep only file name.
if [ "$platform" == "Mac" ]; then
enhAacPlusEnc "$outdir/${iFile%.*}_temp.wav" "$outdir/${iFile%.*}_temp.aac" $audiobitrate s
else
neroAacEnc -br 48000 -he -if $outdir/${iFile%.*}_temp.wav -of $outdir/${iFile%.*}_temp.mp4
fi
}

mux() {
local iFile="${1##*/}"	# remove directory and keep only file name.
MP4Box -add "$outdir/${iFile%.*}_temp.264#video:fps=$MOVIE_FPS" "$outdir/${iFile%.*}.m4v"
if [ "$platform" == "Mac" ]; then
MP4Box -add "$outdir/${iFile%.*}_temp.aac" -sbr "$outdir/${iFile%.*}.m4v"
else
MP4Box -add "$outdir/${iFile%.*}_temp.mp4#audio" "$outdir/${iFile%.*}.m4v"
fi
name=$outdir/${iFile%.*}
album="The Mac Video Archive"
author="Apple Computer // massanti.com"
comment="Professionally encoded by Diego Massanti"
created="2007"
MP4Box -inter 500 -itags album="$album":artist="$author":comment="$comment":created="$created":name="$name" -lang English "$outdir/${iFile%.*}".m4v
}

createThumbnail() {
local iFile="${1##*/}"	# remove directory and keep only file name.
mplayer "$1" -ss 10 -nosound $rsize -ssf lgb=1.0 -sws 7 -vo jpeg:outdir=$outdir -frames 1
mv "$outdir/00000001.jpg" "$outdir/${iFile%.*}.jpg"
mplayer "$1" -ss 10 -nosound -vf scale=150:-3 -ssf lgb=1.0 -sws 7 -vo jpeg:outdir=$outdir -frames 1
mv "$outdir/00000001.jpg" "$outdir/${iFile%.*}_small.jpg"
}

cleanTemporaryFiles() {
local iFile="${1##*/}"	# remove directory and keep only file name.
showMessage "Removing temporary files..."
rm "$outdir/${iFile%.*}"_temp*
}

initialize

You can also download the script by clicking here in order to keep proper code formatting. Installation is really simple, just move it to /usr/local/bin, give it execution rights (chmod +x /usr/local/bin/mkmp4) and done.

This script is really easy to use and implement, basically the script expects the following MANDATORY parameters in the command line:

  • -f to specify the source media (can be a single file or a whole directory with media files)
  • -b to specify the target bitrate for the final h264 encoded movie
  • -w to specify the target width for the final h264 encoded movie

So for example, typing:

mkmp4 -f /path/to/some/video.avi -b 512 -w 640

will create a MP4 video file with a bitrate of 512kbps and a frame width of 640 pixels (please note that mencoder will correctly take care of the aspect ratio for the final movie and the resize will just look OK.)

Optional parameters for the MKMP4 script:

  • -q turns on 2 pass encoding: This will increase the final quality (and the encoding time), but it worths the extra wait if you care about quality.
  • -k to keep temporary files: The script will create several temporary files during the encoding process, if for whatever reason you want to keep that files, you should use this option.
  • -o to specify a target directory: By default, the mkmp4 script will place the output video in the working directory (the directory from where you called the script), if you want to change that behavior, just use this option.
  • -t to turn ON thumbnail and poster frame generation: If you use this flag, mkmp4 will create a thumbnail and a poster frame that you can then use if you plan to publish your videos on the web.

The following command line will create a 512kbps video, will set the target width to 640 pixels AND will use 2 pass encoding, will not delete temporary files, will create thumbnails and poster frames, and will place the files on the /target/directory/ directory.

mkmp4 -f /path/to/some/video.avi -b 512 -w 640 -k -q -t -o /target/directory/

Some things you need to know and FAQ’s about this script:

  • By default, when the -f parameter is a directory, the script will search for any .mov file inside it, encode it, and place the final mp4 file into the directory set in the -o parameter (or the current directory if you have ommited that parameter), if you want to change that behavior, just change the value of the fileext variable inside the script to whatever file extension you want to process.
  • Why are you reducing the audio level by 5db ? This is because with some old audio formats, for some reason that I really ignore, mplayer will distort the audio output. Now, if you take a closer look, you will see that right after the volume filter, im applying a normalization filter to get the audio back to 0db, so the output audio level will always be correct no matter what happens.
  • Why are you forcing the audio output to be always stereo ? what if i want to encode mono material ?: Just modify the audio encoder trigger.
  • The output media produced by this script is automatically interleaved and is fully compatible (and has been tested) with Flash Media Server 3 and Wowza Media Server Pro.

There are a lot of things that could be improved around here, and so far i have not tested this in any platform other than Mac OS Leopard, but so far, this should work in any Linux / Unix variant too without too much work.

If you are interested to get an idea of how the video results will look, you can take a look a The Apple Video Archive here on this site.

As usual, comments / feedback and suggestions are welcome.

73 Comments

Diego Massanti 5 years ago

Hey great, i was actually working on a totally new script, but you’re free to send me back your mods for this one and i will be happy to publish it here with proper credit.
Thanks!

Nicolas 5 years ago

Hi diego, thx for the great mod :)
However I am having troubles compiling gpac on centos 5.3

here is the message :

compositor/events.c:239: warning: passing argument 3 of ‘gf_utf8_wcstombs’ from incompatible pointer type

and finally at the end ..

collect2: ld returned 1 exit status
make[1]: *** [libgpac.so] Error 1
make[1]: Leaving directory `/root/gpac/src’
make: *** [lib] Error 2
[root@lnx-svr gpac]#

I am lost at what could be the problem on this one :S

Thomas 5 years ago

Hi Diego, thanks again, I am running a UGC Site with your script in bckground now for months :)

recently I started adding hd capabilities, everything was downsized to 512 px width – now I generate a secon version wit 1280 px widht max.

I have one strange problem, the only difference between the small and the big version are different width/height, as I set the bitrate for the big version to 4000 I get multiple audio & video streams in one file – any hint on this?

Mediainfo output:
http://pastebin.com/m68940276

Thanks :)

Thomas 5 years ago

ok, figured it out.

was using different debian versions locally and on the server which led to various problems with mplayer, MP4Box and the Nero-Encoder…

Thomas 5 years ago

Just a hint to others, I always had problems with ugly / grey / unsharp thumbnails. Solution: i just replaced Diegos screenshot-command by the corresponding ffmpeg command and now the thumbnails look fine – always.

Diego Massanti 5 years ago

@Thomas, care to write your command here so others can benefit from the fix too ? Cheers.
Diego.

Thomas 5 years ago

i call this command with php passthru, because I generate it in a different part of the process.

$mplayer_command =’ffmpeg -y -i ‘ . $filename . ‘ -f image2 -ss ‘. $duration .’ -s 512x’.$screenshot_height.’ -vframes 1 -an ‘ . $directory . ‘00000001.jpg ‘;

I guess you get the idea. The quality is really much better compared to mplayer…

Diego Massanti 5 years ago

Thank you :)
I will try this and see if it is better. Im actually redoing mkmp4 from scratch, so maybe i will use your approach instead, i honestly wasn’t happy at all with the quality of thumbs.

princeton corporate solutions 2 years ago

Hello there, simply became alert to your weblog thru Google, and located that it is truly informative. I’m going to be careful for brussels. I’ll be grateful for those who continue this in future. A lot of folks shall be benefited out of your writing. Cheers!

Betty Elmer 1 year ago

I agree with the author that we need to share the knowledge we gain!

Brianna 1 year ago

My brother recommended I might like this website.
He was once entirely right. This submit truly made my day.

You can not imagine simply how a lot time I had spent for this info!

Thank you!

my site – Brianna

Lifelong Learning Program - Újra Tanulok Program 1 year ago

My brother recommended I might like this blog. He was totally right. This submit truly made my day. You cann’t imagine just how so much time I had spent for this information! Thanks!

Top XXX Videos 1 year ago

Undeniably believe that which you stated. Your favorite justification appeared to be at the net the easiest thing to take note of. I say to you, I definitely get irked at the same time as folks consider worries that they plainly don’t realize about. You controlled to hit the nail upon the top as smartly as outlined out the entire thing with no need side-effects , other people can take a signal. Will likely be again to get more. Thank you

ebay 10 months ago

Somebody necessarily assist to make severely posts I’d state. That is the first time I frequented your web page and to this point? I surprised with the analysis you made to create this actual put up extraordinary. Fantastic activity!

how to play piano without knowing how to play piano 6 months ago

I like the valuable info you provide to your articles.
I will bookmark your weblog and test once more here regularly.
I am quite certain I will be informed lots of new stuff
proper right here! Best of luck for the following!

Dusty 4 months ago

This excellent website truly has all of the information I wanted about this subject and didn’t know who
to ask.

foxbuzz 2 months ago

Nice weblog here! Additionally your website rather a lot up fast!

What host are you using? Can I am getting your affiliate link for your
host? I wish my site loaded up as fast as yours
lol

Feel free to visit my website … foxbuzz

wynajem kserokopiarek gliwice 1 month ago

Nadzwyczaj fajowy post, ciekawe wpisy polecam wszystkim lekturę

Here is my blog post; wynajem kserokopiarek gliwice

rowery Giant 2 weeks ago

Strona świadczy o nietypowych wydarzeniach, zachęcam do dyskusji

my blog post :: rowery Giant

idm keygen 1 week ago

Hello my family member! I want to say that
this post is awesome, great written and include approximately all significant infos.
I’d like to see more posts like this .

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>