<?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; s3</title>
	<atom:link href="http://www.ioncannon.net/tag/s3/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>
		<item>
		<title>EC2 and NAT</title>
		<link>http://www.ioncannon.net/system-administration/125/ec2-and-nat/</link>
		<comments>http://www.ioncannon.net/system-administration/125/ec2-and-nat/#comments</comments>
		<pubDate>Tue, 03 Apr 2007 12:57:42 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[system administration]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[nat]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/s3/125/ec2-and-nat/</guid>
		<description><![CDATA[Amazon just added NAT to their EC2 service. It also sounds like they will be turning the old direct addressing scheme off soon. This is probably a step towards assigning static IPs to your hosts in EC2. It may even allow you to have EC2 instances with no external IP address at all. It makes [...]]]></description>
			<content:encoded><![CDATA[<p>Amazon just added NAT to their EC2 service. It also sounds like they will be turning the old direct addressing scheme off soon. This is probably a step towards assigning static IPs to your hosts in EC2. It may even allow you to have EC2 instances with no external IP address at all. It makes sense to not use an external IP when all you are doing is processing data from S3 and then sticking it back into S3. You can read more about the NAT change <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=682">here</a>.</p>
<p>Tags: <a href="http://technorati.com/tag/amazon" rel="tag">amazon</a>, <a href="http://technorati.com/tag/ec2" rel="tag">ec2</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/system-administration/125/ec2-and-nat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating your own FC6 instance for EC2</title>
		<link>http://www.ioncannon.net/system-administration/115/creating-your-own-fc6-instance-for-ec2/</link>
		<comments>http://www.ioncannon.net/system-administration/115/creating-your-own-fc6-instance-for-ec2/#comments</comments>
		<pubDate>Fri, 12 Jan 2007 15:59:18 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[system administration]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/system-administration/115/creating-your-own-fc6-instance-for-ec2/</guid>
		<description><![CDATA[I&#039;ve been playing around with the EC2 service at Amazon and figured I would document a little about how you create your own FC6 AMI. The Amazon documentation goes over everything you need to know about creating your own FC4 AMI and if you don&#039;t want to roll your own you can use one of [...]]]></description>
			<content:encoded><![CDATA[<p>I&#039;ve been playing around with the <a href="http://aws.amazon.com/ec2">EC2</a> service at Amazon and figured I would document a little about how you create your own FC6 AMI. The Amazon documentation goes over everything you need to know about <a href="http://docs.amazonwebservices.com/AmazonEC2/dg/2006-10-01/">creating your own FC4 AMI</a> and if you don&#039;t want to roll your own you can use one of the <a href="http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=101">public AMIs</a>. Amazon just started letting people publish their own AMIs on their site so you should expect to see more as time goes by.</p>
<p><span id="more-115"></span></p>
<p>The first step of course is to have an EC2 enabled account. If you haven&#039;t already signed up for one there are <a href="http://aws.typepad.com/aws/2007/01/more_ec2_beta_s.html">more beta openings</a> available (as of 01/10/07) so you may still be able to get one. You will also need to be signed up for S3. Once you do that it is helpful to read the <a href="http://docs.amazonwebservices.com/AmazonEC2/gsg/2006-10-01/">getting started guide</a> and try out a few of the public AMIs. Doing so will get you to get your keys set up for S3, EC2, and SSH. In the following I assume you have read and followed the getting started guide and have set up all the keys you will need for S3, EC2, and SSH.</p>
<h2>Creating your FC6 image</h2>
<p>Here are the steps you need to create your FC6 image. Two notes before getting started: 1) I am using an FC6 box to run the following commands on so your luck may vary with older system and 2) Some of these can be done as a non-root user but you might as well be root for all of them. </p>
<p>If you are in a hurry you may download all of the following steps in a <a href="http://d28nuaxr58rcpu.cloudfront.net/ec2fc6/createami.sh">single script</a> that will generate the custom bootable AMI.</p>
<p>1) Create the image file and initialize the filesystem on it (note that I&#039;m only making giving myself 1G of space for this install, if you think you will need more room you should create a larger file by changing the seek value):</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">dd if=/dev/zero of=fc6-i386.img bs=1M count=1 seek=1024<br />
/sbin/mke2fs -F -j fc6-i386.img</div>
</div>
<p>2) Mount the file with a loopback device:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">mount -o loop fc6-i386.img /mnt</div>
</div>
<p>3) Create base directories and device files:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">mkdir /mnt/dev<br />
mkdir /mnt/proc<br />
mkdir /mnt/etc<br />
for i in console null zero ; do /sbin/MAKEDEV -d /mnt/dev -x $i ; done</div>
</div>
<p>4) Create the initial fstab file:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">cat &lt;&lt;EOL &gt; /mnt/etc/fstab<br />
/dev/sda1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; / &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ext3 &nbsp; &nbsp;defaults 1 1<br />
none &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/dev/pts &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;devpts &nbsp;gid=5,mode=620 0 0<br />
none &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/dev/shm &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tmpfs &nbsp; defaults 0 0<br />
none &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/proc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; proc &nbsp; &nbsp;defaults 0 0<br />
none &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/sys &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sysfs &nbsp; defaults 0 0<br />
/dev/sda2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /mnt &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ext3 &nbsp; &nbsp;defaults 1 2<br />
/dev/sda3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; swap &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;swap &nbsp; &nbsp;defaults 0 0<br />
EOL</div>
</div>
<p>5) Mount the proc under the new root filesystem so yum will work correctly:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">mount -t proc none /mnt/proc</div>
</div>
<p>6) Create your a yum configuration file:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">cat &lt;&lt;EOL &gt; /tmp/yumec2.conf<br />
[main] <br />
cachedir=/var/cache/yum<br />
debuglevel=2<br />
logfile=/var/log/yum.log<br />
exclude=*-debuginfo<br />
gpgcheck=0<br />
obsoletes=1<br />
reposdir=/dev/null</p>
<p>[base] <br />
name=Fedora Core 6 &#8211; i386 &#8211; Base<br />
mirrorlist=http://fedora.redhat.com/download/mirrors/fedora-core-6 <br />
enabled=1</p>
<p>[updates-released]<br />
name=Fedora Core 6 &#8211; i386 &#8211; Released Updates<br />
mirrorlist=http://fedora.redhat.com/download/mirrors/updates-released-fc6<br />
enabled=1<br />
EOL</div>
</div>
<p>7) Run yum to install the base group of packages to your root filesystem (this may take some time but you should see it progress, I have had all kinds of trouble with yum in the past so if it hangs you may want to kill it and try again):</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">yum -c /tmp/yumec2.conf &#8211;installroot=/mnt -y groupinstall Base</div>
</div>
<p>8) Clean the yum cache:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">yum -c /tmp/yumec2.conf &#8211;installroot=/mnt -y clean packages</div>
</div>
<p>9) Move the TLS directory out of the way:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">mv /mnt/lib/tls /mnt/lib/tls-disabled</div>
</div>
<p>10) Modify the boot script to download your SSH key and stick it in root&#039;s directory:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">cat &lt;&lt;EOL &gt;&gt; /mnt/etc/rc.local<br />
if [ ! -d /root/.ssh ] ; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; mkdir -p /root/.ssh<br />
&nbsp; &nbsp; &nbsp; &nbsp; chmod 700 /root/.ssh<br />
fi<br />
# Fetch public key using HTTP<br />
curl http://169.254.169.254/1.0//meta-data/public-keys/0/openssl &gt; /tmp/my-key<br />
if [ $? -eq 0 ] ; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; cat /tmp/my-key &gt;&gt; /root/.ssh/authorized_keys<br />
&nbsp; &nbsp; &nbsp; &nbsp; chmod 600 /root/.ssh/authorized_keys<br />
&nbsp; &nbsp; &nbsp; &nbsp; rm /tmp/my-key<br />
fi<br />
# or fetch public key using the file in the ephemeral store:<br />
if [ -e /mnt/openssh_id.pub ] ; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; cat /mnt/openssh_id.pub &gt;&gt; /root/.ssh/authorized_keys<br />
&nbsp; &nbsp; &nbsp; &nbsp; chmod 600 /root/.ssh/authorized_keys<br />
fi<br />
EOL</div>
</div>
<p>11) Set sshd to allow remote root connections and now hang on DNS problems:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">cat &lt;&lt;EOL &gt;&gt; /mnt/etc/ssh/sshd_config<br />
UseDNS &nbsp;no<br />
PermitRootLogin without-password<br />
EOL</div>
</div>
<p>12) Create the networking scripts:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">cat &lt;&lt;EOL &gt; /mnt/etc/sysconfig/network<br />
NETWORKING=yes<br />
HOSTNAME=localhost.localdomain<br />
EOL</p>
<p>cat &lt;&lt;EOL &gt; /mnt/etc/sysconfig/network-scripts/ifcfg-eth0<br />
ONBOOT=yes<br />
DEVICE=eth0<br />
BOOTPROTO=dhcp<br />
EOL</div>
</div>
<p>13) Sync and umount your root filesystem:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">sync <br />
umount /mnt/proc<br />
umount /mnt</div>
</div>
<p>You have now created your very own bootable AMI. If you want to fiddle with it from this point you may continue to use the yum command as in the above examples or you can also remount the filesystem and chroot to it using a command like this:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">chroot /mnt /bin/sh</div>
</div>
<p>One thing to remember if you use chroot like this is that everything is local now. You will want to mount the proc filesystem and probably add entries to /etc/resolve.conf so any hostnames you try to resolve will work.</p>
<p>The next step is to get the AMI to S3 so that it can be booted.</p>
<h2>Bundling and Uploading your AMI</h2>
<p>Everything you need to know about bundling and uploading your custom AMI is in <a href="http://docs.amazonwebservices.com/AmazonEC2/dg/2006-10-01/">the developer documentation</a> under &#034;Working With AMIs&#034; then &#034;Bundling an AMI&#034;.</p>
<p>One key to remember here is that you need to start your instance with the -k option to allow the key to be copied into place. If you don&#039;t do that or specify the incorrect key name you will end up with an instance you can&#039;t log into.</p>
<p>Tags: <a href="http://technorati.com/tag/ec2" rel="tag">ec2</a>, <a href="http://technorati.com/tag/s3" rel="tag"> s3</a>, <a href="http://technorati.com/tag/fc6" rel="tag"> fc6</a>, <a href="http://technorati.com/tag/amazon" rel="tag"> amazon</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/system-administration/115/creating-your-own-fc6-instance-for-ec2/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Creating S3 URLs that expire using PHP</title>
		<link>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/</link>
		<comments>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/#comments</comments>
		<pubDate>Thu, 01 Jun 2006 16:50:24 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/uncategorized/21/creating-s3-urls-that-expire-using-php/</guid>
		<description><![CDATA[After reading this post on the S3 forum I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to [...]]]></description>
			<content:encoded><![CDATA[<p>After reading <a href="http://developer.amazonwebservices.com/connect/thread.jspa?threadID=10726&#38;tstart=0">this post on the S3 forum</a> I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to host objects that I wanted to protect with some outside scheme. The confusion on the forum seemed to be about the timestamps used to expire the URL. For PHP it is fairly easy.</p>
<p><span id="more-21"></span></p>
<p>To clear up the expiration time issue I think these two steps are needed:</p>
<p>1) Keep in mind that the HTTP header dates <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html">must</a> be in GMT.<br />
2) The PHP function <a href="http://us3.php.net/manual/en/function.time.php">time()</a> returns the seconds since the epoch January 1 1970 00:00:00 GMT). Notice here this is in GMT as well.<br />
3) The HTTP Date header you see in a response from an S3 server is the time on that server. The machine you use to sign your request should be synced with that time. I think a good guess is that all the Amazon servers are synced with the atomic clock.</p>
<p>There isn&#039;t much to securing a URL after you have that tucked away. Here is an example that will sign a URL so that it is valid for 60 seconds:</p>
<div class="codesnip-container" >
<div class="php codesnip" style="font-family:monospace;"><span class="kw2">&lt;?php</span></p>
<p><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">&#039;Crypt/HMAC.php&#039;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="kw1">echo</span> getS3Redirect<span class="br0">&#40;</span><span class="st0">&quot;/test.jpg&quot;</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span></p>
<p><span class="kw2">function</span> getS3Redirect<span class="br0">&#40;</span><span class="re0">$objectName</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="re0">$S3_URL</span> <span class="sy0">=</span> <span class="st0">&quot;http://s3.amazonaws.com&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$keyId</span> <span class="sy0">=</span> <span class="st0">&quot;your key&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$secretKey</span> <span class="sy0">=</span> <span class="st0">&quot;your secret&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$expires</span> <span class="sy0">=</span> <a href="http://www.php.net/time"><span class="kw3">time</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">60</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$bucketName</span> <span class="sy0">=</span> <span class="st0">&quot;/your bucket&quot;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="re0">$stringToSign</span> <span class="sy0">=</span> <span class="st0">&quot;GET<span class="es1">\n</span><span class="es1">\n</span><span class="es1">\n</span><span class="es4">$expires</span><span class="es1">\n</span><span class="es4">$bucketName</span><span class="es4">$objectName</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$hasher</span> <span class="sy0">=&amp;</span>amp<span class="sy0">;</span> <span class="kw2">new</span> Crypt_HMAC<span class="br0">&#40;</span><span class="re0">$secretKey</span><span class="sy0">,</span> <span class="st0">&quot;sha1&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$sig</span> <span class="sy0">=</span> <a href="http://www.php.net/urlencode"><span class="kw3">urlencode</span></a><span class="br0">&#40;</span>hex2b64<span class="br0">&#40;</span><span class="re0">$hasher</span><span class="sy0">-&amp;</span>gt<span class="sy0">;</span>hash<span class="br0">&#40;</span><span class="re0">$stringToSign</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">return</span> <span class="st0">&quot;<span class="es4">$S3_URL</span><span class="es4">$bucketName</span><span class="es4">$objectName</span>?AWSAccessKeyId=<span class="es4">$keyId</span>&amp;amp;Expires=<span class="es4">$expires</span>&amp;amp;Signature=<span class="es4">$sig</span>&quot;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="kw2">function</span> hex2b64<span class="br0">&#40;</span><span class="re0">$str</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$raw</span> <span class="sy0">=</span> <span class="st_h">&#034;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span><span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&amp;</span>lt<span class="sy0">;</span> <a href="http://www.php.net/strlen"><span class="kw3">strlen</span></a><span class="br0">&#40;</span><span class="re0">$str</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">+=</span>2<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$raw</span> <span class="sy0">.=</span> <a href="http://www.php.net/chr"><span class="kw3">chr</span></a><span class="br0">&#40;</span><a href="http://www.php.net/hexdec"><span class="kw3">hexdec</span></a><span class="br0">&#40;</span><a href="http://www.php.net/substr"><span class="kw3">substr</span></a><span class="br0">&#40;</span><span class="re0">$str</span><span class="sy0">,</span> <span class="re0">$i</span><span class="sy0">,</span> 2<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <a href="http://www.php.net/base64_encode"><span class="kw3">base64_encode</span></a><span class="br0">&#40;</span><span class="re0">$raw</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="sy1">?&gt;</span></div>
</div>
<p>The hex2b64 function was pulled from the amazon S3 PHP example library.
</p>
<p>Tags: <a href="http://technorati.com/tag/amazon+s3" rel="tag">amazon s3</a>, <a href="http://technorati.com/tag/s3" rel="tag"> s3</a>, <a href="http://technorati.com/tag/php" rel="tag"> php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

