Pages

Thursday, September 8, 2011

Creating mp4 for Android with ffmpeg

I am traveling to work by public transportation and I thought it would be a cool idea to have some movies with me during the train ride. Of course I have to encode them first. Here is a way to do this with ffmpeg:

$ ffmpeg -i input.avi \
    -s 480x320 -aspect 3:2 -vcodec mpeg4 -vb 400000 -r 13 \
    -acodec aac -strict experimental -ac 1 -ar 64000 -ab 64000 \
    output.mp4

That's all. If you want to know what the options are, continue reading:

-i input.avi: the input file
-s 480x320: this will resize the video to 480 px x 320 px in size
-aspect 3:2: keep the aspect ratio, so the images of the movie won't be stretched
-vcodec mpeg4: the video codec, mp4
-vb 400000: video bitrate (in bits/s), in this case 400kb
-r 13: the framerate, 13 frames/s is enough
-acodec aac: the audio codec, aac
-strict experimental: aac is experimental, without strict ffmpeg will not encode
-ac 1: audio channel, 1 for mono, 2 for stereo
-ar 64000: audio sample rate
-ab 64000: audio bit rate
output.mp4: output file

When you want to copy more movies to your Android device then you can play with the bitrates to decrease the video size:

$ ffmpeg -i input.avi \
    -s 480x320 -aspect 3:2 -vcodec mpeg4 -vb 200000 -r 13 \
    -acodec aac -strict experimental -ac 1 -ar 16000 -ab 16000 \
    output.mp4

In the example above I reduce the vb, ar, and ab switches to create a low quality output file which will consume less space. Enclosed a small script so you don't have to remember the complete commandline:

# vi /usr/local/bin/mov2mp4.sh
#!/bin/bash

function Usage {
  echo "Usage: mov2mp4.sh -i <input> [-q <quality>] [-o <output>]"
  echo "        Quality: l=low, n=normal (default), h=high"
  exit
}

# PARSE ARGUMENTS
while getopts ':i:o:q:h' option; do
  case $option in
  i)
    input="$OPTARG"
    ;;
  o)
    output=$OPTARG
    ;;
  q)
    quality=$OPTARG
    ;;
  h)
    Usage
    exit
    ;;
  esac
done

if [[ $input == "" ]]; then
  Usage
fi

if [[ $output == "" ]]; then
  output=`echo "$input" | awk 'BEGIN {FS="/"} {print $NF}' | awk 'BEGIN {FS="."} {print $1"-android.mp4"}'`
fi

if [[ $quality == "l" ]]; then
  vb="200000"
  ar="32000"
  ab="32000"
fi

if [[ $quality == "n" ]] || [[ $quality == "" ]]; then
  vb="300000"
  ar="48000"
  ab="64000"
fi

if [[ $quality == "h" ]]; then
  vb="400000"
  ar="64000"
  ab="128000"
fi

ffmpeg -i "$input" -s 480x320 -aspect 3:2 -vcodec mpeg4 -vb $vb -r 13 -acodec aac -strict experimental -ac 1 -ar $ar -ab $ab -y "$output" > /tmp/mov2mp4.log 2>&1

echo "Finished, see /tmp/mov2mp4.log for details"

Make it executable:

# chmod 755 /usr/local/bin/mov2mp4.sh

And run it:

# mov2mp4 -i movie.avi -q h -o movie.mp4

The options a very easy:
-i: Input file
-q: Quality - l=low, n=normal (default), h=high
-o: Output file

The higher the quality the bigger the file will get.