<?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>Saturnboy &#187; AIR</title>
	<atom:link href="http://saturnboy.com/tag/air/feed/" rel="self" type="application/rss+xml" />
	<link>http://saturnboy.com</link>
	<description>Code, Work, and Life</description>
	<lastBuildDate>Thu, 19 Aug 2010 03:28:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Metrics and the AIR Install Badge</title>
		<link>http://saturnboy.com/2010/05/metrics-air-install-badge/</link>
		<comments>http://saturnboy.com/2010/05/metrics-air-install-badge/#comments</comments>
		<pubDate>Wed, 19 May 2010 15:21:01 +0000</pubDate>
		<dc:creator>justin</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[AIR]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[flexmonkey]]></category>

		<guid isPermaLink="false">http://saturnboy.com/?p=1405</guid>
		<description><![CDATA[The AIR Install Badge is a very handy little flash application for delivering AIR applications to your users via the web. The badge allows your users to download and install both your application and the Adobe AIR runtime. Additionally, the install badge will automatically prompt users to upgrade if a previously installed version is detected. [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.adobe.com/devnet/air/articles/badge_for_air.html">AIR Install Badge</a> is a very handy little flash application for delivering AIR applications to your users via the web.  The badge allows your users to download and install both your application and the Adobe AIR runtime.  Additionally, the install badge will automatically prompt users to upgrade if a previously installed version is detected.  At <a href="http://www.gorillalogic.com/">Gorilla Logic</a>, we use the AIR Install Badge on the <a href="http://www.gorillalogic.com/flexmonkey/download">FlexMonkey download page</a> (free registration required).</p>
<p>Alas, flash is opaque to analytics.  We have no idea what our users are doing inside the AIR Install Badge application.  Are they installing? Or upgrading?  No problem, we just need to write some code&#8230;</p>
<h3>The Code</h3>
<p>Using flash&#8217;s <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html">ExternalInterface</a>, we can manually push the data out of flash and into javascript.  Once we have the data in javascript, we have total control.  One option is to <a href="http://blog.creoff.net/using-google-analytics-with-adobe-air-install-badge/">use google analytics to store our badge data</a>.  In the case of FlexMonkey, we send the badge data along with the user&#8217;s credentials to our CRM platform, <a href="http://www.salesforce.com/">SalesForce.com</a>.</p>
<p class="bottom"><b>Step 1:</b> First, open <code>AIRInstallBadge.as</code> and add this to the top:</p>

<div class="wp_syntax"><div class="code"><pre class="actionscript" style="font-family:monospace;"><span style="color: #0066CC;">import</span> flash.<span style="color: #006600;">external</span>.<span style="color: #006600;">ExternalInterface</span>;</pre></div></div>

<p class="bottom"><b>Step 2:</b> Next, add the <code>ExternalInterface</code> call to the top of the <code>handleActinClick()</code> function in <code>AIRInstallBadge.as</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="actionscript" style="font-family:monospace;">protected <span style="color: #000000; font-weight: bold;">function</span> handleActionClick<span style="color: #66cc66;">&#40;</span>evt:MouseEvent<span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span>action == <span style="color: #ff0000;">'install'</span> <span style="color: #66cc66;">||</span> action == <span style="color: #ff0000;">'upgrade'</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
        <span style="color: #808080; font-style: italic;">//send data to js</span>
        ExternalInterface.<span style="color: #0066CC;">call</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'badgeJS'</span>,action<span style="color: #66cc66;">&#41;</span>;
    <span style="color: #66cc66;">&#125;</span>
    ...
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>Since I only care about the <code>install</code> or <code>upgrade</code> actions, I&#8217;ll only send those out to javascript.  Re-compile the badge and deploy.</p>
<p class="bottom"><b>Step 3:</b> Last, add the <code>badgeJS()</code> javascript callback to the page containing the badge and do whatever you want with the incoming badge data:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> badgeJS<span style="color: #009900;">&#40;</span>action<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">//do metrics here...</span>
    <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'badge action='</span> <span style="color: #339933;">+</span> action<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>Conclusion</h3>
<p>With an hour of effort, and a very small amount of code, we&#8217;ve managed to get the useful metrics of installs and upgrades out of the AIR Install Badge and into our analytics engine of choice.  A job well done.</p>
]]></content:encoded>
			<wfw:commentRss>http://saturnboy.com/2010/05/metrics-air-install-badge/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open Video Player in AIR</title>
		<link>http://saturnboy.com/2009/04/open-video-player-air/</link>
		<comments>http://saturnboy.com/2009/04/open-video-player-air/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 05:09:57 +0000</pubDate>
		<dc:creator>justin</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[AIR]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[open video player]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://saturnboy.com/?p=413</guid>
		<description><![CDATA[Recently, I&#8217;ve been doing quite a bit of video work in Flex and AIR. My main gripe (that I&#8217;ll try to rectify below) is that there sure is a lot of info around the web about streaming, FMS, and friends, but almost none about playing local video. Even the Adobe Media Player, which is a [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, I&#8217;ve been doing quite a bit of video work in Flex and AIR.  My main gripe (that I&#8217;ll try to rectify below) is that there sure is a lot of info around the web about streaming, FMS, and friends, but almost none about playing local video.  Even the <a href="http://www.adobe.com/go/mp">Adobe Media Player</a>, which is a really nice AIR app, is all about streaming content from the web.</p>
<p>And like <a href="http://www.brooksandrus.com/blog/2008/11/05/3-years-later-netstream-still-sucks/">those before me</a>, I&#8217;ve come to the same conclusion about video in Flash: it&#8217;s pretty cool, but I sure wish it was better.  Enter the <a href="http://www.openvideoplayer.com/">Open Video Player</a> as the answer to some of <code>NetStream</code>&#8216;s woes.  Alas, all of the OVP docs and samples are, once again, all about streaming, Akamai, bandwidth, etc.</p>
<p>The elephant in the room for an AIR video player: What if I&#8217;m offline?</p>
<h5>The Easy Way: VideoDisplay</h5>
<p class="bottom">I gonna get started with the easiest possible local video player, which in Flex means using <code>VideoDisplay</code>.  Here&#8217;s a simple AIR app that allows you to open a FLV file and play it:</p>

<div class="wp_syntax"><div class="code"><pre class="mxml" style="font-family:monospace;"><span style="color: #000000;">&lt;?xml version=<span style="color: #ff0000;">&quot;1.0&quot;</span> encoding=<span style="color: #ff0000;">&quot;utf-8&quot;</span>?<span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:WindowedApplication</span> xmlns:mx=<span style="color: #ff0000;">&quot;http://www.adobe.com/2006/mxml&quot;</span></span>
<span style="color: #000000;">        width=<span style="color: #ff0000;">&quot;700&quot;</span> height=<span style="color: #ff0000;">&quot;500&quot;</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #339933;">&lt;mx:Script&gt;</span>
<span style="color: #339933;">        &lt;![CDATA[</span>
<span style="color: #339933;">            [Bindable] private var filename:String = '';</span>
&nbsp;
<span style="color: #339933;">            private function openVideo():void {</span>
<span style="color: #339933;">                if (vd.playing) {</span>
<span style="color: #339933;">                    vd.pause();</span>
<span style="color: #339933;">                    playPause.label = 'PLAY';</span>
<span style="color: #339933;">                }</span>
&nbsp;
<span style="color: #339933;">                var docs:File = File.documentsDirectory;</span>
<span style="color: #339933;">                docs.browseForOpen('Select a Video', [new FileFilter(&quot;Video&quot;, &quot;*.flv&quot;)]);</span>
<span style="color: #339933;">                docs.addEventListener(Event.SELECT, openFileHandler);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function openFileHandler(e:Event):void {</span>
<span style="color: #339933;">                var f:File = e.target as File;</span>
<span style="color: #339933;">                filename = f.name;</span>
&nbsp;
<span style="color: #339933;">                vd.source = 'file://' + f.nativePath;</span>
<span style="color: #339933;">                vd.play();</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function stopClick():void {</span>
<span style="color: #339933;">                vd.stop();</span>
<span style="color: #339933;">                playPause.label = 'PLAY';</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function toggleClick():void {</span>
<span style="color: #339933;">                if (vd.playing) {</span>
<span style="color: #339933;">                	vd.pause();</span>
<span style="color: #339933;">                	playPause.label = 'PLAY';</span>
<span style="color: #339933;">                } else {</span>
<span style="color: #339933;">                	vd.play();</span>
<span style="color: #339933;">                	playPause.label = 'PAUSE';</span>
<span style="color: #339933;">                } </span>
<span style="color: #339933;">            }</span>
<span style="color: #339933;">        ]]&gt;</span>
<span style="color: #339933;">    &lt;/mx:Script&gt;</span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:ApplicationControlBar</span> dock=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;Open Video&quot;</span> click=<span style="color: #ff0000;">&quot;openVideo()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> text=<span style="color: #ff0000;">&quot;{filename}&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:ApplicationControlBar</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Canvas</span> width=<span style="color: #ff0000;">&quot;100%&quot;</span> height=<span style="color: #ff0000;">&quot;100%&quot;</span> backgroundColor=<span style="color: #ff0000;">&quot;#000000&quot;</span> backgroundAlpha=<span style="color: #ff0000;">&quot;0.1&quot;</span><span style="color: #7400FF;">&gt;</span></span>
    	<span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:VideoDisplay</span> id=<span style="color: #ff0000;">&quot;vd&quot;</span> autoPlay=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:Canvas</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> id=<span style="color: #ff0000;">&quot;playPause&quot;</span> label=<span style="color: #ff0000;">&quot;PAUSE&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;toggleClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;STOP&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;stopClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:WindowedApplication</span><span style="color: #7400FF;">&gt;</span></span></pre></div></div>

<p>The only real magic is the <code>vd.source = 'file://' + f.nativePath</code> line.  Apparently, <code>source</code> wants a URL even for a local file, so just prepend <code>file://</code> and everything works fine.</p>
<h5>Getting Fancy with NetStream</h5>
<p class="bottom">As far as I can tell, no one actually uses <code>VideoDisplay</code> at the core of a video player component in a production system.  The <i>many</i> benefits of <code>NetStream</code> are not really clear to me, but it is the darling of production systems.  Thankfully, despite its name, <code>NetStream</code> is perfectly happy playing local video.  Rebuilding the above example yields:</p>

<div class="wp_syntax"><div class="code"><pre class="mxml" style="font-family:monospace;"><span style="color: #000000;">&lt;?xml version=<span style="color: #ff0000;">&quot;1.0&quot;</span> encoding=<span style="color: #ff0000;">&quot;utf-8&quot;</span>?<span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:WindowedApplication</span> xmlns:mx=<span style="color: #ff0000;">&quot;http://www.adobe.com/2006/mxml&quot;</span></span>
<span style="color: #000000;">        width=<span style="color: #ff0000;">&quot;700&quot;</span> height=<span style="color: #ff0000;">&quot;500&quot;</span></span>
<span style="color: #000000;">        applicationComplete=<span style="color: #ff0000;">&quot;complete()&quot;</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #339933;">&lt;mx:Script&gt;</span>
<span style="color: #339933;">        &lt;![CDATA[</span>
<span style="color: #339933;">            [Bindable] private var filename:String = '';</span>
<span style="color: #339933;">            private var nc:NetConnection;</span>
<span style="color: #339933;">            private var ns:NetStream;</span>
<span style="color: #339933;">            private var vid:Video;</span>
<span style="color: #339933;">            private var t:Timer;</span>
&nbsp;
<span style="color: #339933;">            private function complete():void {</span>
<span style="color: #339933;">                nc = new NetConnection();</span>
<span style="color: #339933;">                nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler);</span>
<span style="color: #339933;">                nc.addEventListener(NetStatusEvent.NET_STATUS, connStatusHandler);</span>
<span style="color: #339933;">                nc.connect(null);</span>
&nbsp;
<span style="color: #339933;">                ns = new NetStream(nc); </span>
<span style="color: #339933;">                ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler);</span>
<span style="color: #339933;">                ns.addEventListener(NetStatusEvent.NET_STATUS, streamStatusHandler);</span>
<span style="color: #339933;">                ns.client = { onMetaData:streamMetadataHandler };</span>
&nbsp;
<span style="color: #339933;">                t = new Timer(300);</span>
<span style="color: #339933;">                t.addEventListener(TimerEvent.TIMER, streamProgressHandler);</span>
&nbsp;
<span style="color: #339933;">                vid = new Video();</span>
<span style="color: #339933;">                uic.addChild(vid);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function openVideo():void {</span>
<span style="color: #339933;">                if (ns) {</span>
<span style="color: #339933;">                    ns.pause();</span>
<span style="color: #339933;">                    playPause.label = 'PLAY';</span>
<span style="color: #339933;">                }</span>
&nbsp;
<span style="color: #339933;">                var docs:File = File.documentsDirectory;</span>
<span style="color: #339933;">                docs.addEventListener(Event.SELECT, openFileHandler);</span>
<span style="color: #339933;">                docs.browseForOpen('Select a Video', [new FileFilter(&quot;Video&quot;, &quot;*.flv&quot;)]);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function openFileHandler(e:Event):void {</span>
<span style="color: #339933;">                var f:File = e.target as File;</span>
<span style="color: #339933;">                filename = f.name;</span>
&nbsp;
<span style="color: #339933;">                ns.play('file://' + f.nativePath);</span>
<span style="color: #339933;">                playPause.label = 'PAUSE';</span>
&nbsp;
<span style="color: #339933;">                vid.attachNetStream(ns);</span>
&nbsp;
<span style="color: #339933;">                t.start();</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function errorHandler(e:AsyncErrorEvent):void {</span>
<span style="color: #339933;">                trace('ERROR: ' + e.text);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function connStatusHandler(e:NetStatusEvent):void {</span>
<span style="color: #339933;">                trace('CONN_STATUS: ' + e.info.code);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamStatusHandler(e:NetStatusEvent):void {</span>
<span style="color: #339933;">                trace('STREAM_STATUS: ' + e.info.code);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamMetadataHandler(info:Object):void {</span>
<span style="color: #339933;">                for (var key:String in info) {</span>
<span style="color: #339933;">                    trace(&quot;STREAM_METADATA: &quot; + key + &quot;=&quot; + info[key]);</span>
<span style="color: #339933;">                }</span>
<span style="color: #339933;">                uic.width = info.width;</span>
<span style="color: #339933;">                uic.height = info.height;</span>
&nbsp;
<span style="color: #339933;">                vid.width = info.width;</span>
<span style="color: #339933;">                vid.height = info.height;</span>
<span style="color: #339933;">                vid.x = 0;</span>
<span style="color: #339933;">                vid.y = 0;</span>
&nbsp;
<span style="color: #339933;">                len.text = parseFloat(info.duration).toFixed(1);    </span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamProgressHandler(e:TimerEvent):void {</span>
<span style="color: #339933;">                   timer.text = ns.time.toFixed(1);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function stopClick():void {</span>
<span style="color: #339933;">                ns.pause();</span>
<span style="color: #339933;">                ns.seek(0);</span>
<span style="color: #339933;">                playPause.label = 'PLAY';</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function toggleClick():void {</span>
<span style="color: #339933;">                ns.togglePause();</span>
<span style="color: #339933;">                playPause.label = (playPause.label == 'PAUSE' ? 'PLAY' : 'PAUSE');</span>
<span style="color: #339933;">            }</span>
<span style="color: #339933;">        ]]&gt;</span>
<span style="color: #339933;">    &lt;/mx:Script&gt;</span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:ApplicationControlBar</span> dock=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;Open Video&quot;</span> click=<span style="color: #ff0000;">&quot;openVideo()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> text=<span style="color: #ff0000;">&quot;{filename}&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> id=<span style="color: #ff0000;">&quot;timer&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> id=<span style="color: #ff0000;">&quot;len&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:ApplicationControlBar</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Canvas</span> width=<span style="color: #ff0000;">&quot;100%&quot;</span> height=<span style="color: #ff0000;">&quot;100%&quot;</span> backgroundColor=<span style="color: #ff0000;">&quot;#000000&quot;</span> backgroundAlpha=<span style="color: #ff0000;">&quot;0.1&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:UIComponent</span> id=<span style="color: #ff0000;">&quot;uic&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> height=<span style="color: #ff0000;">&quot;100&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:Canvas</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> id=<span style="color: #ff0000;">&quot;playPause&quot;</span> label=<span style="color: #ff0000;">&quot;PAUSE&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;toggleClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;STOP&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;stopClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:WindowedApplication</span><span style="color: #7400FF;">&gt;</span></span></pre></div></div>

<p><code>NetStream</code> is a wonder of engineering.  Basic usage is understandable, just feed in <code>null</code> to the <code>NetConnection</code> to switch to <i>progressive download mode</i>, and use the same <code>file://</code> trick above to play a local video.  But after that, things get weird fast.  First, the metadata handling to totally backwards: you have to create a dynamic object with a <code>onMetaData</code> property that maps to a callback function.  My guess would be that there is some legacy bullshit going on that breaks <code>addEventListener</code>.  Second, you can&#8217;t tell if a video is playing or not.  Third, the lack of progress event means you need to use a timer to track playhead time.  Add it up &ndash; <b>LAME</b>.</p>
<h5>Reaching for the Grail: Open Video Player</h5>
<p class="bottom">According to their website, it&#8217;s <a href="http://www.openvideoplayer.com/">Open Video Player</a> to the rescue.  And I&#8217;ll have to agree that it fixes many of <code>NetStream</code> issues.  Swaping in <code>OvpNetStream</code> yields:</p>

<div class="wp_syntax"><div class="code"><pre class="mxml" style="font-family:monospace;"><span style="color: #000000;">&lt;?xml version=<span style="color: #ff0000;">&quot;1.0&quot;</span> encoding=<span style="color: #ff0000;">&quot;utf-8&quot;</span>?<span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:WindowedApplication</span> xmlns:mx=<span style="color: #ff0000;">&quot;http://www.adobe.com/2006/mxml&quot;</span></span>
<span style="color: #000000;">        width=<span style="color: #ff0000;">&quot;700&quot;</span> height=<span style="color: #ff0000;">&quot;500&quot;</span></span>
<span style="color: #000000;">        applicationComplete=<span style="color: #ff0000;">&quot;complete()&quot;</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #339933;">&lt;mx:Script&gt;</span>
<span style="color: #339933;">        &lt;![CDATA[</span>
<span style="color: #339933;">            import org.openvideoplayer.net.OvpConnection;</span>
<span style="color: #339933;">            import org.openvideoplayer.net.OvpNetStream;</span>
<span style="color: #339933;">            import org.openvideoplayer.events.OvpEvent;</span>
&nbsp;
<span style="color: #339933;">            [Bindable] private var filename:String = '';</span>
<span style="color: #339933;">            private var nc:OvpConnection;</span>
<span style="color: #339933;">            private var ns:OvpNetStream;</span>
<span style="color: #339933;">            private var vid:Video;</span>
&nbsp;
<span style="color: #339933;">            private function complete():void {</span>
<span style="color: #339933;">                nc = new OvpConnection();</span>
<span style="color: #339933;">                nc.addEventListener(OvpEvent.ERROR, errorHandler);</span>
<span style="color: #339933;">                nc.addEventListener(NetStatusEvent.NET_STATUS, connStatusHandler);</span>
<span style="color: #339933;">                nc.connect(null);</span>
&nbsp;
<span style="color: #339933;">                ns = new OvpNetStream(nc);</span>
<span style="color: #339933;">                ns.addEventListener(OvpEvent.ERROR, errorHandler);</span>
<span style="color: #339933;">                ns.addEventListener(NetStatusEvent.NET_STATUS, streamStatusHandler);</span>
<span style="color: #339933;">                ns.addEventListener(OvpEvent.NETSTREAM_METADATA, streamMetadataHandler);</span>
<span style="color: #339933;">                ns.addEventListener(OvpEvent.PROGRESS, streamProgressHandler);</span>
&nbsp;
<span style="color: #339933;">                vid = new Video();</span>
<span style="color: #339933;">                uic.addChild(vid);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function openVideo():void {</span>
<span style="color: #339933;">                if (ns) {</span>
<span style="color: #339933;">                    ns.pause();</span>
<span style="color: #339933;">                    playPause.label = 'PLAY';</span>
<span style="color: #339933;">                }</span>
&nbsp;
<span style="color: #339933;">                var docs:File = File.documentsDirectory;</span>
<span style="color: #339933;">                docs.addEventListener(Event.SELECT, openFileHandler);</span>
<span style="color: #339933;">                docs.browseForOpen('Select a Video', [new FileFilter(&quot;Video&quot;, &quot;*.flv&quot;)]);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function openFileHandler(e:Event):void {</span>
<span style="color: #339933;">                var f:File = e.target as File;</span>
<span style="color: #339933;">                filename = f.name;</span>
&nbsp;
<span style="color: #339933;">                ns.play('file://' + f.nativePath);</span>
<span style="color: #339933;">                playPause.label = 'PAUSE';</span>
&nbsp;
<span style="color: #339933;">                vid.attachNetStream(ns);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function errorHandler(e:OvpEvent):void {</span>
<span style="color: #339933;">                trace('ERROR: ' + e.data.errorNumber + ': ' + e.data.errorDescription);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function connStatusHandler(e:NetStatusEvent):void {</span>
<span style="color: #339933;">                trace('CONN_STATUS: ' + e.info.code);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamStatusHandler(e:NetStatusEvent):void {</span>
<span style="color: #339933;">                trace('STREAM_STATUS: ' + e.info.code);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamMetadataHandler(e:OvpEvent):void {</span>
<span style="color: #339933;">                for (var key:String in e.data) {</span>
<span style="color: #339933;">                    trace(&quot;STREAM_METADATA: &quot; + key + &quot;=&quot; + e.data[key]);</span>
<span style="color: #339933;">                }</span>
<span style="color: #339933;">                uic.width = e.data.width;</span>
<span style="color: #339933;">                uic.height = e.data.height;</span>
&nbsp;
<span style="color: #339933;">                vid.width = e.data.width;</span>
<span style="color: #339933;">                vid.height = e.data.height;</span>
<span style="color: #339933;">                vid.x = 0;</span>
<span style="color: #339933;">                vid.y = 0;</span>
&nbsp;
<span style="color: #339933;">                len.text = nc.streamLengthAsTimeCode(e.data.duration);</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function streamProgressHandler(e:OvpEvent):void {</span>
<span style="color: #339933;">                   timer.text = ns.timeAsTimeCode;</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function stopClick():void {</span>
<span style="color: #339933;">                ns.pause();</span>
<span style="color: #339933;">                ns.seek(0);</span>
<span style="color: #339933;">                playPause.label = 'PLAY';</span>
<span style="color: #339933;">            }</span>
&nbsp;
<span style="color: #339933;">            private function toggleClick():void {</span>
<span style="color: #339933;">                ns.togglePause();</span>
<span style="color: #339933;">                playPause.label = (playPause.label == 'PAUSE' ? 'PLAY' : 'PAUSE');</span>
<span style="color: #339933;">            }</span>
<span style="color: #339933;">        ]]&gt;</span>
<span style="color: #339933;">    &lt;/mx:Script&gt;</span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:ApplicationControlBar</span> dock=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;Open Video&quot;</span> click=<span style="color: #ff0000;">&quot;openVideo()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> text=<span style="color: #ff0000;">&quot;{filename}&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> id=<span style="color: #ff0000;">&quot;timer&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Label</span> id=<span style="color: #ff0000;">&quot;len&quot;</span> fontSize=<span style="color: #ff0000;">&quot;14&quot;</span> fontWeight=<span style="color: #ff0000;">&quot;bold&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:ApplicationControlBar</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Canvas</span> width=<span style="color: #ff0000;">&quot;100%&quot;</span> height=<span style="color: #ff0000;">&quot;100%&quot;</span> backgroundColor=<span style="color: #ff0000;">&quot;#000000&quot;</span> backgroundAlpha=<span style="color: #ff0000;">&quot;0.1&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:UIComponent</span> id=<span style="color: #ff0000;">&quot;uic&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> height=<span style="color: #ff0000;">&quot;100&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:Canvas</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> id=<span style="color: #ff0000;">&quot;playPause&quot;</span> label=<span style="color: #ff0000;">&quot;PAUSE&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;toggleClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Button</span> label=<span style="color: #ff0000;">&quot;STOP&quot;</span> width=<span style="color: #ff0000;">&quot;100&quot;</span> click=<span style="color: #ff0000;">&quot;stopClick()&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:HBox</span><span style="color: #7400FF;">&gt;</span></span>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:WindowedApplication</span><span style="color: #7400FF;">&gt;</span></span></pre></div></div>

<p>It&#8217;s not a revolution because <code>OvpNetStream</code> just extends <code>NetStream</code>, but we instantly get standard events thrown for progress ticks and metadata, and also some pretty time formatting as a bonus.  The OVP also does about a hundred other things related to streaming, streaming servers, and Akamai.  That&#8217;s about all I know about video right now, so before I can post again I&#8217;ll need to learn something new&#8230;</p>
<h5>Files</h5>
<p class="bottom">Just tarballs (it&#8217;s an AIR app after all):</p>
<ul>
<li><a href="http://saturnboy.com/proj/test_vid/test_vid.tgz">test_vid.tgz</a> (using VideoDisplay)</li>
<li><a href="http://saturnboy.com/proj/test_vid/test_vid_stream.tgz">test_vid_stream.tgz</a> (using NetStream)</li>
<li><a href="http://saturnboy.com/proj/test_vid/test_vid_ovp.tgz">test_vid_ovp.tgz</a> (using Open Video Player)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://saturnboy.com/2009/04/open-video-player-air/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
