Sorry, you need to enable JavaScript to visit this website.

Howto: Encode H.264 Video for the iPod using ffmpeg

  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.
  • warning: Parameter 1 to phptemplate_comment_submitted() expected to be a reference, value given in /home2/slatedor/public_html/includes/theme.inc on line 669.

Save this script somewhere, e.g. /usr/local/bin/podit, then chmod 755 /usr/local/bin/podit

Usage: podit /path/to/file

Update: Apparently ffmpeg's parameters have changed yet again [sigh]. Depending on what version of ffmpeg you are using, you may need to make the following changes:

Update 1:
-vcodec h264 is now "-vcodec libx264"
-acodec aac is now "-acodec libfaac"

Update 2:
-me is now "-me_method"
-loop is now "-flags +loop"
-slice is now "-flags +slice"
(To set both the above, use "-flags +loop+slice"
-part[xxx] is now "-partitions +parti4x4+partp8x8+partb8x8"
-brdo has gone. This is automatically set if "-subq" is 7 or higher
-chroma is now "-cmp +chroma"

Also ... nobody noticed that "aspect_ratio=xxx" was missing from the variables section (as it stood, this script would not have worked, but just produce an error "invalid aspect ratio")? Oh well, it's fixed now :)

Thanks to FakeOutdoorsman for update 1, and various sources for update 2.

#!/bin/bash
# Name:    podit
# Summary: iPod H264 640x480 video transcoder script 
# Version: 1.1.4
# Date:    2009.10.12
# License: GPLv3+
# URL:     http://slated.org/howto_transcode_h264_for_ipod_with_ffmpeg
# © Homer, 2007 - 2009
#
# Requires: ffmpeg
# Requires: gpac
# Requires: perl-DateManip

# These requirements are a guide only. As a minimum you'll
# need ffmpeg and MP4Box (from gpac). You could even get away
# without using MP4Box, although I doubt you will have iTunes
# compatible files (they'll still work with the iPod though).
# Any components you don't want to use (MP4Box, Perl-DateManip)
# will need to be edited/commented out of this script first.

#########################################################################
# This program is free software: you can redistribute it and/or modify  #
# it under the terms of the GNU General Public License as published by  #
# the Free Software Foundation, either version 3 of the License, or     #
# (at your option) any later version.                                   #
#                                                                       #
# This program is distributed in the hope that it will be useful,       #
# but WITHOUT ANY WARRANTY; without even the implied warranty of        #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
# GNU General Public License for more details.                          #
#                                                                       #
# You should have received a copy of the GNU General Public License     #
# along with this program.  If not, see <http://www.gnu.org/licenses/>. #
#########################################################################

###########################################################
# Credits...                                              #
# Forums:        Doom9, Videohelp, and Ubuntu.            #
# Mailing lists: MPlayer and ffmpeg.                      #
# Blogs:         Everywhere.                              #
#                                                         #
# Special thanks to jdong on Ubuntu forums.               #
#                                                         #
# Note 1: This script is *very* CPU intensive (slow),     #
#         and will probably melt your PC, but then again  #
#         it *does* produce incredibly high quality video #
#         so IMHO it's worth it. YMMV.                    #
#                                                         #
# Note 2: Now uses MP4Box to "fix" the files for iTunes.  #
#                                                         #
# Note 3: I've changed podit to work on a *copy* of the   #
#         original file, rather than directly on the      #
#         source, primarily because I was transcoding     #
#         over a network, and it just made more sense to  #
#         work on local copies.                           #
#                                                         #
###########################################################

################################
### User definable variables ###
###########################################################
# These are the only 4 prefs I would recommend you alter. #
###########################################################
#                                                         #
# framerate: e.g. (10, 15, 23.976, 25, 29.97, 30 = MAX)   #
# A value of 0 will make the output file the same frame   #
# -rate as the input file.                                #
#                                                         #
# framelimit: transcode only the first [n] frames.        #
# (Good for testing.) A value of 0 disables this limit.   #
#                                                         #
# resolution: 320x240 / 640x480 (4:3 aspect)              #
#             320x176 / 640x352 (16:9 aspect)             #
# Other resolutions are possible (untested).              #
#                                                         #
# passes: 1 or 2 pass encoding. ffmpeg does not support   #
# >2 passes, not that >2 passes is very useful anyway.    #
#                                                         #
# NOTE: THIS SCRIPT CURRENTLY DOES *NO* ERROR CHECKING,   #
#       SO PLEASE USE SENSIBLE VALUES.                    #
#                                                         #
###########################################################

framerate=25
framelimit=1000
resolution="640x480"
passes=2
croptop_val=0
cropbottom_val=0
cropleft_val=0
cropright_val=0
video_bitrate="600k"
audio_bitrate="160k"
aspect_ratio="4:3"

########################
### Static variables ###
########################
infile="$1"
tmpdir="${HOME}/tmp"

############################
### transcoding function ###
############################
podenc () {
   local passval=$1

   if [ $passval -eq 0 ]
      then
         local passvar=""
      else
         local passvar="-pass $passval"
   fi
   if [ $passval -eq 1 ]
      then
         local audiovar="-an"
         local outvar="/dev/null"
      else
         local audiovar="-acodec libfaac -ab $audio_bitrate -ar 48000 -ac 2"
         local outvar="$tmpfile2"
   fi
   if [ $framelimit -eq 0 ]
      then
         local framesvar=""
      else
         local framesvar="-vframes $framelimit"
   fi
   if [ $framerate -eq 0 ]
      then
         local ratevar=""
      else
         local ratevar="-r $framerate"
   fi
   ### You are not expected to understand this. I know *I* don't.
   nice -n 19 ffmpeg -y $ratevar -i "$tmpfile1" $ratevar $framesvar \
        -cropleft $cropleft_val -cropright $cropright_val \
        -croptop $croptop_val -cropbottom $cropbottom_val \
        -v 1 -vcodec libx264 -b $video_bitrate -bt 175k -refs 2 \
        -deblockalpha 0 -deblockbeta 0 -flags +loop+slice \
        -partitions +parti4x4+partp8x8+partb8x8 \
        -me_method 8 -me_range 21 -subq 7 -cmp +chroma \
        -bf 0 -level 13 -g 300 -keyint_min 30 -aspect $aspect_ratio \
        -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 \
        -qmax 35 -qdiff 4 -i_qfactor 0.71428572 -b_qfactor 0.76923078 \
        -maxrate 768k -bufsize 3000k -s $resolution -f mp4 \
        $audiovar $passvar "$outvar"

   exit_status=$?
}

########################################
###### Calculate transcoding time ######
########################################
transtime () {
   local newdate=$1
   local olddate=$2

   local difftime=$(echo "${olddate} ${newdate}" | \
      awk '{print "perl -MDate::Manip -e '\''$delta = DateCalc(""\""$1"\""",""\""$2"\""",\\$err,1); print $delta;'\''"}' | \
      sh | sed -e s/^.//)

   local diffyears=$(echo $difftime | cut -d':' -f1)
   local diffmonths=$(echo $difftime | cut -d':' -f2)
   local diffweeks=$(echo $difftime | cut -d':' -f3)
   local diffdays=$(echo $difftime | cut -d':' -f4)
   local diffhours=$(echo $difftime | cut -d':' -f5)
   local diffminutes=$(echo $difftime | cut -d':' -f6)
   local diffseconds=$(echo $difftime | cut -d':' -f7)

   echo -ne "\033[1;31mTranscoding time: "

   if [ "$diffyears" -ne 0 ]
      then
         echo -n "$diffyears Years, "
   fi

   if [ "$diffmonths" -ne 0 ]
      then
         echo -n "$diffmonths Months, "
   fi

   if [ "$diffweeks" -ne 0 ]
      then
         echo -n "$diffweeks Weeks, "
   fi

   if [ "$diffdays" -ne 0 ]
      then
         echo -n "$diffdays Days, "
   fi

   if [ "$diffhours" -ne 0 ]
      then
         echo -n "$diffhours Hours, "
   fi

   if [ "$diffminutes" -ne 0 ]
      then
         echo -n "$diffminutes Minutes, "
   fi

   echo -e "$diffseconds seconds.\033[0m"
} 

##############################
###### Reverse a string ######
##############################
reverseit () {
local instring=$1
instring_length=$(echo -n "$instring" | wc -c)
reverse_string=
while [ $instring_length -gt 0 ]
   do
      reverse_string=${reverse_string}$(echo $instring | cut -c $instring_length)
      let instring_length=instring_length-1
   done
echo "$reverse_string"
}

##################################
###### Strip file extension ######
##################################
stripext () {
local instring=$1
local revnoext_string=$(reverseit "$instring" | sed 's/^.*\.//')
reverseit "$revnoext_string"
}

##################
###### Main ######
##################
outdir=$(find "$infile" -printf "%h")
base_name=$(basename "$infile")
barename=$(stripext "$base_name")
outfile="${outdir}/${barename}.podit.mp4"
tmpfile1="${tmpdir}/${base_name}"
tmpfile2="${tmpdir}/${barename}.podit.mp4"
tmpfile3="${tmpdir}/${barename}.podit.m4v"

clear

mkdir -p "$tmpdir"

if [ -e "$outfile" ]
   then
      echo -n "\"${outfile}\" already exists. Overwrite? (y/n): "
      read answer
      if [ "$answer" != "y" ]
         then
            echo "Transcoding aborted on user request."
            exit 1
      else
         echo
      fi
fi

count=1
while [ $count -lt 11 ]
   do
      let countdown=11-$count
      let count=count+1
      echo -n "Transcode will proceed in $countdown seconds."
      sleep 1
      echo -ne "\b \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
   done
echo -ne "                                       \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"

rm -f "$tmpfile1" "$tmpfile2" "$tmpfile3" "$outfile"
echo "Copying \"${base_name}\" to \"${tmpdir}\""
rsync -v --progress "$infile" "$tmpfile1"

starttime=$(date +%Y%m%d%H:%M:%S)

echo
if [ "$passes" -eq 1 ]
   then
      podenc 0
   else
      podenc 1
      podenc 2
fi

if [ "$exit_status" -ne 0 ]
   then
      echo "Something went wrong with ffmpeg."
      echo "Please check the output and try again."
      exit 1
fi

endtime=$(date +%Y%m%d%H:%M:%S)
echo
transtime $starttime $endtime
echo

echo "Removing temporary copy of source file."
rm -f "$tmpfile1"

echo
echo "Fixing transcoded file for iPod with MP4Box."
echo
MP4Box -ipod -add "$tmpfile2" "$tmpfile3"

if [ "$?" -ne 0 ]
   then
      echo "Something went wrong with MP4Box."
      echo "Please check the output and try again."
      exit 1
fi

echo
echo "Removing MP4Box's temporary source file."
rm -f "$tmpfile2"

echo
echo "Moving finished video to $outdir"
mv "$tmpfile3" "$outfile"

echo
echo -e "\033[1;31mJob \"${barename}\" completed successfully.\033[0m"

exit 0
AttachmentSize
podit-1.1.4.tar.bz24.13 KB

Comments

Script problem

Hi

I keep on getting this:

"Unknown encoder libx264" even when I edit in the full path (/usr/lib64...)

Can't figure out what's happening. (Running Ubuntu LTS 10.4 64 bits)

(Re)install x264

You either don't have x264 installed, or you have the wrong version (presumably from the Medibuntu repos), so need to uninstall the old version then install the latest one.

http://ubuntu-ky.ubuntuforums.org/showthread.php?t=1117283

Error when using latest ffmpeg (SVN r19736)

I get the following error when testing the script on a video with the latest ffmpeg. I'm guessing it has something do with commandline options changing, but I'm not very experienced with ffmpeg. Does anyone have any suggestions? Output is below.

Thanks!

----------------------

FFmpeg version SVN-r19736, Copyright (c) 2000-2009 Fabrice Bellard, et al.
configuration: --enable-gpl --enable-nonfree --enable-pthreads --enable-libfaac --enable-libfaad --enable-libmp3lame --enable-libtheora --enable-libx264 --enable-libxvid --enable-x11grab
libavutil 50. 3. 0 / 50. 3. 0
libavcodec 52.34. 0 / 52.34. 0
libavformat 52.38. 0 / 52.38. 0
libavdevice 52. 2. 0 / 52. 2. 0
libswscale 0. 7. 1 / 0. 7. 1
built on Aug 27 2009 22:38:35, gcc: 4.3.3

Seems stream 0 codec frame rate differs from container frame rate: 47.95 (1918/40) -> 23.98 (24000/1001)
Input #0, matroska, from '/home/dwoods/tmp/Gorillaz - Feel Good.mkv':
Duration: 00:04:06.71, start: 0.000000, bitrate: N/A
Stream #0.0: Video: h264, yuv420p, 1280x720, PAR 1:1 DAR 16:9, 23.98 tbr, 1k tbn, 47.95 tbc
Stream #0.1: Audio: vorbis, 44100 Hz, 2 channels, s16
Invalid value '1' for option 'loop'
FFmpeg version SVN-r19736, Copyright (c) 2000-2009 Fabrice Bellard, et al.
configuration: --enable-gpl --enable-nonfree --enable-pthreads --enable-libfaac --enable-libfaad --enable-libmp3lame --enable-libtheora --enable-libx264 --enable-libxvid --enable-x11grab
libavutil 50. 3. 0 / 50. 3. 0
libavcodec 52.34. 0 / 52.34. 0
libavformat 52.38. 0 / 52.38. 0
libavdevice 52. 2. 0 / 52. 2. 0
libswscale 0. 7. 1 / 0. 7. 1
built on Aug 27 2009 22:38:35, gcc: 4.3.3

Seems stream 0 codec frame rate differs from container frame rate: 47.95 (1918/40) -> 23.98 (24000/1001)
Input #0, matroska, from '/home/dwoods/tmp/Gorillaz - Feel Good.mkv':
Duration: 00:04:06.71, start: 0.000000, bitrate: N/A
Stream #0.0: Video: h264, yuv420p, 1280x720, PAR 1:1 DAR 16:9, 23.98 tbr, 1k tbn, 47.95 tbc
Stream #0.1: Audio: vorbis, 44100 Hz, 2 channels, s16
Invalid value '1' for option 'loop'
Something went wrong with ffmpeg.
Please check the output and try again.

ffmpeg devs have changed things once again...

Invalid value '1' for option 'loop'

IOW the loop option has changed. I have no idea what value it expects, nor where the necessary documentation for the changes is.

Some quick research would seem to indicate the "-me" option has gone too.

Try removing both of those switches in the ffmpeg command, and see if it works.

I haven't updated ffmpeg in a long time, and indeed I try to avoid doing so, for exactly these reasons.

OK, it's all fixed now ... again

I've updated the script to reflect yet more changes to ffmpeg (see above).

Error "let: not found" ?

I've loving the idea of this script. Looks like it'll do everything I want it to, except that it doesn't.

Trying this on Ubuntu 8.04, I can't seem to get past an error of "/usr/local/bin/podit: 238: let: not found" and while I'm always up to learning and messing around, I can't quite seem to get over this stumbling block. Anything you can suggest?

Ubuntu uses the wrong shell

Apparently the default shell in Ubuntu is "dash", which does not support bash builtins like "let" for some reason.

Simply change the first line of the script to:

#!/bin/bash

And make sure bash is installed.

See also: http://ubuntuforums.org/showpost.php?p=2474419&postcount=10

Works like a charm!

The shell change makes everything happy. Many thanks for the prompt reply, and this wonderful script!

Where is deltadays.pl?

More information, please!

deltadays.pl is in the above post

Please read the last few lines of the post starting with: "Save the following as /usr/local/bin/deltadays.pl then chmod 755 /usr/local/bin/detadays.pl".

HTH.

should detadays.pl be deltadays.pl?

Probably explains one of the comments

Thanks for the heads-up

Typo corrected.

Works but the output file...

Hello,
Awesome script. It works well and the product is high quality. However I am having one little issue.

The script runs and it copies the original source file to the tmp folder but the file it creates is called something like 1\n0\n11.podit.m4v (\n = newline) and then after the file is done encoding it can't move it back to the original sources folder.

I'm running FreeBSD 6.3, everything up to date. I'll probably be able to figure it out but just a heads up.

Weird characters in source filename

Can you post the exact name (including path) of the source file?

This script does almost no error checking, and makes no allowances for weird filenames, so the best I can advise is that you make sure all your sources have names with legal chars before proceeding.

I may look into a better way of dealing with this in the future.

I am working on an update, which at this stage is merely feature enhancements (autocrop and other goodies), but I suppose I will get around to doing more extensive error checking too.

Great Script!

Great Script! Thanks been browsing around the tubes for a while and this is the best solution out there. Thanks for sharing!

File Name extension removal in script

Very nice script. I did make one change though:

The stripext() function did not seem to work well for me. If a file name had more than one dot, it would grab the first part of the filename.

"this.file.mp4" became "this.mp4"

I did the following to correct this, making the stripext() and reversit() functions not necessary:

##################
###### Main ######
##################
outdir=$(find "$infile" -printf "%h")
base_name=$(basename "$infile")
barename=$(basename "$infile" | sed 's/\(.*\)\..*/\1/')
#barename=$(stripext "$base_name")

Yeah it works like a charm.

This Script has solved alot of my problems. I spent alot of time doing things the manual way up until now.

Great job.

Thanks for this script.
NssY.