<?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>h6 - HatSix &#187; AIR</title>
	<atom:link href="http://hat6.com/category/projects/air/feed/" rel="self" type="application/rss+xml" />
	<link>http://hat6.com</link>
	<description>Hat6 Web Application Engineering</description>
	<lastBuildDate>Wed, 23 Nov 2011 18:46:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>AIR Video Transcoding &#8211; NativeProcess example</title>
		<link>http://hat6.com/2010/08/02/air-video-transcoding-nativeprocess/</link>
		<comments>http://hat6.com/2010/08/02/air-video-transcoding-nativeprocess/#comments</comments>
		<pubDate>Mon, 02 Aug 2010 07:17:24 +0000</pubDate>
		<dc:creator>Dusty</dc:creator>
				<category><![CDATA[AIR]]></category>

		<guid isPermaLink="false">http://hat6.com/?p=100</guid>
		<description><![CDATA[I hope to write up an explanation of the code, but I promised the User Group that I&#8217;d post the code, and I had better do it now while I&#8217;m thinking about it (only 2 weeks late!) One important note: copy the .ffmpeg directory to your /user/ directory. Use ProcMon to see exactly where (on [...]]]></description>
			<content:encoded><![CDATA[<p>I hope to write up an explanation of the code, but I promised the User Group that I&#8217;d post the code, and I had better do it now while I&#8217;m thinking about it (only 2 weeks late!)</p>
<p>One important note: copy the .ffmpeg directory to your /user/ directory.  Use ProcMon to see exactly where (on windows).  If I were to release this as an actual app, I&#8217;d re-compile ffmpeg to look in the current directory.</p>
<p>Also, I didn&#8217;t include anything in the media folder, you&#8217;ll have to find your own AVI file to transcode.</p>
<p><a href="http://hat6.com/wp-content/uploads/2010/08/AirFFmpeg.fxp">Download the fxp</a>.</p>
<p>Code below the fold.<br />
<span id="more-100"></span></p>
<pre class="brush: php">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;s:WindowedApplication xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
					   xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
					   xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;&gt;

	&lt;fx:Script&gt;
		&lt;![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			[Bindable] protected var process:NativeProcess;
			[Bindable] protected var outputText:String = &quot;&quot;;

			protected var lengthRegExp:RegExp = /Duration: (.*), start: (.*), bitrate: (.*)/m;
			[Bindable] protected var lengthStr:String = &quot;&quot;;
			[Bindable] protected var lengthNum:Number = 0;

			protected var sizeRegExp:RegExp = /Video(.*) (\d+)x(\d+)/m;
			[Bindable] protected var sizeStr:String = &quot;&quot;;

			protected var curTimeRegExp:RegExp = /time=(\d+).(\d+) /m;
			[Bindable] protected var currentTime:Number = 0;

			protected var startTime:Date = new Date();

			[Bindable] protected var profiles:ArrayCollection = new ArrayCollection([
				{label:&quot;Baseline&quot;, data:&quot;baseline&quot;},
				{label:&quot;Default&quot;, data:&quot;default&quot;},
				{label:&quot;Very Slow&quot;, data:&quot;veryslow&quot;},
				{label:&quot;Slower&quot;, data:&quot;slower&quot;},
				{label:&quot;Slow&quot;, data:&quot;slow&quot;},
				{label:&quot;Fast&quot;, data:&quot;fast&quot;},
				{label:&quot;Faster&quot;, data:&quot;faster&quot;},
				{label:&quot;Super Fast&quot;, data:&quot;superfast&quot;},
				{label:&quot;Ultra Fast&quot;, data:&quot;ultrafast&quot;},
				{label:&quot;IPod Low&quot;, data:&quot;ipod320&quot;},
				{label:&quot;IPod High&quot;, data:&quot;ipod640&quot;},
				{label:&quot;High Quality&quot;, data:&quot;hq&quot;},
				{label:&quot;Lossless - Slow&quot;, data:&quot;lossless_slow&quot;},
				{label:&quot;Lossless - Ultrafast&quot;, data:&quot;lossless_ultrafast&quot;}]);

			protected function button1_clickHandler(event:MouseEvent):void
			{
				if(!NativeProcess.isSupported) return;
				if(process &amp;&amp; process.running){
					trace(&quot;FFmpeg is already running&quot;);
					return;
				}
				startTime = new Date();
				var vid:File = File.applicationDirectory.resolvePath(&#039;media\\P7030615.avi&#039;);
				var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
				var f:File = new File(File.applicationDirectory.nativePath + &quot;\\NativeApps\\windows\\ffmpeg\\bin\\ffmpeg.exe&quot;);
				nativeProcessStartupInfo.executable = f;
				var args:Vector.&lt;String&gt; = new Vector.&lt;String&gt;();
					args.push(&quot;-i&quot;);
					args.push(vid.nativePath);
					args.push(&quot;-acodec&quot;);
					args.push(&quot;libmp3lame&quot;);
					args.push(&quot;-ab&quot;);
					args.push(&quot;128k&quot;);
					args.push(&quot;-vcodec&quot;);
					args.push(&quot;libx264&quot;);
					args.push(&quot;-vpre&quot;);
					args.push(profileDDL.selectedItem.data);
					args.push(&quot;-crf&quot;);
					args.push(&quot;22&quot;);
					args.push(&quot;-threads&quot;);
					args.push(&quot;0&quot;);
					args.push(vid.nativePath + &#039;.mp4&#039;);
				nativeProcessStartupInfo.arguments = args;
				//ffmpeg -i ..\..\media\P7030615.avi -acodec libmp3lame -ab 128k -vcodec libx264 -vpre slow -crf 22 -threads 0 ..\..\media\P7030615.mp4
				process = new NativeProcess();
				process.start(nativeProcessStartupInfo);
				process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, handleFFmpegError);
				process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, handleFFmpegOutput);
				process.addEventListener(NativeProcessExitEvent.EXIT, handleFFmpegExit);
			}
			protected function parseOutput(output:String):void{
				if(lengthRegExp.test(output)){
					var results:Array = lengthRegExp.exec(output);
					lengthStr = results[1];
					var lengthArr:Array = String(results[1]).split(&quot;:&quot;);
					lengthNum = parseFloat(lengthArr[2]) +
								(parseFloat(lengthArr[1]) * 60);
				}
				if(sizeRegExp.test(output)){
					var resultsSize:Array = sizeRegExp.exec(output);
					sizeStr = resultsSize[2] + &quot; x &quot; + resultsSize[3];
					trace(sizeStr);
				}
				if(curTimeRegExp.test(output)){
					var resultsTime:Array = curTimeRegExp.exec(output);
					currentTime = parseFloat(resultsTime[1] + &quot;.&quot; + resultsTime[2]);
					var percentDone:Number = Math.floor(currentTime / lengthNum * 100)
					progress.setProgress(percentDone, 100);
					var now:Date = new Date();
					var diff:Number = now.valueOf() - startTime.valueOf();
					var total:Number = diff / (percentDone / 100);
					var remaining:Date = new Date(total - diff);
					progress.label = percentDone + &quot;% Complete / &quot; + remaining.toUTCString().split(&quot; &quot;)[3] + &quot; remaining&quot;;
				}

			}
			protected function handleFFmpegOutput(e:ProgressEvent):void{
				var output:String = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
				outputText += output;
				parseOutput(output);
				textReceived.scrollToRange(-1);
				if(output.indexOf(&quot;already exists&quot;) &gt; -1){
					Alert.show(&quot;Overwrite File?&quot;, &quot;File Exists&quot;, 4, null, handleOverwriteClosed)
				}

			}
			public function ffmpegcommand(cmdStr:String):void
			{
				if(process &amp;&amp; process.running){
					process.standardInput.writeUTFBytes(cmdStr + &quot;\n&quot;);
				}
			}
			protected function handleOverwriteClosed(e:CloseEvent):void{
				if(e.detail == Alert.YES){
					ffmpegcommand(&quot;y&quot;);
					startTime = new Date();
				}else{
					ffmpegcommand(&quot;n&quot;);
				}

			}
			protected function handleFFmpegError(e:ProgressEvent):void{
				var errorData:String = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
				outputText += errorData;
				parseOutput(errorData);
				textReceived.scrollToRange(-1);
				if(errorData.indexOf(&quot;already exists&quot;) &gt; -1){
					Alert.show(&quot;Overwrite File?&quot;, &quot;File Exists&quot;, Alert.YES|Alert.NO, null, handleOverwriteClosed)
				}
			}

			protected function handleFFmpegExit(e:NativeProcessExitEvent):void{
				trace(&quot;FFmpeg Exited: &quot; + e.exitCode);
				trace(e.toString());
			}
			protected function traceError(e:Event):void{
				trace(e);
			}
			protected function tr_updateCompleteHandler(evt:FlexEvent):void {
				textReceived.scroller.verticalScrollBar.value = textReceived.scroller.verticalScrollBar.maximum;
			}

		]]&gt;
	&lt;/fx:Script&gt;

	&lt;fx:Declarations&gt;
		&lt;!-- Place non-visual elements (e.g., services, value objects) here --&gt;
	&lt;/fx:Declarations&gt;
	&lt;s:layout&gt;
		&lt;s:VerticalLayout paddingTop=&quot;5&quot; paddingRight=&quot;5&quot; paddingLeft=&quot;5&quot; gap=&quot;0&quot; /&gt;
	&lt;/s:layout&gt;
	&lt;s:TabBar dataProvider=&quot;{mainView}&quot; /&gt;
	&lt;mx:ViewStack id=&quot;mainView&quot; width=&quot;100%&quot; height=&quot;100%&quot; borderStyle=&quot;solid&quot; borderVisible=&quot;true&quot; creationPolicy=&quot;all&quot;&gt;
		&lt;s:NavigatorContent label=&quot;Convert&quot;&gt;
			&lt;s:VGroup width=&quot;100%&quot; height=&quot;100%&quot;&gt;
				&lt;mx:Text height=&quot;40&quot; id=&quot;infoText&quot; width=&quot;100%&quot;
							text=&quot;Duration: {lengthStr}
							Size: {sizeStr}&quot; /&gt;
				&lt;s:HGroup width=&quot;100%&quot;&gt;
					&lt;mx:ProgressBar width=&quot;100%&quot; id=&quot;progress&quot; mode=&quot;manual&quot; direction=&quot;right&quot; /&gt;
					&lt;s:Button label=&quot;Cancel&quot; click=&quot;if(process)process.exit(true);&quot; /&gt;
				&lt;/s:HGroup&gt;
				&lt;s:DropDownList id=&quot;profileDDL&quot; width=&quot;200&quot;
								dataProvider=&quot;{profiles}&quot;
								prompt=&quot;Choose an encoding profile&quot; /&gt;
				&lt;s:Button label=&quot;Start Conversion&quot; enabled=&quot;{profileDDL.selectedIndex &gt; -1}&quot;
						  click=&quot;button1_clickHandler(event)&quot; /&gt;

			&lt;/s:VGroup&gt;
		&lt;/s:NavigatorContent&gt;
		&lt;s:NavigatorContent label=&quot;Log&quot;&gt;
			&lt;s:TextArea id=&quot;textReceived&quot; width=&quot;100%&quot; height=&quot;100%&quot;
						text=&quot;{outputText}&quot;
						updateComplete=&quot;tr_updateCompleteHandler(event)&quot; /&gt;

		&lt;/s:NavigatorContent&gt;
	&lt;/mx:ViewStack&gt;
&lt;/s:WindowedApplication&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://hat6.com/2010/08/02/air-video-transcoding-nativeprocess/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Singleton-esque, self-aware &#8216;Kind&#8217; Classes</title>
		<link>http://hat6.com/2008/09/08/singleton-esque-self-aware-kind-classes/</link>
		<comments>http://hat6.com/2008/09/08/singleton-esque-self-aware-kind-classes/#comments</comments>
		<pubDate>Tue, 09 Sep 2008 00:49:46 +0000</pubDate>
		<dc:creator>Dusty</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Projects]]></category>

		<guid isPermaLink="false">http://hat6.com/?p=26</guid>
		<description><![CDATA[I recently started a project of converting an older flex app to an AIR app. This app has to scan through a bunch of files, and classify certain types of media based on the location and file extension. Then it saves a manifest of the media to an xml file that can be loaded (without [...]]]></description>
			<content:encoded><![CDATA[<p>I recently started a project of converting an older flex app to an AIR app. This app has to scan through a bunch of files, and classify certain types of media based on the location and file extension.  Then it saves a manifest of the media to an xml file that can be loaded (without scanning every time).</p>
<p>I started out with a Kind class (think CollectionEventKind) that had the types as strings&#8230; but then I had to figure out how to store the regular expressions to make them easily accessible&#8230; Then I remembered that while everyone just uses Strings, static const vars can be any class, including it&#8217;s own class.  So I came up with this class that you can use constants, or locate a specific constant.  Code after the fold.<br />
<span id="more-26"></span><br />
<code><br />
package aero.panasonic.training.model<br />
{<br />
	import flash.utils.Dictionary;<br />
	import flash.utils.describeType;</p>
<p>	public class MediaKind<br />
	{<br />
		public function MediaKind(title:String, label:String, regex:RegExp ){<br />
			this.title = title;<br />
			this.label = label;<br />
			this.regex = regex;<br />
		}<br />
		public function toString():String{<br />
			return title;<br />
		}<br />
		public var title:String;<br />
		public var label:String;<br />
		public var regex:RegExp;<br />
		public static const D_ILLUSTRATION_PERSPECTIVE:MediaKind =<br />
			new MediaKind("Illustration - Perspective", "Perspective", /Deliverables\/Illustrations\/Perspective\/.*_perspective\.(?:png|swf|jpg)/i );<br />
		public static const D_ILLUSTRATION_THREEQUARTER:MediaKind =<br />
			new MediaKind("Illustration - 3/4 (ISO)", "ISO", /Deliverables\/Illustrations\/Isometric\/.*_iso\.(?:png|swf|jpg)/i );</p>
<p>		private static var lookup:Dictionary = new Dictionary();<br />
		private static var allKinds:Array = new Array();<br />
		private static var kindsMapped:Boolean = false;</p>
<p>		private static function mapKinds():void{<br />
			var description:XML = describeType(MediaKind);<br />
			for each(var cXML:XML in description.constant){<br />
				var m:MediaKind = MediaKind[cXML.@name] as MediaKind;<br />
				lookup[m.title] = m;<br />
				lookup[m.label] = m;<br />
				allKinds.push(m);<br />
			}<br />
			kindsMapped = true;<br />
		}<br />
		public static function getMediaKind(input:String):MediaKind{<br />
			if(!kindsMapped) mapKinds();<br />
			return lookup[input];<br />
		}<br />
		public static function getAllKinds():Array{<br />
			if(!kindsMapped) mapKinds();<br />
			return allKinds;<br />
		}<br />
	}<br />
}<br />
</code></p>
<p>In my code, I have a piece that gets all of the MediaKinds, then checks each file against each kind. I can filter, I can create drop-downs, all from one class.  The getMediaKind() function lets me effortlessly create my ValueObjects from XML. The toString() writes out the title, making my XML human-readable.</p>
<p>Instinctively, I want to take this farther and create a base &#8216;Kind&#8217; class that has this all in it. It turns out that static properties/functions aren&#8217;t inherited. The properties and functions are still in scope, however&#8230; so it may be possible to write a generic function that takes a class, creates a lookup for that specific class and passes it back down the line, allowing the allKinds() function to return ALL of the static definitions for the class and it&#8217;s super-classes.  Anyways, that&#8217;s too much for my head at this point, but maybe someone else will take the bait <img src='http://hat6.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://hat6.com/2008/09/08/singleton-esque-self-aware-kind-classes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debugging AIR Drag and Drop</title>
		<link>http://hat6.com/2008/05/11/debugging-air-drag-and-drop/</link>
		<comments>http://hat6.com/2008/05/11/debugging-air-drag-and-drop/#comments</comments>
		<pubDate>Sun, 11 May 2008 23:59:03 +0000</pubDate>
		<dc:creator>Dusty</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Flex]]></category>

		<guid isPermaLink="false">http://hat6.com/2008/05/11/debugging-air-drag-and-drop/</guid>
		<description><![CDATA[So&#8230; you thought you&#8217;d be cool and throw a break point on some DRAG_ENTER code, but after you&#8217;re done debugging, you&#8217;re left with a little floating icon on your desktop. Never fear, you don&#8217;t have to restart your entire computer, open up Taskmanager and you&#8217;ll see an application called &#8216;Drag&#8217;. You can kill this application [...]]]></description>
			<content:encoded><![CDATA[<p>So&#8230; you thought you&#8217;d be cool and throw a break point on some DRAG_ENTER code, but after you&#8217;re done debugging, you&#8217;re left with a little floating icon on your desktop.</p>
<p><img src="http://hat6.com/wp-content/uploads/2008/05/debugging_dragdrop.png" alt="Debugging Drag and Drop" /></p>
<p>Never fear, you don&#8217;t have to restart your entire computer, open up Taskmanager and you&#8217;ll see an application called &#8216;Drag&#8217;.  You can kill this application and the icon will disappear.  If you don&#8217;t see this app, close Eclipse and find the process called &#8216;adl.exe&#8217; and kill this process.</p>
<p>Restart averted, Yay!</p>
]]></content:encoded>
			<wfw:commentRss>http://hat6.com/2008/05/11/debugging-air-drag-and-drop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

