H.264 video encoding for iPhone/iPod Touch. Ffmpeg Linux, Mac OS X, Dos
Posted by: mhanney in Apple TV, Fun, Gadgets, Mac OS X, Open Source, Uncategorized, Unix, tags: bash, Fun, H.264, Linux, Mac OS X, Open SourceAh, what better way to spend a lazy Sunday afternoon than compiling ffmpeg and the half dozen dependencies required to encode H.264 for the iPhone / iPod Touch. (I am supposed to be painting and decorating. Waiting for video to encode is only slightly more interesting than watching paint dry…)
This is not about how to compile open source mpeg encoder ffmpeg. There are loads of good guides how to do that, and it’s mostly ./configure, make, make install anyway. If you’re not comfortable with compiling apps from source then the scripts below might not be your cup of tea either. There are loads of good apps for less than $50 to do the same thing, but if you know what ffmeg is, and are familiar with bash or DOS batch files, you might find these scripts useful. Please leave a comment if you see an error, or improvement, or just want to add something.
Here are the settings I use for encoding H.264 videos that are compatible with the iPhone, iPod Touch and Apple TV. The movies need to be high enough resolution to look good on TV, but low enough data rate to play-back on the iPhone/iPod Touch. Another important requirement is that the file and can easily be dragged from iTunes (on Mac or Windows) to any of those devices. (iTunes is really fussy about that). My source videos are not DVD rips (I believe Handbrake is a popular and free open source application for encoding DVD rips to H.264. Handbrake also uses ffmpeg and works on Mac OS X, Linux and Windows). My movies are PVR recordings and home movies I want to watch on the hand-held device. (In a few years DVB-T might be transmitted already encoded as H.264 and this transcoding not be required).
The script will convert a directory full of movies, in any format your OS is capable of playing, to H.264/AAC mp4. I’ve made versions for bash and DOS which work pretty much in the same way on Linux, Mac OS X and Windows. Pre-compiled versions of ffmpeg for Windows with all the required libraries are easily available online. Compiling your own version is recommended so the binary will make best use of your CPU capabilities, like MMX2, SSE2Fast, SSSE3, Cache64, etc.
These setting are high quality 2 pass encoding. Using a 2.33Ghz Intel Core 2 Duo Macbook Pro with 3 GB RAM I get 100 fps on 1st pass, 50 fps on second. That’s 3/4 real-time encoding, or 22.5 mins for a 30 min movie. Interestingly my old dual 2.8Ghz P4 Xeon server with 4GB ram encodes at the same speed as the Core 2 duo laptop. I was surprised by that, I was hoping the dual P4 Xeons would be faster. (Anyone want to buy a dual P4 Xeon Dell workstation? It’s got 4 Gig of RAM, U320 ultra-wide SCSI, Linux and ffmpeg already installed!)
transcode2ipod.sh
#!/bin/sh
# Mac OS X, path will be different on Linux
FFMPEG_EXE=/Volumes/HD/opt/local/bin/ffmpeg
WORKING_DIR=/Volumes/Media/Movies
QUEUE_DIR=${WORKING_DIR}/transcode-queue
FILE_EXTENSION=avi
DONE_DIR=${WORKING_DIR}/transcode-done
OUT_DIR=${WORKING_DIR}/iPod
#BBC TV widescreen to iPod 640x352, cropping 12 pixels all round
#to remove extraneous black outside the TV safe area
WIDTH_BEFORE_CROP=664
HEIGHT_BEFORE_CROP=376
CROP_TOP=12
CROP_BOTTOM=12
CROP_LEFT=12
CROP_RIGHT=12
WIDTH_OUT=640
HEIGHT_OUT=352
#TESTING="-ss 00:01:55 -t 00:01:01"
TESTING=""
VIDEO_BIT_RATE=896k
MAX_VIDEO_BIT_RATE=1024k
VIDEO_BIT_RATE_TOLERANCE=128k
AUDIO_BIT_RATE=128k
AUDIO_FREQ=48000
GOPS=250
KEY_INT_MIN=25
# default
MOTION_ESTIMATION_METHOD=epzs
#slightly faster
#MOTION_ESTIMATION_METHOD=umh
#slightly faster again
#MOTION_ESTIMATION_METHOD=hex
for FULL_FILENAME in ${QUEUE_DIR}/*.${FILE_EXTENSION}
do
#gets filename without dir path
FILENAME=${FULL_FILENAME##*/}
#gets filename without extension, depends on first path manipulation ##*/
echo "Transcoding ${FILENAME%%.*}.${FILE_EXTENSION}"
${FFMPEG_EXE} \
-i ${QUEUE_DIR}/${FILENAME%%.*}.${FILE_EXTENSION} \
-y ${TESTING} \
-s ${WIDTH_BEFORE_CROP}x${HEIGHT_BEFORE_CROP} \
-vcodec libx264 \
-b ${VIDEO_BIT_RATE} \
-bt ${VIDEO_BIT_RATE_TOLERANCE} \
-threads 2 \
-croptop ${CROP_TOP} \
-cropbottom ${CROP_BOTTOM} \
-cropleft ${CROP_LEFT} \
-cropright ${CROP_RIGHT} \
-f mp4 \
-flags +loop \
-cmp +chroma \
-partitions 0 \
-subq 1 \
-trellis 0 \
-refs 1 \
-coder 0 \
-me_range 16 \
-me ${MOTION_ESTIMATION_METHOD} \
-g ${GOPS} \
-keyint_min ${KEY_INT_MIN} \
-sc_threshold 40 \
-i_qfactor 0.71 \
-maxrate ${MAX_VIDEO_BIT_RATE} \
-bufsize 10M \
-rc_eq 'blurCplx^(1-qComp)' \
-qcomp 0.6 \
-qmin 10 \
-qmax 51 \
-qdiff 4 \
-level 30 \
-aspect ${WIDTH_OUT}:${HEIGHT_OUT} \
-acodec libfaac \
-an \
-pass 1 /dev/null
${FFMPEG_EXE} \
-i "${QUEUE_DIR}/${FILENAME%%.*}.${FILE_EXTENSION}" \
-y ${TESTING} \
-s ${WIDTH_BEFORE_CROP}x${HEIGHT_BEFORE_CROP} \
-vcodec libx264 \
-b ${VIDEO_BIT_RATE} \
-bt ${VIDEO_BIT_RATE_TOLERANCE} \
-threads 4 \
-croptop ${CROP_TOP} \
-cropbottom ${CROP_BOTTOM} \
-cropleft ${CROP_LEFT} \
-cropright ${CROP_RIGHT} \
-f mp4 \
-flags +loop \
-cmp +chroma \
-partitions +parti4x4+partp8x8+partb8x8 \
-subq 5 \
-trellis 1 \
-refs 1 \
-coder 0 \
-me_range 16 \
-me ${MOTION_ESTIMATION_METHOD} \
-g ${GOPS} \
-keyint_min ${KEY_INT_MIN} \
-sc_threshold 40 \
-i_qfactor 0.71 \
-maxrate ${MAX_VIDEO_BIT_RATE} \
-bufsize 10M \
-rc_eq 'blurCplx^(1-qComp)' \
-qcomp 0.6 \
-qmin 10 \
-qmax 51 \
-qdiff 4 \
-level 30 \
-aspect ${WIDTH_OUT}:${HEIGHT_OUT} \
-acodec libfaac \
-ab ${AUDIO_BIT_RATE} \
-ar ${AUDIO_FREQ} \
-ac 2 \
-pass 2 ${OUT_DIR}/${FILENAME%%.*}.mp4
ERROR=$?
if [ $ERROR -ne 0 ]; then
echo "Transcoding failed for $ with error $ERROR"
exit 3
fi
rm ${WORKING_DIR}/*.log
rm ${WORKING_DIR}/*.temp
mv ${QUEUE_DIR}/${FILENAME%%.*}.${FILE_EXTENSION} ${DONE_DIR}
done;
transcode2ipod.dos.bat
@echo off
set FFMPEG_EXE="C:\Program Files\ffmpeg\bin\ffmpeg.exe"
set BLAT_EXE="C:\Program Files\Blat\Blat.exe"
set UNIX_UTILS=D:\UnixUtils\usr\local\wbin
SET WORKING_DIR=D:\Movies
set QUEUE_DIR=%WORKING_DIR%\xvid-queue
rem set QUEUE_DIR=%WORKING_DIR%\Home
set FILE_EXTENSION=avi
set DONE_DIR=%WORKING_DIR%\xvid-done
set OUT_DIR=%WORKING_DIR%\iPod
set TEMP_DIR=%WORKING_DIR%\tmp
set TEMP_FILENAME="%TEMP_DIR%\filename.txt"
set LOG_FILE="%TEMP_DIR%\ffmpeg.log"
rem email settings
rem whether or not to send an email after each transcoding
set EMAIL=1
set SMTP_HOST=your.mailhost.com
set TO=email@address.com
set FROM=sender@address.com
set MAILBODY_FILE="%TEMP_DIR%\mail.txt"
rem ffmpeg settings for iPod touch / iPhone
rem
rem small PAL
rem DIMENSIONS=480x256
rem pal
rem DIMENSIONS=480x352
rem rem max
rem DIMENSIONS=720x480
rem
rem rem home movies from Sony Camcorder do 356x376 with crop 12 top bottom left right. Output size is Width -cropleft -cropright, & Height -croptop -cropbottom 536 -12 -12 = 512, 376 -12 -12 = 352
rem set WIDTH=536
rem set HEIGHT=376
rem BBC TV widescreen to iPod 640x352, cropping 12 pixels all round to remove extraneous black outside the TV safe area
set WIDTH=664
set HEIGHT=376
set CROP_TOP=12
set CROP_BOTTOM=12
set CROP_LEFT=12
set CROP_RIGHT=12
rem set TEST=-ss 00:03:00 -t 00:00:10
set TEST=
set VIDEO_BIT_RATE=896k
set MAX_VIDEO_BIT_RATE=1024k
set VIDEO_BIT_RATE_TOLERANCE=128k
set AUDIO_BIT_RATE=128k
set AUDIO_FREQ=48000
set GOPS=250
set KEY_INT_MIN=25
rem default
set MOTION_ESTIMATION_METHOD=epzs
rem slightly faster
rem MOTION_ESTIMATION_METHOD=umh
rem slightly faster again
rem MOTION_ESTIMATION_METHOD=hex
rem create tmp dir if not exists
IF NOT EXIST %TEMP_DIR% MKDIR %TEMP_DIR%
rem create out dir if not exists
IF NOT EXIST %OUT_DIR% MKDIR %OUT_DIR%
rem FOR %%f in ("%QUEUE_DIR%\*.%FILE_EXTENSION%") DO (
SETLOCAL ENABLEDELAYEDEXPANSION
set FILES=
for /f %%a IN ('DIR /b "%QUEUE_DIR%\*.%FILE_EXTENSION%"') do (
set FILES=!FILES! %%~na
echo "Transcoding %%~na.%FILE_EXTENSION%"
%FFMPEG_EXE%
-i "%QUEUE_DIR%\%%~na.%FILE_EXTENSION%"
-y %TEST%
-s %WIDTH%x%HEIGHT%
-vcodec libx264
-b %VIDEO_BIT_RATE%
-bt %VIDEO_BIT_RATE_TOLERANCE%
-threads 4
-croptop %CROP_TOP%
-cropbottom %CROP_BOTTOM%
-cropleft %CROP_LEFT%
-cropright %CROP_RIGHT%
-f mp4
-flags +loop
-cmp +chroma
-partitions 0
-subq 1
-trellis 0
-refs 1
-coder 0
-me_range 16
-me %MOTION_ESTIMATION_METHOD%
-g %GOPS%
-keyint_min %KEY_INT_MIN%
-sc_threshold 40
-i_qfactor 0.71
-maxrate %MAX_VIDEO_BIT_RATE%
-bufsize 10M
-rc_eq "blurCplx^(1-qComp)"
-qcomp 0.6
-qmin 10
-qmax 51
-qdiff 4
-level 30
-aspect %WIDTH%:%HEIGHT%
-acodec libfaac
-an
-pass 1 "%TEMP_DIR%\%%~na.pass1"
%FFMPEG_EXE%
-i "%QUEUE_DIR%\%%~na.%FILE_EXTENSION%"
-y %TEST%
-s %WIDTH%x%HEIGHT%
-vcodec libx264
-b %VIDEO_BIT_RATE%
-bt %VIDEO_BIT_RATE_TOLERANCE%
-threads 4
-croptop %CROP_TOP%
-cropbottom %CROP_BOTTOM%
-cropleft %CROP_LEFT%
-cropright %CROP_RIGHT%
-f mp4
-flags +loop
-cmp +chroma
-partitions +parti4x4+partp8x8+partb8x8
-subq 5
-trellis 1
-refs 1
-coder 0
-me_range 16
-me %MOTION_ESTIMATION_METHOD%
-g %GOPS%
-keyint_min %KEY_INT_MIN%
-sc_threshold 40
-i_qfactor 0.71
-maxrate %MAX_VIDEO_BIT_RATE%
-bufsize 10M
-rc_eq "blurCplx^(1-qComp)"
-qcomp 0.6
-qmin 10
-qmax 51
-qdiff 4
-level 30
-aspect %WIDTH%:%HEIGHT%
-acodec libfaac
-ab %AUDIO_BIT_RATE%
-ar %AUDIO_FREQ%
-ac 2
-pass 2 "%OUT_DIR%\%%~na.mp4" > "%LOG_FILE%"
IF EXIST "%TEMP_DIR%\%%~na.pass1" DEL "%TEMP_DIR%\%%~na.pass1"
IF %EMAIL% == 1 (
echo Finished transcoding %%~na.mp4" > %MAILBODY_FILE%
echo "" >> %MAILBODY_FILE%
IF EXIST %LOG_FILE% copy %MAILBODY_FILE%+%LOG_FILE% %MAILBODY_FILE%
%BLAT_EXE% %MAILBODY_FILE% -to %TO% -subject "%%~na.mp4 is ready" -server %SMTP_HOST%
)
MOVE "%QUEUE_DIR%\%%~na.%FILE_EXTENSION%" %DONE_DIR%
)
GOTO CLEANUP
:CLEANUP
IF EXIST %TEMP_DIR% RD %TEMP_DIR% /S /Q
@echo complete
I’ve left a lot of commented lines in, so you can see what I’ve been tweaking. I could have set the dimensions lower than 640×352 if I was only every going to view the movies on the iPod screen (420 wide), but I want to play the movies on my laptop and TV too. 640 was a good compromise. 720 would be larger than the source, so I would gain nothing in quality, just bigger files.
A few observations:
- The way the cropping settings interact with the final width and height is a bit weird.
- level 30 is essential for getting files from iTunes to the iPod/iPhone. iTunes is strict about what H.264 profiles and levels it ‘thinks’ the iPod can and can not play. The device can do all kinds of data rates, it’s iTunes that is fussy. -level 30 sets the level to 3 in the list of H.264 profiles on Wikipedia
- I chose 25 fps because my source movies are mostly PAL (25fps)
- I chose 250 for the GOP size because the fps is 25. (GOP is group of picture, or key frame. 250 means a full picture frame sample (key frame with no in-betweening info) will be created once every 10 seconds at 25 fps. This is quite high but keeps the file size low and the movie is still perfectly watchable when you skip ffwd or rewind - the picture may be blocky for a few seconds - until the next keyframe.
How does it look?
Here is a one minute clip from a documentary about musician/composer/arranger Quincy Jones. I chose this section because it contains lots of colours, music, rostrum pans, zooms, talking heads, and some grainy old black and white footage - the kind of thing that can sometimes be blocky or stutter at lower data rates. Click the poster frame to download the movie. (See the TESTING variable for how to encode just a section of a movie). Sorry, the movie is not optimized for streaming, you’ll have to down load all 7MB.


Entries (RSS)
All those parameters, wow! Does anyone really use DOS!? I’ve been using this:
#!/bin/bash
abitrate=19200
vbitrate=1200kb
ffmpeg -y -i "$1" -r 29.97 -b $vbitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -ar 48000 -ab $abitrate -s 320x240 -vcodec mpeg4 -acodec aac $(echo "$1" | sed -e 's,\(.*\)\..*,\1.mp4,')
I’ve also been looking at mass video conversion for stylo.tv using S3 & EC2 using the Python boto support:
http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1404&categoryID=152
Hi Chris,
Interesting sed line.
DOS batch script was the quickest way to make use of an idle Windows machine without having to install a scripting language, power shell or cygwin environment. I still see a lot of bat files created by windows sys admins to pass params to console apps. It’ not dead yet.
Thanks for that link to the AWS article, looks incredible.
Your ffmpeg settings rock, but the shell script isn’t working for me on Ubuntu. /bin/sh on Ubuntu is dash which is POSIX compliant. I’ve never seen the % characters inside variable interpolation before.
I’d also been using a script called podit from here:
http://slated.org/howto_transcode_h264_for_ipod_with_ffmpeg
It’s a nice script, but the ffmpeg settings are painfully slow–1h37m for two pass of a 30m video versus 28m for your settings. I’ve merged the two which you can get here:
ftp://u39603658-quack:quackquack@s150912529.onlinehome.us
I’ve commented out the call to MP4Box because the new iPod firmware doesn’t need it. The script also uses a short Perl script to show the transcode time, you could comment that out if you like.
Great work though, it’s the ffmpeg settings that are the hard part. My simple settings took the same amount of time for much worse quality.
Thanks man. The altered podit bash is great!
The settings in the podit script at slated.org didn’t work for me. I think it is designed to make video for the regular video iPod and nano. 600kbs is low and iTunes refused to copy movies with -level 13 to the iPod Touch. -level 30 worked for me. I effed about with that level setting for hours. Bloody iTunes.
Great to have them merged. I have yours working on the Macbook with it’s odd bash version. All I had to do was change cp -aL to cp -L and install Date::Manip.
The ffmeg args are slightly different in my pass 1 and pass 2 though. Sub-quantisation ( -subq ) is deliberately lower on 1st pass than second because it doesn’t factor in the first pass. 1 makes 1st pass quicker. You might get higher quality on second pass with -subq 5 or 6. Maybe you meant it to be the same for speed. (I don’t know how much it really matters to the quality).
Some other notes:
Threads can be set to auto. This absolutely f’ing flies on a dual quad core xeon workstation.
I had to use -vcodec x264 not h264
Note to self: to get Perl Date::Manip installed on Mac, grab a few ports using Mac Ports, then use the CPAN shell…
sudo bash
port install lynx
port install ftpget
port install gpg
perl -MCPAN -e ’shell’
answered all questions with enter, unless I had to give a path to one of the above (lynx, ftpget, gpg)
at the cpan prompt>
make Date::Manip
install Date::Manip
Bugger making web sites, I want to write software that takes ‘blurCplx^(1-qComp)’ as a parameter argument.
[…] Useful script which will convert a directory full of movies, in any format your Operating System is capable of, to H.264/AAC mp4. There are versions for bash and DOS which should work on Linux, Mac OS X and Windows (using pre-compiled versions of ffmpeg for Windows with all the required libraries which are easily available online). Compiling your own version is recommended so the binary will make best use of your CPU capabilities, like MMX2, SSE2Fast, SSSE3, Cache64, etc. H.264 video encoding for iPhone/iPod Touch. Ffmpeg Linux, Mac OS X … […]