<?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; cloudfront</title>
	<atom:link href="http://www.ioncannon.net/tag/cloudfront/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>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>
	</channel>
</rss>

