<?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; video</title>
	<atom:link href="http://saturnboy.com/tag/video/feed/" rel="self" type="application/rss+xml" />
	<link>http://saturnboy.com</link>
	<description>Code, Work, and Life</description>
	<lastBuildDate>Thu, 01 Mar 2012 22:35:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Degrafa Video Player, Part 2</title>
		<link>http://saturnboy.com/2009/06/degrafa-video-player-2/</link>
		<comments>http://saturnboy.com/2009/06/degrafa-video-player-2/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 10:00:57 +0000</pubDate>
		<dc:creator>justin</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[degrafa]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[inkscape]]></category>
		<category><![CDATA[open video player]]></category>
		<category><![CDATA[skinning]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://saturnboy.com/?p=503</guid>
		<description><![CDATA[The plan is simple, take the nice Degrafa-skinned components from Part 1 and assemble them into a video player powered by the OvpNetStream class from the Open Video Player project. Design I knew right away that the design was not going to have any right angles, but I also didn&#8217;t want to go with rounded [...]]]></description>
			<content:encoded><![CDATA[<p>The plan is simple, take the nice <a href="http://www.degrafa.org/">Degrafa</a>-skinned components from <a href="http://saturnboy.com/2009/05/degrafa-video-player-1/">Part 1</a> and assemble them into a video player powered by the <code>OvpNetStream</code> class from the <a href="http://www.openvideoplayer.com/">Open Video Player</a> project.</p>
<h5>Design</h5>
<p class="bottom">I knew right away that the design was not going to have any right angles, but I also didn&#8217;t want to go with rounded rectangles everywhere.  Modern TVs tend to have a lot of soft rounded edges, so I decided to go with a more vintage look.  So I fired up <a href="http://www.inkscape.org/">Inkscape</a> and got to work:</p>
<div class="span-14 top bottom" style="min-height:232px; text-align:center;">
<img src="http://saturnboy.com/wp-content/uploads/2009/06/tv.png" alt="tv" title="tv" width="176" height="222" />
</div>
<p class="bottom">Implementing the video player design above in Degrafa, the cabinet mapped to a <code>RoundedRectangle</code>, and the screen &#038; antenna became <code>Path</code>s.  You can read more about about translating SVG to Degrafa in my <a href="http://saturnboy.com/2009/06/inkscape-svg-degrafa-pathinkscape-svg-degrafa-path/">Inkscape SVG to Degrafa Path</a> article.  But for now, let&#8217;s focus on the resulting Degrafa code:</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:Application</span></span>
<span style="color: #000000;">        xmlns:mx=<span style="color: #ff0000;">&quot;http://www.adobe.com/2006/mxml&quot;</span></span>
<span style="color: #000000;">        xmlns:Degrafa=<span style="color: #ff0000;">&quot;http://www.degrafa.com/2007&quot;</span></span>
<span style="color: #000000;">        layout=<span style="color: #ff0000;">&quot;absolute&quot;</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:GeometryComposition</span> graphicsTarget=<span style="color: #ff0000;">&quot;{[box]}&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #808080; font-style: italic;">&lt;!-- Antenna --&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:Path</span> data=<span style="color: #ff0000;">&quot;...path data...&quot;</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:transform</span><span style="color: #7400FF;">&gt;</span></span>
                <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:TranslateTransform</span> x=<span style="color: #ff0000;">&quot;15&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:transform</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:Path</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
        <span style="color: #000000;"><span style="color: #808080; font-style: italic;">&lt;!-- TV cabinet --&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:RoundedRectangle</span> y=<span style="color: #ff0000;">&quot;144&quot;</span> width=<span style="color: #ff0000;">&quot;350&quot;</span> height=<span style="color: #ff0000;">&quot;300&quot;</span></span>
<span style="color: #000000;">                cornerRadius=<span style="color: #ff0000;">&quot;20&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
&nbsp;
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:fill</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:SolidFill</span> color=<span style="color: #ff0000;">&quot;#333333&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:fill</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:stroke</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:SolidStroke</span> color=<span style="color: #ff0000;">&quot;#FF00FF&quot;</span> weight=<span style="color: #ff0000;">&quot;4&quot;</span> alpha=<span style="color: #ff0000;">&quot;0.4&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:stroke</span><span style="color: #7400FF;">&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:GeometryComposition</span><span style="color: #7400FF;">&gt;</span></span>
&nbsp;
    <span style="color: #000000;"><span style="color: #808080; font-style: italic;">&lt;!-- TV Screen --&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:GeometryComposition</span> graphicsTarget=<span style="color: #ff0000;">&quot;{[tvscreen]}&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:Path</span> data=<span style="color: #ff0000;">&quot;...path data...&quot;</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:fill</span><span style="color: #7400FF;">&gt;</span></span>
                <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:SolidFill</span> color=<span style="color: #ff0000;">&quot;#FF99FF&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:fill</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:stroke</span><span style="color: #7400FF;">&gt;</span></span>
                <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:SolidStroke</span> color=<span style="color: #ff0000;">&quot;#FF00FF&quot;</span> weight=<span style="color: #ff0000;">&quot;2&quot;</span> alpha=<span style="color: #ff0000;">&quot;0.4&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:stroke</span><span style="color: #7400FF;">&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;Degrafa:filters</span><span style="color: #7400FF;">&gt;</span></span>
                <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:GlowFilter</span> color=<span style="color: #ff0000;">&quot;#EEEEEE&quot;</span> alpha=<span style="color: #ff0000;">&quot;0.2&quot;</span> blurX=<span style="color: #ff0000;">&quot;16&quot;</span> blurY=<span style="color: #ff0000;">&quot;16&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
            <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:filters</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:Path</span><span style="color: #7400FF;">&gt;</span></span>
    <span style="color: #000000;"><span style="color: #7400FF;">&lt;/Degrafa:GeometryComposition</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;350&quot;</span> height=<span style="color: #ff0000;">&quot;444&quot;</span></span>
<span style="color: #000000;">            horizontalCenter=<span style="color: #ff0000;">&quot;0&quot;</span> verticalCenter=<span style="color: #ff0000;">&quot;0&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Canvas</span> id=<span style="color: #ff0000;">&quot;box&quot;</span> <span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;"><span style="color: #7400FF;">&lt;mx:Canvas</span> x=<span style="color: #ff0000;">&quot;50&quot;</span> y=<span style="color: #ff0000;">&quot;174&quot;</span> id=<span style="color: #ff0000;">&quot;tvscreen&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>
<span style="color: #000000;"><span style="color: #7400FF;">&lt;/mx:Application</span><span style="color: #7400FF;">&gt;</span></span></pre></div></div>

<p>I ended up using a pair of <code>GeometryComposition</code>s to wrap my three shapes to help keep my fills, strokes, and filters organized.  It made sense to do it this way, but I won&#8217;t claim it&#8217;s the best way.  Throw the control bar on below the TV screen, and the design is done.</p>
<h5>Backend</h5>
<p>The backend is build on the <code>OvpNetStream</code> class provided by the Open Video Player project.  <code>OvpNetStream</code> extends <code>NetStream</code> and smooths out some of the rough edges as I <a href="http://saturnboy.com/2009/04/open-video-player-air/">discussed</a> previously.  Basically, it provides a sane interface (no need to construct a dynamic object with function callbacks) and useful events (like metadata and progress events).</p>
<p class="bottom">For this demo, we simply instantiate <code>OvpNetStream</code> on <code>creationComplete</code> and wire up all the event handlers:</p>

<div class="wp_syntax"><div class="code"><pre class="actionscript" style="font-family:monospace;"><span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> complete<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    nc = <span style="color: #000000; font-weight: bold;">new</span> OvpConnection<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
    nc.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>OvpEvent.<span style="color: #0066CC;">ERROR</span>, errorHandler<span style="color: #66cc66;">&#41;</span>;
    nc.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>NetStatusEvent.<span style="color: #006600;">NET_STATUS</span>, connStatusHandler<span style="color: #66cc66;">&#41;</span>;
    nc.<span style="color: #0066CC;">connect</span><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">null</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
    ns = <span style="color: #000000; font-weight: bold;">new</span> OvpNetStream<span style="color: #66cc66;">&#40;</span>nc<span style="color: #66cc66;">&#41;</span>;
    ns.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>OvpEvent.<span style="color: #0066CC;">ERROR</span>, errorHandler<span style="color: #66cc66;">&#41;</span>;
    ns.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>NetStatusEvent.<span style="color: #006600;">NET_STATUS</span>, streamStatusHandler<span style="color: #66cc66;">&#41;</span>;
    ns.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>OvpEvent.<span style="color: #006600;">NETSTREAM_METADATA</span>, streamMetadataHandler<span style="color: #66cc66;">&#41;</span>;
    ns.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>OvpEvent.<span style="color: #006600;">PROGRESS</span>, streamProgressHandler<span style="color: #66cc66;">&#41;</span>;
    ns.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>OvpEvent.<span style="color: #006600;">COMPLETE</span>, streamCompleteHandler<span style="color: #66cc66;">&#41;</span>;
&nbsp;
    vid = <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #0066CC;">Video</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
    vid.<span style="color: #006600;">attachNetStream</span><span style="color: #66cc66;">&#40;</span>ns<span style="color: #66cc66;">&#41;</span>;
    vidContainer.<span style="color: #006600;">addChild</span><span style="color: #66cc66;">&#40;</span>vid<span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>The most interesting events are the metadata and progress events.  The metadata event delivers the duration of the video and its size.  The progress event arrives periodically (theoretically every 100ms by default, but in reality I see them come in just a couple of times per second) and delivers the current video time.</p>
<h5>Control Bar</h5>
<p>The control bar consists of three components: a play-pause button, a scrubber, and a volume slider.  They were skinned using Degrafa in <a href="http://saturnboy.com/2009/05/degrafa-video-player-1/">Part 1</a>.  In order to control video playback, we need to wire the control bar components to the instance of <code>OvpNetStream</code> created above.</p>
<p class="bottom">Here are the event handlers for the three control bar components:</p>

<div class="wp_syntax"><div class="code"><pre class="actionscript" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">// PlayPause event handler</span>
<span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> playPauseClick<span style="color: #66cc66;">&#40;</span><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>first<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
        first = <span style="color: #000000; font-weight: bold;">false</span>;
        ns.<span style="color: #0066CC;">play</span><span style="color: #66cc66;">&#40;</span>filename<span style="color: #66cc66;">&#41;</span>;
    <span style="color: #66cc66;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #66cc66;">&#123;</span>
        ns.<span style="color: #006600;">togglePause</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
     <span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">// Scrub event handlers</span>
<span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> scrubPress<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    ns.<span style="color: #0066CC;">pause</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
    playPause.<span style="color: #006600;">selected</span> = <span style="color: #000000; font-weight: bold;">false</span>;
<span style="color: #66cc66;">&#125;</span>
<span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> scrubDrag<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    ns.<span style="color: #0066CC;">seek</span><span style="color: #66cc66;">&#40;</span>scrub.<span style="color: #006600;">value</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#125;</span>
<span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> scrubRelease<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    ns.<span style="color: #006600;">togglePause</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
    playPause.<span style="color: #006600;">selected</span> = <span style="color: #000000; font-weight: bold;">true</span>;
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">// Volume event handler</span>
<span style="color: #0066CC;">private</span> <span style="color: #000000; font-weight: bold;">function</span> volumeChange<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span>
    ns.<span style="color: #006600;">volume</span> = volume.<span style="color: #006600;">value</span>;
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>In the <code>playPauseHandler()</code>, the initial click calls <code>play()</code> which actually <i>loads</i> the video (and then starts playback), all subsequent clicks just toggle between play or pause.  For the scrubber handlers, I chose to break them up into three separate steps: on mouse down pause the video, on mouse up restart playback, and on drag attempt to seek to the to the new time.</p>
<p class="bottom">That&#8217;s it.  Here is the resulting Degrafa-skinned video player (view source enabled):</a></p>
<div id="flashcontent-degrafa-videoplayer">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<p>Click <b>Play</b> to start playing <a href="http://www.elephantsdream.org/">Elephants Dream</a> (which is the &#8220;world&#8217;s first open movie,&#8221; and pretty cool too).  Right away you&#8217;ll notice some visual issues because the dimensions of the video are unknown until the metadata arrives.  Also, scrubbing has some problems which I believe are related to cue points in progressive downloads.  Lastly, I didn&#8217;t implement any indicators for buffering or download progress, so you&#8217;ll need to be patient.  Since this is just a demo, I&#8217;ll have to leave fixing those bugs as an exercise for the reader.</p>
<h5>Files</h5>
<ul>
<li><a href="http://saturnboy.com/proj/degrafa_videoplayer/degrafa_videoplayer.html">Video Player</a> (<a href="http://saturnboy.com/proj/degrafa_videoplayer/srcview/degrafa_videoplayer.zip">download</a>)</li>
</ul>
<div>
<script type="text/javascript">
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_videoplayer/degrafa_videoplayer.swf', 'flashcontent-degrafa-videoplayer', '500', '460', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
</script></div>
]]></content:encoded>
			<wfw:commentRss>http://saturnboy.com/2009/06/degrafa-video-player-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Degrafa Video Player, Part 1</title>
		<link>http://saturnboy.com/2009/05/degrafa-video-player-1/</link>
		<comments>http://saturnboy.com/2009/05/degrafa-video-player-1/#comments</comments>
		<pubDate>Wed, 27 May 2009 05:42:47 +0000</pubDate>
		<dc:creator>justin</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[degrafa]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[open video player]]></category>
		<category><![CDATA[skinning]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://saturnboy.com/?p=472</guid>
		<description><![CDATA[I&#8217;m going to combine my earlier Degrafa skinning efforts with my more recent video work to create a Degrafa-skinned video player. In this post, I&#8217;ll build out all the components required for a video player control bar. And in Part 2, I&#8217;ll weld the control bar to an Open Video Player OvpNetStream backend. Button To [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m going to combine my earlier <a href="http://www.degrafa.org/">Degrafa</a> <a href="http://saturnboy.com/2009/03/degrafa-button-explorer/">skinning</a> <a href="http://saturnboy.com/2009/04/more-degrafa-skins/">efforts</a> with my more recent <a href="http://saturnboy.com/2009/04/open-video-player-air/">video work</a> to create a Degrafa-skinned video player.  In this post, I&#8217;ll build out all the components required for a video player control bar.  And in <a href="http://saturnboy.com/2009/06/degrafa-video-player-2/">Part 2</a>, I&#8217;ll weld the control bar to an <a href="http://www.openvideoplayer.com/">Open Video Player</a> OvpNetStream backend.</p>
<h5>Button</h5>
<p class="bottom">To get started, I built out a simple button skin with a slight shine on the top half.  I also added rounded corners to give the classic web 2.0 look-and-feel.  There&#8217;s really not much to look at:</p>
<div id="flashcontent-degrafa-videoplayer-button">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<h5>Play-Pause Button</h5>
<p class="bottom">I&#8217;m using a toggleable Button component as the base of my Play-Pause button.  By leveraging Degrafa&#8217;s stateful skins, I simply change the visibility of the Play and Pause geometry depending on the skin&#8217;s state.  Here&#8217;s a snippet from the <code>states</code> block of the skin:</p>

<div class="wp_syntax"><div class="code"><pre class="mxml" style="font-family:monospace;"><span style="color: #000000;">&lt;states<span style="color: #7400FF;">&gt;</span></span>
    <span style="color: #000000;">&lt;State name=<span style="color: #ff0000;">&quot;upSkin&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;">&lt;SetProperty target=<span style="color: #ff0000;">&quot;{rect}&quot;</span> name=<span style="color: #ff0000;">&quot;fill&quot;</span> value=<span style="color: #ff0000;">&quot;{upFill}&quot;</span><span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;">&lt;SetProperty target=<span style="color: #ff0000;">&quot;{play}&quot;</span> name=<span style="color: #ff0000;">&quot;visible&quot;</span> value=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;">&lt;/State<span style="color: #7400FF;">&gt;</span></span>
    <span style="color: #000000;">&lt;State name=<span style="color: #ff0000;">&quot;selectedUpSkin&quot;</span><span style="color: #7400FF;">&gt;</span></span>
        <span style="color: #000000;">&lt;SetProperty target=<span style="color: #ff0000;">&quot;{rect}&quot;</span> name=<span style="color: #ff0000;">&quot;fill&quot;</span> value=<span style="color: #ff0000;">&quot;{upFill}&quot;</span><span style="color: #7400FF;">/&gt;</span></span>
        <span style="color: #000000;">&lt;SetProperty target=<span style="color: #ff0000;">&quot;{play}&quot;</span> name=<span style="color: #ff0000;">&quot;visible&quot;</span> value=<span style="color: #ff0000;">&quot;false&quot;</span><span style="color: #7400FF;">/&gt;</span></span>
    <span style="color: #000000;">&lt;/State<span style="color: #7400FF;">&gt;</span></span>
    ...
<span style="color: #000000;">&lt;/states<span style="color: #7400FF;">&gt;</span></span></pre></div></div>

<p class="bottom">Additionally, the play arrow and pause bars are dynamically sized based on the button height, so they readily scale to any size.  The result:</p>
<div id="flashcontent-degrafa-videoplayer-playpause">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<h5>Scrubber</h5>
<p class="bottom">Getting total control over a Slider skin is not easy in Flex 3.  There are two issue that I find quite annoying: the thumb size is hard-coded into the class and the highlight skin is offset from the track.  Fortunately, the web is full of workarounds for the <a href="http://blog.flexexamples.com/2007/09/12/changing-a-slider-controls-thumb-skin/">thumb size issue</a>, but for this skin I only needed to hard-code an offset (+6,+6) to bring the thumb&#8217;s origin to (0,0) and make all my geometry line up correctly.  Similarly, I added a 1 pixel offset to the highlight skin to bring its origin to (0,0) to match the track.  The result:</p>
<div id="flashcontent-degrafa-videoplayer-scrub">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<h5>Volume Slider</h5>
<p class="bottom">For consistency, the Volume slider re-uses the Scrubber&#8217;s thumb skin, but the track and highlight are wedges (<code>Polygon</code>&#8216;s in Degrafa).  The only clever piece of code is the highlight skin&#8217;s overridden <code>updateDisplayList()</code> function:</p>

<div class="wp_syntax"><div class="code"><pre class="actionscript3" style="font-family:monospace;">override <span style="color: #0033ff; font-weight: bold;">protected</span> <span style="color: #339966; font-weight: bold;">function</span> updateDisplayList<span style="color: #000000;">&#40;</span>unscaledWidth<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span><span style="color: #000066; font-weight: bold;">,</span> unscaledHeight<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #0033ff; font-weight: bold;">void</span> <span style="color: #000000;">&#123;</span>
    <span style="color: #0033ff; font-weight: bold;">super</span><span style="color: #000066; font-weight: bold;">.</span>updateDisplayList<span style="color: #000000;">&#40;</span>unscaledWidth<span style="color: #000066; font-weight: bold;">,</span> unscaledHeight<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span>
    awidth = unscaledWidth<span style="color: #000066; font-weight: bold;">;</span>
    aheight = unscaledHeight<span style="color: #000066; font-weight: bold;">;</span>
&nbsp;
    <span style="color: #0033ff; font-weight: bold;">if</span> <span style="color: #000000;">&#40;</span><span style="color: #0033ff; font-weight: bold;">this</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">hasOwnProperty</span><span style="color: #000000;">&#40;</span><span style="color: #990000;">'parent'</span><span style="color: #000000;">&#41;</span> <span style="color: #000066; font-weight: bold;">&amp;&amp;</span>
             <span style="color: #0033ff; font-weight: bold;">this</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">hasOwnProperty</span><span style="color: #000000;">&#40;</span><span style="color: #990000;">'parent'</span><span style="color: #000000;">&#41;</span> <span style="color: #000066; font-weight: bold;">&amp;&amp;</span>
             <span style="color: #0033ff; font-weight: bold;">this</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">hasOwnProperty</span><span style="color: #000000;">&#40;</span><span style="color: #990000;">'width'</span><span style="color: #000000;">&#41;</span> <span style="color: #000066; font-weight: bold;">&amp;&amp;</span>
             <span style="color: #0033ff; font-weight: bold;">this</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">width</span> <span style="color: #000066; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span>
        _ratio = awidth <span style="color: #000066; font-weight: bold;">/</span> <span style="color: #0033ff; font-weight: bold;">this</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">parent</span><span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">width</span><span style="color: #000066; font-weight: bold;">;</span>
    <span style="color: #000000;">&#125;</span> <span style="color: #0033ff; font-weight: bold;">else</span> <span style="color: #000000;">&#123;</span>
        _ratio = <span style="color: #000000; font-weight:bold;">0.00001</span><span style="color: #000066; font-weight: bold;">;</span>
    <span style="color: #000000;">&#125;</span>
<span style="color: #000000;">&#125;</span></pre></div></div>

<p class="bottom">In order to correctly draw the highlight wedge, we first need to know the dimensions of the track.  For simplicity, the Volume track height is hard-coded in both the track skin and highlight skin, but the width of the track is variable.  In this case, the width of the track is the same as the width of the <code>HSlider</code> component itself.  So in the <code>updateDisplayList()</code> code above, the track width is found and used to compute the ratio of widths.  This ratio is later used to draw the highlight geometry.  The end result:</p>
<div id="flashcontent-degrafa-videoplayer-volume">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<h5>The Full Control Bar</h5>
<p class="bottom">Just displaying all the components side-by-side gives us the beginnings of a video player control bar:</p>
<div id="flashcontent-degrafa-videoplayer-controlbar">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
<p>Not too shabby.  In <a href="http://saturnboy.com/2009/06/degrafa-video-player-2/">Part 2</a>, I&#8217;ll assemble the final video player frontend including the pretty control bar components above, and weld it to an OvpNetStream-powered backend.</p>
<h5>Files</h5>
<ul>
<li><a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_button/degrafa_videoplayer_button.html">Button</a> (<a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_button/srcview/degrafa_videoplayer_button.zip">download</a>)</li>
<li><a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_playpause/degrafa_videoplayer_playpause.html">Play-Pause Button</a> (<a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_playpause/srcview/degrafa_videoplayer_playpause.zip">download</a>)</li>
<li><a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_scrub/degrafa_videoplayer_scrub.html">Scrubber</a> (<a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_scrub/srcview/degrafa_videoplayer_scrub.zip">download</a>)</li>
<li><a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_volume/degrafa_videoplayer_volume.html">Volume Slider</a> (<a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_volume/srcview/degrafa_videoplayer_volume.zip">download</a>)</li>
<li><a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_controlbar/degrafa_videoplayer_controlbar.html">The Full Control Bar</a> (<a href="http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_controlbar/srcview/degrafa_videoplayer_controlbar.zip">download</a>)</li>
</ul>
<div>
<script type="text/javascript">
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_button/degrafa_videoplayer_button.swf', 'flashcontent-degrafa-videoplayer-button', '500', '40', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_playpause/degrafa_videoplayer_playpause.swf', 'flashcontent-degrafa-videoplayer-playpause', '500', '120', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_scrub/degrafa_videoplayer_scrub.swf', 'flashcontent-degrafa-videoplayer-scrub', '500', '50', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_volume/degrafa_videoplayer_volume.swf', 'flashcontent-degrafa-videoplayer-volume', '500', '50', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
swfobject.embedSWF('http://saturnboy.com/proj/degrafa_skins/degrafa_videoplayer_controlbar/degrafa_videoplayer_controlbar.swf', 'flashcontent-degrafa-videoplayer-controlbar', '500', '40', '9.0.28', 'expressInstall.swf', false, { bgColor:'#ffffff', base:'.' });
</script></div>
]]></content:encoded>
			<wfw:commentRss>http://saturnboy.com/2009/05/degrafa-video-player-1/feed/</wfw:commentRss>
		<slash:comments>2</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>

