<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Diego's Rants &#187; AppleTV</title>
	<atom:link href="http://blog.massanti.com/tag/appletv/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.massanti.com</link>
	<description>Confessions from a Geek</description>
	<lastBuildDate>Wed, 25 Aug 2010 09:39:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>iPhone and iPod Touch compatible video encoding, server side.</title>
		<link>http://blog.massanti.com/2008/10/12/iphone-and-ipod-touch-compatible-server-side-encoding/</link>
		<comments>http://blog.massanti.com/2008/10/12/iphone-and-ipod-touch-compatible-server-side-encoding/#comments</comments>
		<pubDate>Sun, 12 Oct 2008 06:50:51 +0000</pubDate>
		<dc:creator>Diego Massanti</dc:creator>
				<category><![CDATA[Video Encoding]]></category>
		<category><![CDATA[AppleTV]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iPod]]></category>
		<category><![CDATA[mkmp4]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://blog.massanti.com/?p=852</guid>
		<description><![CDATA[Some days ago, I published a tutorial with some bash scripts to automate the whole process of encoding MP4 files with H264 video and HE-AAC audio which are playable by flash players and the like. Since I&#8217;m working on a really big project which involves a &#8220;youtube like&#8221; behavior, i though it would be cool [...]]]></description>
			<content:encoded><![CDATA[<p><a rel="lightbox" href="http://blog.massanti.com/wp-content/uploads/2008/10/iphone_massanti.png"><img class="size-medium wp-image-861 alignright" title="iPhone screenshot" src="http://blog.massanti.com/wp-content/uploads/2008/10/iphone_massanti-161x300.png" alt="" width="97" height="180" /></a>Some days ago, I <a href="http://blog.massanti.com/2008/09/26/mkmp4-automated-h264-aacplus-encoder-script-mac-linux/">published a tutorial with some bash scripts to automate the whole process of encoding MP4 files with H264 video and HE-AAC audio</a> which are playable by flash players and the like.</p>
<p>Since I&#8217;m working on a really big project which involves a &#8220;youtube like&#8221; behavior, i though it would be cool to also create iPhone / iPod compatible streams (I&#8217;m amazed at the fact that 3% of the people coming to this site is using iPod&#8217;s or iPhone&#8217;s)&#8230; so, I did some modifications to the original script for it to generate the compatible streams.</p>
<p>Now, how am i dealing with this&#8230; well&#8230; let me explain you:</p>
<p>The <a href="http://www.apple.com/iphone/specs.html">iPhone specification</a> states:</p>
<blockquote><p>Video formats supported: H.264 video, up to 1.5 Mbps, 640 by 480 pixels, 30 frames per second, Low-Complexity version of the H.264 Baseline Profile with AAC-LC audio up to 160 Kbps, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats; H.264 video, up to 2.5 Mbps, 640 by 480 pixels, 30 frames per second, Baseline Profile up to Level 3.0 with AAC-LC audio up to 160 Kbps, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats; MPEG-4 video, up to 2.5 Mbps, 640 by 480 pixels, 30 frames per second, Simple Profile with AAC-LC audio up to 160 Kbps, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats</p></blockquote>
<p>This means that the high profile streams that I am generating with the MKMP4 script won&#8217;t play, simple as that. So i have 2 possible approachs:</p>
<ol>
<li>I could just use a single low profile stream for the iPhone and iPod, and since the stream is H264 and low complexity AAC audio, it will just play fine on the flash players too.</li>
<li>I could have 2 different files, one for the flash players (making use of b-frames, entropy coding, and a high profile stream, which substantially improves the final video quality).</li>
</ol>
<p>I decided to use the option 2, mostly because i want to give desktop users as much quality as possible, and i want to use the HE-AAC encoder to save some extra bandwidth that i can instead use for the video stream, so I am using 2 different files, one for desktop users, and a different one for iPhone or iPod users.</p>
<p><span id="more-852"></span>To check if the user is visiting the site with a desktop browser or a Mobile Safari version is really simple: Let&#8217;s consider a PHP based scenario.</p>
<p>The first thing that i had to do is to create a simple function to check the browser&#8217;s user-agent string.</p>
<pre class="brush: php;">function isIphone() {
$agent = $_SERVER['HTTP_USER_AGENT'];
if (stristr($agent, 'iPhone') == TRUE || stristr($agent, 'iPod') == TRUE) {
echo '&lt;!--iPhone or iPod Touch detected--&gt;;';
return TRUE;
} else {
echo '&lt;!--iPhone or iPod Touch not detected--&gt;';
return FALSE;
}
}</pre>
<p>This function is self explanatory for anybody with basic PHP skills, basically it checks the user-agent string and returns TRUE if it contains the words &#8220;iPhone&#8221; or &#8220;iPod&#8221;.</p>
<p>So, then in your template you could make something like this:</p>
<pre class="brush: php;">if (!isIphone()) {
// Code to display the standard high profile stream goes here.
} else {
// Code to display the iPhone compatible stream goes here.
}</pre>
<p>Pretty simple.</p>
<h2>The updated MKMP4 script.</h2>
<p>This is an updated version of my MKMP4 encoding script, basically, it is almost the same thing than <a href="http://blog.massanti.com/2008/09/26/mkmp4-automated-h264-aacplus-encoder-script-mac-linux/">the original one published here</a>, with the addition of an extra -i switch that you can use if you want to create an iPhone compatible stream.</p>
<blockquote class="warning">
<h3>Important: FAAC encoder is a requeriment for this!</h3>
<p>This version of the MKMP4 script uses the FAAC encoder that you can download <a title="FAAC download page" href="http://www.audiocoding.com/downloads.html">by going here</a>. Be sure that you have correctly compiled and installed it BEFORE trying to use this script on your desktop or server.</p></blockquote>
<pre class="brush: bash;">#! /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=&quot;.mov&quot; # Set this variable to the video file extension that you want to search for when encoding whole directories.
onlythumbs=FALSE # IF you set this to TRUE, the script will only generate thumbnails.
# End user settings
# You should not change anything below this line unless you know what you are doing.

usage()
{
cat &lt;&lt; EOF
usage: $0 -f  [-w ] -b  [-q][-i][-k][-t][-o ]

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

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

width=&quot;&quot;
bitrate=
deltemp=TRUE
quality=FALSE
iphone=FALSE
poster=FALSE
filename=&quot;&quot;
rsize=&quot;&quot;
outdir=`pwd`
outFileName=&quot;&quot;
itemVideoBitrate=&quot;&quot;

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

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

if [ &quot;$filename&quot; == &quot;&quot; ]; 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 [ &quot;$bitrate&quot; == &quot;&quot; ]; then
 echo
 echo you MUST specify a target bitrate!, use the -b parameter. i.e: -b 512
 echo
 usage
 exit 1
fi

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

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

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

# Start the magic...

showMessage() {
 echo &quot;MKMP4 : &quot;$1&quot;&quot;
}

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

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

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

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

secondPass() {
 local iFile=&quot;${1##*/}&quot;    # remove directory and keep only file name.
 showMessage &quot;Starting video encoding pass 2...&quot;
 if [ $iphone == &quot;TRUE&quot; ]; then
 mencoder &quot;$1&quot; -o &quot;$outdir/${iFile%%.*}_temp.264&quot; -demuxer lavf -passlogfile &quot;$outdir/${iFile%%.*}&quot;_temp.log $rsize -ssf lgb=0.2 -ovc x264 -x264encopts bitrate=$itemVideoBitrate:frameref=1:nocabac:bframes=0:b_adapt:b_pyramid:weight_b:partitions=all:me=umh:subq=6:trellis=2:brdo:threads=auto:pass=2:analyse=all -of rawvideo -nosound
 else
 mencoder &quot;$1&quot; -o &quot;$outdir/${iFile%%.*}_temp.264&quot; -demuxer lavf -passlogfile &quot;$outdir/${iFile%%.*}&quot;_temp.log $rsize -ssf lgb=0.2 -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
 fi
}

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

}

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

mux() {
 local iFile=&quot;${1##*/}&quot;    # remove directory and keep only file name.
 MP4Box -add &quot;$outdir/${iFile%.*}_temp.264#video:fps=$MOVIE_FPS&quot; &quot;$outdir/${iFile%.*}.m4v&quot;
 if [ $iphone == &quot;TRUE&quot; ]; then
 MP4Box -add &quot;$outdir/${iFile%.*}_temp.mp4#audio&quot; &quot;$outdir/${iFile%.*}.m4v&quot;
 else
 if [ &quot;$platform&quot; == &quot;Mac&quot; ]; then
 MP4Box -add &quot;$outdir/${iFile%.*}_temp.aac&quot; -sbr &quot;$outdir/${iFile%.*}.m4v&quot;
 else
 MP4Box -add &quot;$outdir/${iFile%.*}_temp.mp4#audio&quot; &quot;$outdir/${iFile%.*}.m4v&quot;
 fi
 fi

 name=${iFile%.*}
 album=&quot;The Mac Video Archive&quot;
 author=&quot;Apple Computer // massanti.com&quot;
 comment=&quot;Professionally encoded by Diego Massanti&quot;
 created=&quot;2008&quot;
 MP4Box -inter 500 -itags album=&quot;$album&quot;:artist=&quot;$author&quot;:comment=&quot;$comment&quot;:created=&quot;$created&quot;:name=&quot;$name&quot; -lang English &quot;$outdir/${iFile%.*}&quot;.m4v
}

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

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

initialize</pre>
<p>The following example command line will encode the video stream in my_video.avi as a low profile H264 without b-frames, will encode the audio track as a Low Complexity AAC stream and will mux everything inside a M4V file which will play on the iPhone, iPod Touch, Apple TV and Quicktime / iTunes.</p>
<pre>./mkmp4 -f my_video.avi -b 512 -w 640 -q -t -i</pre>
<p>For a detailed explanation of all the supported command line switches, you can visit <a href="http://blog.massanti.com/2008/09/26/mkmp4-automated-h264-aacplus-encoder-script-mac-linux/">the original MKMP4 post</a>.</p>
<p>Some final considerations:</p>
<ul>
<li>Due to the fact that I am forced to use a low complexity AAC encoder for the iPhone streams, the audio bandwidth is hardcoded at around 100kbps inside the script, that would give you an audio quality similar to the one you get with the HE-AAC encoder at 48kbps, feel free to experiment with different values for this.</li>
<li>Using a low profile H264 stream gives me iPhone compatibility, but at the expense of losing some video quality, clearly, a 512kbps stream looks much better when we use b-frames and entropy coding so unless you really care about iPhone and iPod users like I do, you should use high profile streams.</li>
<li>If you are visiting this page with an iPhone or iPod and want to see how all this works, feel free to visit <a href="http://blog.massanti.com/apple-video-archive/ipod-itunes/">this section of the Apple Video Archive here</a> to see the magic happening: If you visit the page with a desktop browser, it will simply show the standard flash player, <strong>BUT</strong> when it detects you are on a Mobile Safari platform, it will automatically show you the iPhone compatible stream (<a rel="lightbox" href="http://blog.massanti.com/wp-content/uploads/2008/10/iphone_massanti.png">click here to see how this all looks</a>) and properly embed the quicktime player on the page.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.massanti.com/2008/10/12/iphone-and-ipod-touch-compatible-server-side-encoding/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
	</channel>
</rss>
