<?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>IONCANNON &#187; iphone</title>
	<atom:link href="http://www.ioncannon.net/tag/iphone/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ioncannon.net</link>
	<description>Thoughts on Software Development and Engineering</description>
	<lastBuildDate>Tue, 03 Jan 2012 13:59:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<atom:link rel='hub' href='http://www.ioncannon.net/?pushpress=hub'/>
		<item>
		<title>iPad Streaming Video and More</title>
		<link>http://www.ioncannon.net/programming/1015/ipad-streaming-video-and-more/</link>
		<comments>http://www.ioncannon.net/programming/1015/ipad-streaming-video-and-more/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 11:01:33 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[segmenter]]></category>
		<category><![CDATA[Streaming Video]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1015</guid>
		<description><![CDATA[I&#039;ve updated the configuration examples in the open source segmenter project to reflect Apple&#039;s recommended stream bitrates for iPad video streaming, added a few fixes and a few new features. If you are interested in streaming video on the iPad, iPhone or iPod Touch and haven&#039;t done so yet you it may help to start [...]]]></description>
			<content:encoded><![CDATA[<p>I&#039;ve updated the configuration examples in the open source segmenter project to reflect <a href="http://developer.apple.com/iphone/library/technotes/tn2010/tn2224.html">Apple&#039;s recommended stream bitrates</a> for iPad video streaming, added a few fixes and a few new features. If you are interested in streaming video on the iPad, iPhone or iPod Touch and haven&#039;t done so yet you it may help to start with my post on <a href="http://www.ioncannon.net/meta/564/iphone-windowed-http-live-streaming-server/">windowed streaming on for the iPhone</a>, then read about <a href="http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/">iPhone HTTP streaming with FFMpeg and the open source segmenter</a> and finally check out the <a href="http://www.ioncannon.net/projects/http-live-video-stream-segmenter-and-distributor/">iPad, iPhone, and iPod Touch live video streaming project page</a>.</p>
<p>Here is a demo of the iPad streaming video created with the segmenter (I tried to show the progressive upgrade happening but it happens very quickly since the iPad is on WIFI, I also show that you can scrub without any issues and if you look in the background you can see the server log displaying entries as the segments are downloaded):</p>
<p><object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/ydP51WxRDDk&#038;hl=en_US&#038;fs=1&#038;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/ydP51WxRDDk&#038;hl=en_US&#038;fs=1&#038;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></p>
<p>If you want to view the demo yourself I&#039;ve created a demo for the <a href="http://www.ioncannon.net/projects/ivids/ipad/">iPad</a>, <a href="http://www.ioncannon.net/projects/ivids/iphone/">iPhone, and iPod Touch</a>. Note that those are two links, one for the iPad version and one for the iPhone/iPod Touch version. The main difference is the size of the video.</p>
<p>I used the open source video <a href="http://www.bigbuckbunny.org/">Big Buck Bunny</a> (the 1920&#215;1080 ogg version) for the above demos.</p>
<p>If you are interested in more details on what changed read on or skip to the bottom if you want to see what I&#039;ll be working towards in future versions of the segmenter.</p>
<p><span id="more-1015"></span></p>
<p>Over the past few weeks I learned that it is important that each segment starts with an <a href="http://avidemux.org/admWiki/index.php?title=H264#I-Frames">IDR frame</a>. To accomplish this I thought setting the gop so that the segment time % (gop size / frame rate) = 0 would work but I haven&#039;t completely convinced myself of this yet. An example of would be a segment size of 10 seconds % (300 gop size / 30 frame rate) = 0. This should insure that each segment starts with the correct i-frame and while I have looked over the resulting segments with a hex editor and believe it works I still get errors when using certain tools on the individual segments that makes me think it isn&#039;t working like I think it should. Either way I have included updates to the gop size in the example configuration files. The gop size is controlled by the -g option for FFMpeg and in the examples I have set it so that for a 10 second segment with the given frame rate the gop size makes sure each segment starts correctly. If you want to know more you can dig into the resulting segments and use this forum post on <a href="http://forum.digital-digest.com/showthread.php?t=89736">how to extract an i-frame</a>, this <a href="http://www.mpucoder.com/DVD/mpeghdrs.html">list of mpeg headers</a> to verify that each has the correct i-frame at the start.</p>
<p>The <a href="http://developer.apple.com/iphone/library/technotes/tn2010/tn2224.html">Apple streaming tech notes</a> where informative in a few other ways as well. The tech notes contain the supported h264 profiles for each device. For the iPhone/iPod Touch baseline level 3.0 is supported while the iPad supports baseline level 3.1. You can find out more about <a href="http://h264.code-shop.com/trac/wiki/Encoding">h264 encoding levels with FFMpeg</a> and also review <a href="http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/">FFMpeg x264 encoding guide</a>, <a href="http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping">FFMpeg option mappings</a> and <a href="http://www.mplayerhq.hu/DOCS/HTML/en/menc-feat-x264.html">information on encoding with the x264 codec</a> for more information.</p>
<p>Another bit of information disclosed in the Apple tech notes is the existence of a media stream validation tool. This tool can be downloaded from the Apple iPhone developer site and has to be run on OS X. Apple recommends that you use this tool to validate any streams that you create.</p>
<p>One other note that I have recently ran across is that Apple seems to be <a href="http://techcrunch.com/2010/03/26/iphone-video-apps-downgrade-streaming-rates/">rejecting <i>native apps</i> that have video streams without a fall back audio stream</a>. The correct way of generating the fallback 64k audio only stream is something that is lacking in the current version of the segmenter but I hope to fix that soon. It won&#039;t matter for those using the HTML5 video tag however.</p>
<p>If you are using HTML5 video tags it is important not to use my older posts as a guide for the iPad since I use a now outdated example. Those examples still work on the iPhone but they do not work on the iPad. Here is an updated example that will work on both the iPhone and iPad:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Video Test&lt;/title&gt;
  &lt;/head&gt;
  &lt;body style=&quot;background-color:#FFFFFF; &quot;&gt;
    &lt;center&gt;
      &lt;video controls&gt;
        &lt;source src=&quot;stream_multi.m3u8&quot;/&gt;
      &lt;/video&gt;
    &lt;/center&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Finally I am currently using the latest version of FFMpeg to do tests and it seems to have become more reliable for me. The current version in git seems to be pretty good at producing usable videos from a variety of source videos.</p>
<p>The following is a quick summary of the recent changes I have made:</p>
<ul>
<li>Applied an audio patch for the segmenter from Scott Kidder. This patch skips any video processing if there is no video stream.</li>
<li>Modified sample configs with newer FFMpeg string, gop sizes, frame rates and other parameters to match the Apple recomended values for iPhone/iPod Touch with aspect ratios of 4:3 and 6:9 on cell or wifi and iPad with aspect ratios of 4:3 and 6:9 on cell or wifi</li>
<li>Added configuration sanity checks</li>
<li>Made gems not required if not using those features that need them</li>
<li>Added fix for deprecation warning from newest version of libavformat</li>
<li>Made termination work better when killed with SIGINT/ctrl-c</li>
<li>Fixed some minor issues with index file format</li>
</ul>
<p>The following are enhancements I&#039;m planning on making:</p>
<ul>
<li>
I plan on creating a howto on getting everything set up on EC2.
</li>
<li>
Dig more into verification that each segment starts with the correct I-frame.
</li>
<li>
I&#039;m going to work on refactoring everything so that instead of creating files and then transferring them it streams them all. This makes more sense with doing encryption as just another part of the pipeline.
</li>
<li>
Add ability to do <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming-02#section-6.1.2">encryption</a>. I have a proof of concept working but I decided I need to get everything set up in one continuous stream before integrating encryption into the mix.
</li>
<li>
<a href="http://www.ioncannon.net/programming/941/using-daemon-kit-and-robustthread-to-build-ruby-daemons/">I had thoughtabout using the ruby daemon utils</a> to wrap the segmenter but I decided it probably isn&#039;t necessary at this point.
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1015/ipad-streaming-video-and-more/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>iPhone Windowed HTTP Live Streaming Server</title>
		<link>http://www.ioncannon.net/meta/564/iphone-windowed-http-live-streaming-server/</link>
		<comments>http://www.ioncannon.net/meta/564/iphone-windowed-http-live-streaming-server/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 11:22:54 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[meta]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[streaming]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=564</guid>
		<description><![CDATA[After some more work I have enhanced the HTTP segmenter and uploading script from my iPhone streaming using AWS S3 and Cloudfront post. I added a number of features and tried to pull together some of the ideas from the comments. I&#039;ll go over some of the features here and there is a full list [...]]]></description>
			<content:encoded><![CDATA[<p>After some more work I have enhanced the HTTP segmenter and uploading script from my <a href="http://www.ioncannon.net/programming/475/iphone-windowed-http-live-streaming-using-amazon-s3-and-cloudfront-proof-of-concept/">iPhone streaming using AWS S3 and Cloudfront</a> post. I added a number of features and tried to pull together some of the ideas from the comments. I&#039;ll go over some of the features here and there is a full list of configuration options on the <a href="http://www.ioncannon.net/projects/http-live-video-stream-segmenter-and-distributor/">HTTP Live Video Streaming server</a> project page and the source is available at the <a href="http://github.com/carsonmcdonald/HTTP-Live-Video-Stream-Segmenter-and-Distributor/tree/master">github repo</a>.</p>
<p>So the major changes I have added are:</p>
<ul>
<li>Yaml based configuration file. See the project page for a complete list of options.</li>
<li>Ability to transfer segments via copy, ftp, scp and s3.</li>
<li>Added the ability to do variable bitrate streams.</li>
<li>Added re-streaming support.</li>
<li>Added logging to a file and better debug output.</li>
</ul>
<p>The variable bitrate streams where done by using pipes. I haven&#039;t done a large amount of testing but it seems to work fairly well. I am able to stream a live HD video source into 3 different bitrates on a fairly old PC. Here are a couple clips I created to show the progressive enhancement in action, you probably want to switch to the HD version of the video and watch it full screen to get the bet view:</p>
<p><center><br />
<object width="480" height="295"><param name="movie" value="http://www.youtube.com/v/teKAyN0qZVY&#038;rel=0&#038;color1=0xb1b1b1&#038;color2=0xcfcfcf&#038;feature=player_profilepage&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/teKAyN0qZVY&#038;rel=0&#038;color1=0xb1b1b1&#038;color2=0xcfcfcf&#038;feature=player_profilepage&#038;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="480" height="295"></embed></object></center></p>
<p>The configuration file will allow for any number of encoding options or transfer options and they can be put together in a number of different ways. Here are a couple examples of both, see the example configuration files for more. </p>
<p>An encoder example:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ep_128k:<br />
&nbsp; ffmpeg_command: &quot;ffmpeg -er 4 -y -i %s -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s 320&#215;240 -vcodec libx264 -b 128k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 128k -maxrate 128k -bufsize 128k -rc_eq &#039;blurCplx^(1-qComp)&#039; -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2 &#8211; | %s %s %s %s %s&quot;<br />
&nbsp; bandwidth: 128000</div>
</div>
<p><br/></p>
<p>Transfer configuration example:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ftp_dev:<br />
&nbsp; transfer_type: &#039;ftp&#039;<br />
&nbsp; remote_host: &#039;192.168.1.1&#039;<br />
&nbsp; user_name: &#039;user&#039;<br />
&nbsp; password: &#039;pass&#039;<br />
&nbsp; directory: &#039;html/streamingvideo&#039;</div>
</div>
<p>As a final note on changes, you are no longer able to use the segmenter without the script now really. If you want to do that you should use the original version of the <a href="http://svn.assembla.com/svn/legend/segmenter/">segmenter source</a>. </p>
<p>Please note that there is still some work to be done on the script to be complete. If I have time my next enhancement will be to add encryption and I will probably try to test builds on other distributions (maybe attempt to create segmenter binaries).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/meta/564/iphone-windowed-http-live-streaming-server/feed/</wfw:commentRss>
		<slash:comments>63</slash:comments>
		</item>
		<item>
		<title>iPhone Windowed HTTP Live Streaming Using Amazon S3 and Cloudfront Proof of Concept</title>
		<link>http://www.ioncannon.net/programming/475/iphone-windowed-http-live-streaming-using-amazon-s3-and-cloudfront-proof-of-concept/</link>
		<comments>http://www.ioncannon.net/programming/475/iphone-windowed-http-live-streaming-using-amazon-s3-and-cloudfront-proof-of-concept/#comments</comments>
		<pubDate>Mon, 06 Jul 2009 02:07:37 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[cloudfront]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[s3]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=475</guid>
		<description><![CDATA[This post should be seen as a proof of concept. I&#039;m working on creating a more concise and easier to use package of everything covered here but I felt like getting the knowledge out sooner rather than later would be of help to people looking for a way to do this. If you are interested [...]]]></description>
			<content:encoded><![CDATA[<p>This post should be seen as a proof of concept. I&#039;m working on creating a more concise and easier to use package of everything covered here but I felt like getting the knowledge out sooner rather than later would be of help to people looking for a way to do this. If you are interested keep an eye on the <a href="http://www.ioncannon.net/projects/http-live-video-stream-segmenter-and-distributor/">HTTP live video stream segementer and distributor project page</a> as well as the <a href="http://github.com/carsonmcdonald/HTTP-Live-Video-Stream-Segmenter-and-Distributor/tree/master">github git repository</a>.</p>
<p>After my post on <a href="http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/">using FFMpeg and an open source segmenter</a> to create videos for the iPhone that conform to the <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming-01">HTTP live streaming protocol</a> I decided to see if I could get the same segmenter to work on a live stream. As it turns out it didn&#039;t take much modification to work. </p>
<p>If you are looking for something you can buy out of the box it appears that <a href="http://gigaom.com/2009/07/02/akamai-to-make-iphone-video-streaming-smooth/">Akamai</a> is doing <a href="http://newteevee.com/2009/06/30/video-see-apples-http-adaptive-video-streaming-in-action/">iPhone video streaming</a> now. I believe that the following solution using Amazon <a href="http://aws.amazon.com/s3/">S3</a> and <a href="http://aws.amazon.com/cloudfront/">Cloudfront</a> is probably as good as what Akamai can offer but it may be a better choice if you don&#039;t want to have to maintain the configuration.</p>
<p>I put together a quick diagram of the process of transferring the video stream from source to final destination that will hopefully help people understand the full picture before jumping into the details:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/07/streaming-diagram.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/07/streaming-diagram.png" alt="HTTP Live Streaming Diagram" title="HTTP Live Streaming Diagram" width="450" height="250" class="alignnone size-medium wp-image-508" /></a></p>
<p><span id="more-475"></span></p>
<p>Please note that except for the video stream all of the following was done using Fedora 11. I believe it could work on Windows or OS X but I haven&#039;t had time to test it on either.</p>
<h3>Step 1: Find a suitable video source</h3>
<p>This is an important part and can take more work than it seems like it should. I started out trying to stream video from a  USB QuickCam but for some reason the resulting stream wasn&#039;t correctly formatted even after going through the FFMpeg transcoding. I then turned to the iSight camera on a macbook using <a href="http://www.ioncannon.net/software/478/streaming-video-between-quicktime-broadcaster-and-vlc/">QuickTime Broadcaster to VLC streaming</a>. The resulting stream from the iSight camera works well.</p>
<p>The easiest way I found to experiment with finding a good stream is to dump a short clip out to a file then use the instructions in my <a href="http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/">previous post</a> to test.</p>
<p>It is also possible to do all of the following steps using a non-live video stream. In fact, due to the flexibility of what can be input into FFMpeg, someone could pull a stream from somewhere like <a href="http://ustream.tv">Ustream TV</a> or <a href="http://www.livestream.com/">Livestream</a> and rebroadcast it. Doing so would open up the door for live streaming to the iPhone using just Safari.</p>
<h3>Step 2: Set up modified segmenter</h3>
<p>To handle the live stream I had to modify the segmenter in two ways. The first was required to bypass an issue with the input coming in as a pipe. For those interested here is the modified section of code:</p>
<div class="codesnip-container" >
<div class="c codesnip" style="font-family:monospace;">ret <span class="sy0">=</span> av_write_frame<span class="br0">&#40;</span>output_context<span class="sy0">,</span> <span class="sy0">&amp;</span>packet<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>ret <span class="sy0">&lt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;Could not write frame of stream: %d<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> ret<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; av_free_packet<span class="br0">&#40;</span><span class="sy0">&amp;</span>packet<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="co1">//break; ****** removed for streaming support *****</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>ret <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;End of stream requested<span class="es1">\n</span>&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; av_free_packet<span class="br0">&#40;</span><span class="sy0">&amp;</span>packet<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw2">break</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</div>
<p><br/></p>
<p>The second modification was more extensive. The original segmenter wrote the index file out for each segment as the segment ended. Instead of writing the index to disk I need the index information to push to S3 as well as to know when the segment itself is ready to be pushed. I could have used a <a href="http://libs3.ischo.com/index.html">S3 library</a> and stuck everything into the C code but instead I decided that it made more sense to save the stream to disk then push the index information to another process.</p>
<p>I do the transfer of information over a TCP socket connection from the segmenter to the upload process. A side effect of doing this is that it will allow for the upload process to take input from multiple transcode and segmenters at the same time. This should make for easy variable rate configurations where the transcoding can take advantage of multiple machines.</p>
<p>Here is the modified section of code:</p>
<div class="codesnip-container" >
<div class="c codesnip" style="font-family:monospace;"><span class="kw4">int</span> write_index_file<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw4">char</span> index<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">unsigned</span> <span class="kw4">int</span> segment_duration<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> output_directory<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> output_prefix<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> http_prefix<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">unsigned</span> <span class="kw4">int</span> first_segment<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">unsigned</span> <span class="kw4">int</span> last_segment<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">int</span> end<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> bucket_name<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> key_prefix<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw4">char</span> buffer<span class="br0">&#91;</span>1024 <span class="sy0">*</span> 10<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; memset<span class="br0">&#40;</span>buffer<span class="sy0">,</span> 0<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">char</span><span class="br0">&#41;</span> <span class="sy0">*</span> 1024 <span class="sy0">*</span> 10<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; sprintf<span class="br0">&#40;</span>buffer<span class="sy0">,</span> <span class="st0">&quot;%s, %s, %d, %s, %s, %d, %d, %d, %s, %s&quot;</span><span class="sy0">,</span> index<span class="sy0">,</span> output_directory<span class="sy0">,</span> segment_duration<span class="sy0">,</span> output_prefix<span class="sy0">,</span> http_prefix<span class="sy0">,</span> first_segment<span class="sy0">,</span> last_segment<span class="sy0">,</span> end<span class="sy0">,</span> bucket_name<span class="sy0">,</span> key_prefix<span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;Sending: %s<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> buffer<span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw4">int</span> sock<span class="sy0">;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>sock <span class="sy0">=</span> socket<span class="br0">&#40;</span>PF_INET<span class="sy0">,</span> SOCK_STREAM<span class="sy0">,</span> IPPROTO_TCP<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">&lt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;Could not open socket.&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="sy0">-</span><span class="nu0">1</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>serverIP <span class="sy0">=</span> <span class="st0">&quot;127.0.0.1&quot;</span><span class="sy0">;</span> &nbsp;<br />
&nbsp; <span class="kw4">int</span> serverPort <span class="sy0">=</span> <span class="nu0">10234</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw4">struct</span> sockaddr_in serverAddress<span class="sy0">;</span><br />
&nbsp; memset<span class="br0">&#40;</span><span class="sy0">&amp;</span>serverAddress<span class="sy0">,</span> 0<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span>serverAddress<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; serverAddress.<span class="me1">sin_family</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> AF_INET<span class="sy0">;</span><br />
&nbsp; serverAddress.<span class="me1">sin_addr</span>.<span class="me1">s_addr</span> <span class="sy0">=</span> inet_addr<span class="br0">&#40;</span>serverIP<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; serverAddress.<span class="me1">sin_port</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> htons<span class="br0">&#40;</span>serverPort<span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>connect<span class="br0">&#40;</span>sock<span class="sy0">,</span> <span class="br0">&#40;</span><span class="kw4">struct</span> sockaddr <span class="sy0">*</span><span class="br0">&#41;</span> <span class="sy0">&amp;</span>serverAddress<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span>serverAddress<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">&lt;</span> <span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;Could not connect to socket.&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="sy0">-</span><span class="nu0">1</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="kw4">int</span> buffer_len <span class="sy0">=</span> strlen<span class="br0">&#40;</span>buffer<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>send<span class="br0">&#40;</span>sock<span class="sy0">,</span> buffer<span class="sy0">,</span> buffer_len<span class="sy0">,</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="sy0">!=</span> buffer_len<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr<span class="sy0">,</span> <span class="st0">&quot;Could not send command.&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="sy0">-</span><span class="nu0">1</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; close<span class="br0">&#40;</span>sock<span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></div>
</div>
<p> <br/></p>
<p>The above function assumes that the upload server lives on the same machine as the segmenter at this point. Also note that the command line arguments for the segmenter have grown to include a S3 bucket name and a S3 key prefix:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">Usage: live_upload &lt;input MPEG-TS file&gt; &lt;segment duration in seconds&gt; &lt;output directory&gt; &lt;output MPEG-TS file prefix&gt; &lt;output m3u8 index file&gt; &lt;http prefix&gt; &lt;bucket name&gt; &lt;key prefix&gt;</div>
</div>
<ul>
<li>input MPEG-TS file &#8211; For the live streaming use you want this to be a pipe so it should be -</li>
<li>segment duration in seconds &#8211; How long to make each segment of video</li>
<li>output directory &#8211; Where the video segments live before they are transfered</li>
<li>output MPEG-TS file prefix &#8211; The prefix of the video file</li>
<li>output m3u8 index file &#8211; The name of the m3u8 index file</li>
<li>http prefix &#8211; The prefix of the URL where the segments are ultimately located</li>
<li>bucket name &#8211; The S3 bucket name that the segments and index will be stored in</li>
<li>key prefix &#8211; The S3 key that the segments and index should be prefixed with</li>
</ul>
<p>Download the <a href="http://www.ioncannon.net/examples/httplive/Makefile">Makefile</a> and <a href="http://www.ioncannon.net/examples/httplive/live_upload.c">segmenter source</a> and compile if you want to follow step 4.</p>
<h3>Step 3: Transfer the segments</h3>
<p>The modified segmenter is now ready to push the index information to a process that will in turn upload the index as well as the stream segment. In this case I chose to push the segments and index to S3 but that isn&#039;t the only option possible. Furthermore I&#039;ve stuck Cloudfront in front of the segments so they can be cached closer to the destination. Letting the index files be cached by Cloudfront could be done but care would need to be taken to make sure the index isn&#039;t cached for longer than the segment duration.</p>
<p>For the upload server I&#039;m using Ruby and the <a href="http://rightscale.rubyforge.org/right_aws_gem_doc/">Rightscale AWS gem</a> to push the segments and the index files to S3. Here is the complete code to do the upload server (this file is called s3server.rb in the git repository):</p>
<div class="codesnip-container" >
<div class="ruby codesnip" style="font-family:monospace;"><span class="kw3">require</span> <span class="st0">&#039;thread&#039;</span><br />
<span class="kw3">require</span> <span class="st0">&#039;socket&#039;</span><br />
<span class="kw3">require</span> <span class="st0">&#039;ftools&#039;</span><br />
<span class="kw3">require</span> <span class="st0">&#039;rubygems&#039;</span><br />
<span class="kw3">require</span> <span class="st0">&#039;right_aws&#039;</span></p>
<p>AWS_S3_ID=<span class="st0">&quot;your s3 id&quot;</span><br />
AWS_S3_KEY=<span class="st0">&quot;your s3 private key&quot;</span></p>
<p><span class="kw1">def</span> create_index<span class="br0">&#40;</span>segment_duration, output_prefix, http_prefix, first_segment, last_segment, stream_end<span class="br0">&#41;</span><br />
&nbsp; <span class="kw4">File</span>.<span class="kw3">open</span><span class="br0">&#40;</span><span class="st0">&quot;tmp.index.m3u8&quot;</span>, <span class="st0">&#039;w&#039;</span><span class="br0">&#41;</span> <span class="kw1">do</span> <span class="sy0">|</span>index_file<span class="sy0">|</span> <br />
&nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#EXTM3U<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#EXT-X-TARGETDURATION:#{segment_duration}<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#EXT-X-MEDIA-SEQUENCE:#{last_segment &gt;= 5 ? last_segment-4 : 1}<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; first_segment.<span class="me1">upto</span><span class="br0">&#40;</span>last_segment<span class="br0">&#41;</span> <span class="kw1">do</span> <span class="sy0">|</span> segment_index <span class="sy0">|</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> segment_index <span class="sy0">&gt;</span> last_segment <span class="sy0">-</span> <span class="nu0">5</span><br />
&nbsp; &nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#EXTINF:#{segment_duration}<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#{http_prefix}#{output_prefix}-%05u.ts<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> segment_index<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; index_file.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;#EXT-X-ENDLIST&quot;</span><span class="br0">&#41;</span> <span class="kw1">if</span> stream_end<br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> push_to_s3<span class="br0">&#40;</span>index, output_directory, bucket_name, key_prefix, output_prefix, last_segment<span class="br0">&#41;</span><br />
&nbsp; s3 = <span class="re2">RightAws::S3Interface</span>.<span class="me1">new</span><span class="br0">&#40;</span>AWS_S3_ID, AWS_S3_KEY<span class="br0">&#41;</span></p>
<p>&nbsp; video_filename = <span class="st0">&quot;#{output_directory}/#{output_prefix}-%05u.ts&quot;</span> <span class="sy0">%</span> last_segment<br />
&nbsp; <span class="kw3">puts</span> <span class="st0">&quot;Pushing #{video_filename} to s3://#{bucket_name}/#{key_prefix}/#{output_prefix}-%05u.ts&quot;</span> <span class="sy0">%</span> last_segment<br />
&nbsp; s3.<span class="me1">put</span><span class="br0">&#40;</span>bucket_name, <span class="st0">&quot;#{key_prefix}/#{output_prefix}-%05u.ts&quot;</span> <span class="sy0">%</span> last_segment, <span class="kw4">File</span>.<span class="kw3">open</span><span class="br0">&#40;</span>video_filename<span class="br0">&#41;</span>, <span class="br0">&#123;</span><span class="st0">&#039;x-amz-acl&#039;</span> <span class="sy0">=&gt;</span> <span class="st0">&#039;public-read&#039;</span>, <span class="st0">&#039;content-type&#039;</span> <span class="sy0">=&gt;</span> <span class="st0">&#039;video/MP2T&#039;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> <span class="st0">&quot;Done pushing video file&quot;</span><br />
&nbsp; <br />
&nbsp; <span class="kw3">puts</span> <span class="st0">&quot;Pushing tmp.index.m3u8 to s3://#{bucket_name}/#{key_prefix}/#{index}&quot;</span><br />
&nbsp; s3.<span class="me1">put</span><span class="br0">&#40;</span>bucket_name, key_prefix <span class="sy0">+</span> <span class="st0">&quot;/&quot;</span> <span class="sy0">+</span> index, <span class="kw4">File</span>.<span class="kw3">open</span><span class="br0">&#40;</span><span class="st0">&quot;tmp.index.m3u8&quot;</span><span class="br0">&#41;</span>, <span class="br0">&#123;</span><span class="st0">&#039;x-amz-acl&#039;</span> <span class="sy0">=&gt;</span> <span class="st0">&#039;public-read&#039;</span>, <span class="st0">&#039;content-type&#039;</span> <span class="sy0">=&gt;</span> <span class="st0">&#039;video/MP2T&#039;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> <span class="st0">&quot;Done pushing index file&quot;</span><br />
<span class="kw1">end</span></p>
<p>queue = <span class="kw4">Queue</span>.<span class="me1">new</span></p>
<p>server_thread = <span class="kw4">Thread</span>.<span class="me1">new</span> <span class="kw1">do</span><br />
&nbsp; server = TCPServer.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&#039;0.0.0.0&#039;</span>, 10234<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>session = server.<span class="me1">accept</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; input = session.<span class="kw3">gets</span><br />
&nbsp; &nbsp; queue <span class="sy0">&lt;&lt;</span> input<br />
&nbsp; &nbsp; session.<span class="me1">close</span><br />
&nbsp; <span class="kw1">end</span> &nbsp; &nbsp;<br />
<span class="kw1">end</span></p>
<p>upload_thread = <span class="kw4">Thread</span>.<span class="me1">new</span> <span class="kw1">do</span><br />
&nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>value = queue.<span class="me1">pop</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>index, output_directory, segment_duration, output_prefix, http_prefix, first_segment, last_segment, stream_end, bucket_name, key_prefix<span class="br0">&#41;</span> = value.<span class="me1">strip</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="sy0">%</span>r<span class="br0">&#123;</span>,\s<span class="sy0">*</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> last_segment.<span class="me1">to_i</span> <span class="sy0">&gt;</span> 0<br />
&nbsp; &nbsp; &nbsp; create_index<span class="br0">&#40;</span>segment_duration.<span class="me1">to_i</span>, output_prefix, http_prefix, first_segment.<span class="me1">to_i</span>, last_segment.<span class="me1">to_i</span>, stream_end.<span class="me1">to_i</span> == 1<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; push_to_s3<span class="br0">&#40;</span>index, output_directory, bucket_name, key_prefix, output_prefix, last_segment.<span class="me1">to_i</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>server_thread.<span class="me1">join</span></div>
</div>
<p><br/></p>
<p>To use the above code you will need to put your S3 credentials in place for the values AWS_S3_ID and AWS_S3_KEY.</p>
<h3>Step 4: Test it</h3>
<ol>
<li>Set up a HTML file that points to the streaming index file:
<div class="codesnip-container" >
<div class="html4strict codesnip" style="font-family:monospace;"><span class="sc2">&lt;<a href="http://december.com/html/4/element/html.html"><span class="kw2">html</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/head.html"><span class="kw2">head</span></a>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/title.html"><span class="kw2">title</span></a>&gt;</span>Video Test<span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/title.html"><span class="kw2">title</span></a>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/meta.html"><span class="kw2">meta</span></a> <span class="kw3">name</span><span class="sy0">=</span><span class="st0">&quot;viewport&quot;</span> <span class="kw3">content</span><span class="sy0">=</span><span class="st0">&quot;width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;&quot;</span><span class="sy0">/</span>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/head.html"><span class="kw2">head</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/body.html"><span class="kw2">body</span></a> <span class="kw3">style</span><span class="sy0">=</span><span class="st0">&quot;background-color:#FFFFFF; &quot;</span>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/center.html"><span class="kw2">center</span></a>&gt;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sc2">&lt;video <span class="kw3">width</span><span class="sy0">=</span><span class="st0">&#039;150&#039;</span> <span class="kw3">height</span><span class="sy0">=</span><span class="st0">&#039;150&#039;</span> <span class="kw3">src</span><span class="sy0">=</span><span class="st0">&quot;http://s3.amazonaws.com/ionlivestream/stream0001/stream-128k.m3u8&quot;</span> <span class="sy0">/</span>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/center.html"><span class="kw2">center</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/body.html"><span class="kw2">body</span></a>&gt;</span><br />
<span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/html.html"><span class="kw2">html</span></a>&gt;</span></div>
</div>
<p>Note that the index file is coming from S3 directly and not from Cloudfront to keep it from being cached and a stale version being served. In case it helps, the format for the source index location in this example is: http://s3.amazonaws.com/&lt;bucket name>/&lt;key prefix>/&lt;index file>
</li>
<li>Start the upload script. Once started It will sit and wait for input from the segmenter:
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ruby s3server.rb</div>
</div>
<p>As requests are made the script will dump output to stdout describing what it is doing.
</li>
<li>Configure and start your source video stream. In my case I need to start the VLC to QuickTime Broadcaster connection for the iSight.
</li>
<li>Run FFMpeg against the source video stream and pipe the resulting transcoded output into the segmenter. For my stream I used the following command:
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ffmpeg -v 0 -i http://192.168.132.101:8080 -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s 320&#215;240 -vcodec libx264 -b 128k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 200k -maxrate 128k -bufsize 128k rc_eq &#039;blurCplx^(1-qComp)&#039; -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2 &#8211; | live_upload &#8211; 10 /tmp/ sample_128k stream-128k.m3u8 http://d3vmly3syseqo9.cloudfront.net/stream0001/ ionlivestream stream0001</div>
</div>
</li>
<li>Point safari on your iPhone to the HTML file created in #1 and hit play. After buffering starts you should see the live video with a segment or two delay.</li>
</ol>
<p><i>Other information</i></p>
<p>Again, this is just a proof of concept so there are a number of things lacking. Here are a list of enhancements I could imagine people would find useful:</p>
<ul>
<li>Variable bitrate segments</li>
<li>Easier and more flexible configuration</li>
<li>Use <a href="http://aws.amazon.com/ec2/">EC2</a> for encoding from one source stream</li>
<li>Pluggable transfer to a website using FTP or SCP instead of S3</li>
</ul>
<p>I thought some about the cost breakdown of doing this using S3 and Cloudfront. The following is a quick calculation on what it might cost for a variable rate stream using the cost of S3 and Cloudfront today.</p>
<p><b>Some assumptions:</b><br />
Everyone always finishes the entire stream when they start it.<br />
Variable rates: 128kbps, 256kbps, 364kbps<br />
Video length: 5 minute video<br />
Client streams: 100</p>
<p>30 segments for each stream (5 minutes = 300 seconds / 10 second intervals)</p>
<p>128kbps x 5 minutes = 4.8MB<br />
256kbps x 5 minutes = 9.6MB<br />
364kbps x 5 minutes = 13.65MB</p>
<p>% of each stream rate<br />
25% client streams @ 364kbps = 25 * 13.65MB = 341.25MB<br />
50% client streams @ 256kbps = 50 * 9.6MB = 480MB<br />
25% client streams @ 128kbps = 25 * 4.8MB = 128MB</p>
<p>3 stream index files + 1 variable index file<br />
30 segments * 3 streams = 90 index puts + 90 stream segment puts</p>
<p><b>S3 put cost:</b><br />
  $0.10 per GB – all data transfer in<br />
  $0.01 per 1,000 PUT, COPY, POST, or LIST requests</p>
<p>  $0.01 * (180/1000) = $0.0018 + $0.10 * (4.8+9.6+13.65)/1000 = $0.002805 = $0.004605</p>
<p><b>S3 storage cost:</b><br />
  $0.15 per GB – first 50 TB / month of storage used</p>
<p>  $0.15 * (4.8+9.6+13.65)/1000 = $0.0042075</p>
<p><b>S3 transfer cost:</b><br />
  $0.17 per GB – first 10 TB / month data transfer out<br />
  $0.01 per 10,000 GET and all other requests</p>
<p>  The index gets pulled from S3 every time, the streams come from Cloudfront<br />
  Assumes the transfer to Cloudfront happens 3 times per stream<br />
  $0.17 * (3 * (4.8+9.6+13.65)) / 1000 = $0.0143055<br />
  $0.01 * (100 * 30) + (30 * 3) / 10000 = $0.00309</p>
<p><b>Cloudfront transfer cost:</b><br />
  $0.17 per GB – first 10 TB / month data transfer out<br />
  $0.01 per 10,000 GET requests</p>
<p>  $0.17 * (341.25 + 480 + 128) / 1000 = $0.1613708<br />
  $0.01 * (30 * 100) / 10000 = $0.003</p>
<p><b>Rough total cost:</b> $0.1933833</p>
<p>So for 100 streams of 5 minutes worth of video you would be looking at something around 20 cents.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/475/iphone-windowed-http-live-streaming-using-amazon-s3-and-cloudfront-proof-of-concept/feed/</wfw:commentRss>
		<slash:comments>49</slash:comments>
		</item>
		<item>
		<title>iPhone HTTP Streaming with FFMpeg and an Open Source Segmenter</title>
		<link>http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/</link>
		<comments>http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/#comments</comments>
		<pubDate>Sun, 28 Jun 2009 23:43:45 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[streaming]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=452</guid>
		<description><![CDATA[With the release of the iPhone OS 3 update came the ability to do live streaming. There are a few types of streaming and each requires a certain encoding and segmentation. I&#039;ve put together a cheat sheet on how I went about building a static stream using FFMpeg and an example segmenter that someone has [...]]]></description>
			<content:encoded><![CDATA[<p>With the release of the iPhone OS 3 update came the ability to do live streaming. There are a few types of streaming and each requires a certain encoding and segmentation. I&#039;ve put together a cheat sheet on how I went about building a static stream using FFMpeg and an example segmenter that someone has posted. I&#039;m not covering windowed streams in this post but if you are thinking about implementing a windowed stream the following will help you make a step in that direction.</p>
<p>Before getting started it is best to read over the Apple documentation on HTTP live streaming. Start out with the <a href="http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/iPhoneStreamingMediaOverview/iPhoneStreamingMediaOverview.html#//apple_ref/doc/uid/TP40008332-CH100-SW4">iPhone streaming media overview</a>. This document covers the basics of how the streaming works and has some nice diagrams.</p>
<p>If you want even more information after reading the overview you can take a look at the <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming-01">HTTP Live streaming draft proposal</a> that was submitted to the IETF by Apple. It covers the streaming protocol in complete detail and has examples of the stream file format for reference.</p>
<p>Once you are ready to start grab a decent quality video clip to use. If you don&#039;t have one handy I found a nice list of downloadable <a href="http://www.highdefforum.com/high-definition-movies-video-clips/6537-official-hd-video-clip-list.html">HD clips</a> in various formats for testing.</p>
<p><span id="more-452"></span></p>
<h3>Step 1: Grab the latest version of FFMpeg</h3>
<p>You may be able to get away with anything after FFMpeg 0.5 but you might as well pull down a more recent version. The FFMpeg <a href="http://ffmpeg.org/download.html">download page</a> has instructions on getting the latest version. I pulled the version I used out of git.</p>
<p>I used the following command to configure FFMpeg:</p>
<pre>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">configure --enable-gpl --enable-nonfree --enable-pthreads --enable-libfaac --enable-libfaad --enable-libmp3lame --enable-libx264</div>
</div>
</pre>
<p>One of the main things to note is the &#45;-enable-libx264 flag.</p>
<h3>Step 2: Encode your video for the iPhone</h3>
<p>Once you have a working version of FFMpeg it is time to create an X264 encoded stream that will work with the iPhone. There are a few things to note before diving in:</p>
<ol>
<li>The supported bitrates for streaming are: 100 Kbps to 1.6 Mbps</li>
<li>The suggested bitrates for streaming are*:
<ul>
<li>Low &#8211; 96 Kbps video, 64 Kbps audio</li>
<li>Medium &#8211; 256 Kbps video, 64 Kbps audio</li>
<li>High &#8211; 800 Kbps video, 64 Kbps audio</li>
</ul>
</li>
<li>The iPhone screen size is: 480&#215;320</li>
</ol>
<p>* See step 7 for more information on what I think are better bitrates.</p>
<p>Taking all that into account someone on the iPhone developer forums suggested the following and it works well for me:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">ffmpeg -i &lt;in file&gt; -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s 320&#215;240 -vcodec libx264 -b 96k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 200k -maxrate 96k -bufsize 96k -rc_eq &#039;blurCplx^(1-qComp)&#039; -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2 &lt;output file&gt;</div>
</div>
<p>If you want some more detail on some of these commands check out the <a href="http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/">X264 encoding guide</a> and in general the <a href="http://ffmpeg.org/ffmpeg-doc.html">FFMpeg documentation</a> to see what all the flags mean.</p>
<p>Note that I have the bitrate set to 96k in the above example. That can be changed to fit your needs. Use the script that I have created later in the post or just make sure you change the -b, -maxrate, and -bufsize values.</p>
<h3>Step 3: Download and build the segmenter</h3>
<p>Now you have a complete video but you don&#039;t want to toss the entire thing up or you wouldn&#039;t be reading about HTTP streaming. What you need is a way to segment the video stream into smaller chunks. You can download Apple&#039;s segmenter (see the overview above for more information on where to find it) or you can download one created by the forum user <a href="https://devforums.apple.com/people/corp186">corp186</a>. </p>
<p>There is an SVN repository set up for the <a href="http://svn.assembla.com/svn/legend/segmenter/">segmenter source</a>. It is only a couple files and it is easy to build. The trouble you may run into is that the Makefile that it comes with won&#039;t build the binary correctly. Don&#039;t worry it just takes some extra link flags to make it work. The following is what I needed in the Makefile to get it to build on my system:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">all:<br />
&nbsp; &nbsp; &nbsp; &nbsp; gcc -Wall -g segmenter.c -o segmenter -lavformat -lavcodec -lavutil -lbz2 -lm -lz -lfaac -lmp3lame -lx264 -lfaad</p>
<p>clean:<br />
&nbsp; &nbsp; &nbsp; &nbsp; rm segmenter</div>
</div>
<p>After compiling the segmenter you are ready to create your first HTTP streaming content. </p>
<p>The format of the segmenter command is:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">segmenter &lt;input MPEG-TS file&gt; &lt;segment duration in seconds&gt; &lt;output MPEG-TS file prefix&gt; &lt;output m3u8 index file&gt; &lt;http prefix&gt;</div>
</div>
<p>Following is an example used to create a stream from a video file created with the above FFMpeg command split into 10 second intervals:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">segmenter sample_low.ts 10 sample_low stream_low.m3u8 http://www.ioncannon.net/</div>
</div>
<h3>Step 4: Prepare the HTTP server</h3>
<p>At this point you should have a set of files that represent the stream and a stream definition file. Those files can be uploaded to a web server at this point but there is another important step to take that ensures they will be download correctly and that is setting up mime types. There are two mime types that are important for the streaming content:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">.m3u8 application/x-mpegURL<br />
.ts video/MP2T</div>
</div>
<p>If you are using Apache you would want to add the following to your httpd.conf file:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">AddType application/x-mpegURL .m3u8<br />
AddType video/MP2T .ts</div>
</div>
<p>If you are using lighttpd you would want to put this in your configuration file (if you have other mime types defined make sure you just add these and don&#039;t set them):</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">mimetype.assign = ( &quot;.m3u8&quot; =&gt; &quot;application/x-mpegURL&quot;, &quot;.ts&quot; =&gt; &quot;video/MP2T&quot; )</div>
</div>
<h3>Step 5: Test the stream</h3>
<p>The video is encoded for the iPhone, segmented for streaming, and the server is configured. The only thing left to do is test the stream and the fastest way to do that is to use the new HTML5 video tag. Here is an example of how to set it up:</p>
<div class="codesnip-container" >
<div class="html4strict codesnip" style="font-family:monospace;"><span class="sc2">&lt;<a href="http://december.com/html/4/element/html.html"><span class="kw2">html</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/head.html"><span class="kw2">head</span></a>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/title.html"><span class="kw2">title</span></a>&gt;</span>Video Test<span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/title.html"><span class="kw2">title</span></a>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/meta.html"><span class="kw2">meta</span></a> <span class="kw3">name</span><span class="sy0">=</span><span class="st0">&quot;viewport&quot;</span> <span class="kw3">content</span><span class="sy0">=</span><span class="st0">&quot;width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;&quot;</span><span class="sy0">/</span>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/head.html"><span class="kw2">head</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/body.html"><span class="kw2">body</span></a> <span class="kw3">style</span><span class="sy0">=</span><span class="st0">&quot;background-color:#FFFFFF; &quot;</span>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<a href="http://december.com/html/4/element/center.html"><span class="kw2">center</span></a>&gt;</span><br />
&nbsp; &nbsp; &nbsp; <span class="sc2">&lt;video <span class="kw3">width</span><span class="sy0">=</span><span class="st0">&#039;150&#039;</span> <span class="kw3">height</span><span class="sy0">=</span><span class="st0">&#039;150&#039;</span> <span class="kw3">src</span><span class="sy0">=</span><span class="st0">&quot;stream-128k.m3u8&quot;</span> <span class="sy0">/</span>&gt;</span><br />
&nbsp; &nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/center.html"><span class="kw2">center</span></a>&gt;</span><br />
&nbsp; <span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/body.html"><span class="kw2">body</span></a>&gt;</span><br />
<span class="sc2">&lt;<span class="sy0">/</span><a href="http://december.com/html/4/element/html.html"><span class="kw2">html</span></a>&gt;</span></div>
</div>
<p> If everything has been done correctly you should see the video.</p>
<p>If you want to test the stream out in an application then <a href="http://developer.apple.com/iphone/library/samplecode/MoviePlayer_iPhone/index.html">download the MoviePlayer iPhone demo application</a> from the iPhone developer site. Build and run it in the simulator or put it on an actual phone and then type the URL in for the server you uploaded your stream to.</p>
<p>That is all there is to building a single static HTTP stream. A good number of steps but if you have some experience using FFMpeg it isn&#039;t too hard to set up. The only pitfalls I ran into revolve around trying to segment the stream without the segmeter code. I don&#039;t know enough about how the segmentation works to know why this is so difficult to do but I believe it could have something to do with synchronization points in the stream. Of course when you stray from the path the stream just doesn&#039;t work and you get a generic error message so that is just my best guess. I&#039;ll also guess that Apple may tighten up the player over time and make it work better with miss-formatted streams.</p>
<h3>Step 6: Automating the stream encoding and segmentation</h3>
<p>Here is a little script I put together that first encodes an input file and then segments it into 10 second chunks:</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="co0">#!/bin/sh</span></p>
<p><span class="re2">BR</span>=800k</p>
<p><span class="kw2">ffmpeg</span> <span class="re5">-i</span> $1 <span class="re5">-f</span> mpegts <span class="re5">-acodec</span> libmp3lame <span class="re5">-ar</span> 48000 <span class="re5">-ab</span> 64k <span class="re5">-s</span> 320&#215;240 <span class="re5">-vcodec</span> libx264 <span class="re5">-b</span> <span class="re1">$BR</span> <span class="re5">-flags</span> +loop <span class="re5">-cmp</span> +chroma <span class="re5">-partitions</span> +parti4x4+partp8x8+partb8x8 <span class="re5">-subq</span> 5 <span class="re5">-trellis</span> 1 <span class="re5">-refs</span> 1 <span class="re5">-coder</span> 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 <span class="re5">-bt</span> 200k <span class="re5">-maxrate</span> <span class="re1">$BR</span> <span class="re5">-bufsize</span> <span class="re1">$BR</span> -rc_eq <span class="st_h">&#039;blurCplx^(1-qComp)&#039;</span> <span class="re5">-qcomp</span> 0.6 <span class="re5">-qmin</span> 10 <span class="re5">-qmax</span> 51 <span class="re5">-qdiff</span> 4 <span class="re5">-level</span> 30 <span class="re5">-aspect</span> 320:240 <span class="re5">-g</span> 30 <span class="re5">-async</span> 2 sample_<span class="re1">$BR_pre</span>.ts</p>
<p>segmenter sample_<span class="re1">$BR_pre</span>.ts 10 sample_<span class="re1">$BR</span> stream-<span class="re1">$BR</span>.m3u8 http:<span class="sy0">//</span>www.ioncannon.net<span class="sy0">/</span></p>
<p><span class="kw2">rm</span> <span class="re5">-f</span> sample_<span class="re1">$BR_pre</span>.ts</div>
</div>
<p>The script could use some work but it does a good enough job for testing.</p>
<h3>Step 7: Create a variable rate HTTP stream</h3>
<p>Once you have creating a single stream down you need to try out creating a variable bitrate stream. There isn&#039;t much to it, just create different bitrate encoded streams and link to their stream definition files in a separate stream definition file. Here is an example:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">#EXTM3U<br />
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=96000<br />
http://192.168.132.15/ipv/stream-96k.m3u8<br />
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000<br />
http://192.168.132.15/ipv/stream-256k.m3u8<br />
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=800000</p>
<p>http://192.168.132.15/ipv/stream-800k.m3u8</p></div>
</div>
<p>I gave the above a try using both the iPhone&#039;s 3G connection and a WIFI connection. The following log shows the two different attempts (first 3G then WIFI):</p>
<pre>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">6.x.x.x ioncannon.net - [20:49:13] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 304 0 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:14] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 206 288 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:15] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 200 288 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:16] &quot;GET /stream-96k.m3u8 HTTP/1.1&quot; 200 719 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:18] &quot;GET /s_96k-00001.ts HTTP/1.1&quot; 200 334828 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:21] &quot;GET /s_96k-00002.ts HTTP/1.1&quot; 200 377880 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:30] &quot;GET /s_96k-00003.ts HTTP/1.1&quot; 200 383520 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:32] &quot;GET /stream-256k.m3u8 HTTP/1.1&quot; 200 730 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:39] &quot;GET /s_256k-00003.ts HTTP/1.1&quot; 200 716844 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:49] &quot;GET /s_256k-00004.ts HTTP/1.1&quot; 200 705564 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:57] &quot;GET /stream-96k.m3u8 HTTP/1.1&quot; 200 719 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:49:59] &quot;GET /s_96k-00004.ts HTTP/1.1&quot; 200 368668 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:03] &quot;GET /s_96k-00005.ts HTTP/1.1&quot; 200 371300 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:13] &quot;GET /s_96k-00006.ts HTTP/1.1&quot; 200 398936 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:16] &quot;GET /stream-256k.m3u8 HTTP/1.1&quot; 200 730 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:22] &quot;GET /s_256k-00006.ts HTTP/1.1&quot; 200 758016 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:36] &quot;GET /s_256k-00007.ts HTTP/1.1&quot; 200 737524 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:40] &quot;GET /s_256k-00008.ts HTTP/1.1&quot; 200 773244 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:46] &quot;GET /s_256k-00009.ts HTTP/1.1&quot; 200 717032 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:50:57] &quot;GET /s_256k-00010.ts HTTP/1.1&quot; 200 768920 &quot;-&quot; &quot;...&quot;
6.x.x.x ioncannon.net - [20:51:06] &quot;GET /s_256k-00011.ts HTTP/1.1&quot; 200 611000 &quot;-&quot; &quot;...&quot;

1.x.x.x ioncannon.net - [20:52:23] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 304 0 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:24] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 206 288 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:25] &quot;GET /varpl.m3u8 HTTP/1.1&quot; 200 288 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:25] &quot;GET /stream-96k.m3u8 HTTP/1.1&quot; 200 719 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:26] &quot;GET /s_96k-00001.ts HTTP/1.1&quot; 200 334828 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:27] &quot;GET /s_96k-00002.ts HTTP/1.1&quot; 200 377880 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:28] &quot;GET /stream-800k.m3u8 HTTP/1.1&quot; 200 730 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:31] &quot;GET /s_800k-00002.ts HTTP/1.1&quot; 200 1774156 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:34] &quot;GET /s_800k-00003.ts HTTP/1.1&quot; 200 1916096 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:38] &quot;GET /s_800k-00004.ts HTTP/1.1&quot; 200 1831872 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:41] &quot;GET /s_800k-00005.ts HTTP/1.1&quot; 200 1831496 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:46] &quot;GET /s_800k-00006.ts HTTP/1.1&quot; 200 1967608 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:50] &quot;GET /s_800k-00007.ts HTTP/1.1&quot; 200 1676208 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:54] &quot;GET /s_800k-00008.ts HTTP/1.1&quot; 200 2094132 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:52:58] &quot;GET /s_800k-00009.ts HTTP/1.1&quot; 200 1860260 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:53:08] &quot;GET /s_800k-00010.ts HTTP/1.1&quot; 200 2008404 &quot;-&quot; &quot;...&quot;
1.x.x.x ioncannon.net - [20:53:19] &quot;GET /s_800k-00011.ts HTTP/1.1&quot; 200 1400224 &quot;-&quot; &quot;...&quot;</div>
</div>
</pre>
<p>Notice that there is a decent bit of indecisiveness on the part of what stream to pick when using 3G. For my test it actually caused the player to pause while it switched from the 256k stream back to the 96k stream. The stream on the WIFI connection starts out low but then jumps right to the highest quality and stays there. Overall it seems like the variable rate streaming works decently and again Apple may be able to tweak it down the road to get even better results.</p>
<p>The bitrate jump between 96k and 256k is probably too large even though that is what Apple seems to recommend. I believe with some testing a better set of bitrates could be found. The video quality of the 256k bitrate looks pretty good so I would say that 96k, 128k and 384k would potentially be a better choice.</p>
<p><i>Some parting notes</i></p>
<p>If you are interested in how the segmenter works you can find out more on how to use libavformat at the following resources: <a href="http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html">an older libavformat tutorial</a>, <a href="http://www.cryptosystem.org/archives/2006/03/libavcodec-libavformat-sample-code/">some sample libavformat code</a>, <a href="http://www.dranger.com/ffmpeg/">How to Write a Video Player in Less Than 1000 Lines</a>, and <a href="http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c.html">more sample libavformat code</a>.</p>
<p>The next step for this is to do a windowed live stream. I&#039;ve done a little experimenting so far and with a modified segmeter I can generate a live stream. I will need to heavily modify the segmeter to get a live windowed stream so it may take a little while to get it done. My intent of course will be to combine the modifications with something fun like S3 and cloudfront since I believe that would be a sweat combination.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/452/iphone-http-streaming-with-ffmpeg-and-an-open-source-segmenter/feed/</wfw:commentRss>
		<slash:comments>195</slash:comments>
		</item>
		<item>
		<title>How to create iPhone wireframes with Inkscape</title>
		<link>http://www.ioncannon.net/meta/313/how-to-create-iphone-wireframes-with-inkscape/</link>
		<comments>http://www.ioncannon.net/meta/313/how-to-create-iphone-wireframes-with-inkscape/#comments</comments>
		<pubDate>Thu, 16 Apr 2009 01:03:01 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[meta]]></category>
		<category><![CDATA[inkscape]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=313</guid>
		<description><![CDATA[While developing ideas for iPhone applications I&#039;ve played around with just using Interface Builder to stub things out. This works reasonably well but I know how to use Interface Builder so that makes a difference. If you are designing an application and want to stick with graphical tools only you are in luck because Yahoo [...]]]></description>
			<content:encoded><![CDATA[<p>While developing ideas for iPhone applications I&#039;ve played around with just using Interface Builder to stub things out. This works reasonably well but I know how to use Interface Builder so that makes a difference. If you are designing an application and want to stick with graphical tools only you are in luck because Yahoo has produced a set of <a href="http://developer.yahoo.com/ypatterns/wireframes/">wireframing SVG stencils</a> for the iPhone that are very nice.</p>
<p><span id="more-313"></span></p>
<p>This is what the complete iPhone UI template set looks like:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/06/mobileiphoneall.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/06/mobileiphoneall-300x185.png" alt="Yahoo iPhone Template" title="Yahoo iPhone Template" width="300" height="185" class="alignnone size-medium wp-image-351" /></a></p>
<p>As you can see it has all of the standard UI elements of the iPhone.</p>
<p>Using the stencils is easy. Open the SVG file with Inkscape and then create a new empty project. Select the UI element you want and copy it into your project:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/06/step1screenshotexampl1.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/06/step1screenshotexampl1-300x176.png" alt="Example iPhone Screen Design" title="Example iPhone Screen Design" width="300" height="176" class="alignnone size-medium wp-image-355" /></a></p>
<p>Knowing some of the <a href="http://www.inkscape.org/doc/keys046.html">Inkscape shortcut keys</a> helps a lot in creating your wireframe. Being able to align and move elements in z-ordering is much easier if you know the correct shortcut keys to use.</p>
<p>Here are a few example images that I made using Inkscape for a demo application:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/06/screen3.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/06/screen3.png" alt="Example iPhone Screen with buttons" title="Example iPhone Screen with buttons" width="388" height="732" class="alignnone size-full wp-image-354" /></a></p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/06/screen2.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/06/screen2.png" alt="Example iPhone Screen with entry boxes" title="Example iPhone Screen with entry boxes" width="388" height="732" class="alignnone size-full wp-image-353" /></a></p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2009/06/screen1.png"><img src="http://www.ioncannon.net/wp-content/uploads/2009/06/screen1.png" alt="Example iPhone Screen with table" title="Example iPhone Screen with table" width="388" height="732" class="alignnone size-full wp-image-352" /></a></p>
<p>If you are new to wireframing it is worth reading the <a href="http://thinkvitamin.com/features/20-steps-to-better-wireframing/">20 steps to better wireframing</a> post by Think Vitamin. One of the 20 steps is not to worry about making your wireframes look exactly like the final product. If you need it to look perfect then using Interface Builder is probably going to work out better.</p>
<p>As a side note I wish Inkscape had a resource for stencils like <a href="http://www.omnigroup.com/applications/OmniGraffle/">OmniGraffle</a> does. There are any number of people who publish stencils on their blogs and there is even a site specifically for <a href="http://graffletopia.com/">OmniGraffle stencils</a>. I would add stencils to my list of <a href="http://www.ioncannon.net/utilities/123/10-tips-for-creating-good-looking-diagrams-using-inkscape/">tips for creating diagrams using Inkscape</a> if it existed. Right now it is seems like the best option is to look for SVG formatted stencils. The gstencil format doesn&#039;t seem too complicated so maybe someone will eventually find a way to convert the contents into SVG where they could then be used in Inkscape.</p>
<p>Update: If you have Adobe Fireworks or have money to spend on doing this you should check out this video on <a href="http://www.building43.com/videos/2009/06/23/mockup-iphone-app-adobe-fireworks/">building mockups for the iphone using Adobe Fireworks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/meta/313/how-to-create-iphone-wireframes-with-inkscape/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Converting videos (flv,wmv,avi,etc) into a format that will work with the iPhone/iTunes</title>
		<link>http://www.ioncannon.net/meta/210/converting-videos-flvwmvavietc-into-a-format-that-will-work-with-the-iphoneitunes/</link>
		<comments>http://www.ioncannon.net/meta/210/converting-videos-flvwmvavietc-into-a-format-that-will-work-with-the-iphoneitunes/#comments</comments>
		<pubDate>Sun, 28 Dec 2008 02:37:20 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[meta]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[ipod]]></category>
		<category><![CDATA[itunes]]></category>
		<category><![CDATA[mencoder]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[touch]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=210</guid>
		<description><![CDATA[I have had an iPhone for a while now and I keep running into instances where I want to have videos outside of youtube in some format that I can watch on the device. These include windows WMV formatted videos from PDC as well as FVL formatted videos on Flex. I finally broke down and [...]]]></description>
			<content:encoded><![CDATA[<p>I have had an iPhone for a while now and I keep running into instances where I want to have videos outside of youtube in some format that I can watch on the device. These include windows WMV formatted videos from PDC as well as FVL formatted videos on Flex. I finally broke down and found a working solution to convert pretty much any video into a iPhone/iTunes format using mencoder.</p>
<p><span id="more-210"></span></p>
<p> Here is the script I came up with to convert a video:</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="co0">#!/bin/sh</span><br />
&nbsp;<br />
<span class="re2">BN</span>=<span class="sy0">`</span><span class="kw2">basename</span> $1 .flv<span class="sy0">`</span><br />
&nbsp;<br />
mencoder $1 <span class="re5">-ofps</span> 23.976 <span class="re5">-ovc</span> lavc <span class="re5">-oac</span> copy <span class="re5">-o</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>test.<span class="re1">$BN</span>.avi<br />
&nbsp;<br />
mencoder <span class="sy0">/</span>tmp<span class="sy0">/</span>test.<span class="re1">$BN</span>.avi <span class="re5">-o</span> togo<span class="sy0">/</span><span class="re1">$BN</span>.mp4 <span class="re5">-vf</span> <span class="re2">scale</span>=480:-10,harddup <span class="re5">-lavfopts</span> <span class="re2">format</span>=mp4 <span class="re5">-faacopts</span> <span class="re2">mpeg</span>=4:<span class="re2">object</span>=2:raw:<span class="re2">br</span>=128 <span class="re5">-oac</span> faac <span class="re5">-ovc</span> x264 <span class="re5">-sws</span> 9 <span class="re5">-x264encopts</span> nocabac:<span class="re2">level_idc</span>=30:<span class="re2">bframes</span>=0:global_header:<span class="re2">threads</span>=auto:<span class="re2">subq</span>=5:<span class="re2">frameref</span>=6:<span class="re2">partitions</span>=all:<span class="re2">trellis</span>=1:chroma_me:<span class="re2">me</span>=umh:<span class="re2">bitrate</span>=500 <span class="re5">-of</span> lavf<br />
&nbsp;<br />
<span class="kw2">rm</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>test.<span class="re1">$BN</span>.avi</div>
</div>
<p>One thing to notice is that I have the file format hard coded in the basename argument. I did this because I didn&#039;t need the script to be that generic and I just change it when I move from one format to another. For some reason I found that converting the video with the first statement works best. Without that I had a number of audio sync issues as well as some outright conversion failures .</p>
<p>The options for the first command are</p>
<ul>
<li>-ofps output frames per second</li>
<li>-ovc lavc output codec is lavc</li>
<li>-oac copy output audio is a copy of the input</li>
<li>-o the output file</li>
</ul>
<p>The options for the second command are</p>
<ul>
<li>-o the output file</li>
<li>-vf scale=480:-10,harddup video filter that scales to 480 width and rounds the height to the closest 10/16th width as well as forcing duplicate frames to be encoded in the output </li>
<li>-lavfopts format=mp4 set the video format options to mp4</li>
<li>-faacopts mpeg=4:object=2:raw:br=128 set the audio options to mp4</li>
<li>-oac faac set the audio output format to faac</li>
<li>-ovc x264 set the output to x264 encoding</li>
<li>-sws 9 set the scaler options to lanczos</li>
<li>-x264encopts nocabac:level_idc=30:bframes=0:global_header:threads=auto:subq=5:frameref=6:partitions=all:trellis=1:chroma_me:me=umh:bitrate=500 sets the 265 enocoding options</li>
<li>-of lavf set the output video format</li>
</ul>
<p>After converting the video all you need to to do is drag it onto the iTunes Library bar and it will then import it and make it available to sync to your iPhone, touch or iPod. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/meta/210/converting-videos-flvwmvavietc-into-a-format-that-will-work-with-the-iphoneitunes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

