AIR 2.0 NativeProcess API - What’s It Good For? > O'Reilly Publishing

 

Dropfolders

While there are a few examples available for connecting AIR applications to native operating system processes today, most of them are basic "Hello World" examples, or happen to be purely theoretical. I've only come across oneor tworeal world examples of applications that leverage this functionality in a meaningful way. In this article, I'm going to talk about the DropFolders application which uses AIR 2.0 NativeProcess APIs to make working with HandBrakea more streamlined, hands-off process.

 

AIR 2.0 NativeProcess

So, before we get into all that, what is NativeProcess, exactly? The ActionScript 3.0 Reference for the Adobe Flash Platformdocumentation has a good summary:

The NativeProcess class provides command line integration and general launching capabilities. The NativeProcess class lets an AIR application execute native processes on the host operating system. The AIR applcation can monitor the standard input (stdin) and standard output (stdout) stream of the process as well as the process's standard error (stderr) stream.

Basically, this new API allows your application to communicate with just about any process running on the host machine. This can be an operating system core process such as or a third party installed application like HandBrake.

Things to Think About

Pretty cool, huh? This opens up a world of possibilities but also imposes some restrictions upon the distribution of applications that leverage NativeProcess. When dealing with native code, AIR applications must be compiled for specific, targeted platforms. This means that you cannot simply compile a .air file and be done with it. The simplest way to implement NativeProcess is to target a specific platform (Windows, Mac OSX, Linux) and compile for that platform. In the case of DropFolders, we are interfacing with the headless Handbrake CLI which also happens to be cross platform. In order to take advantage of this, we will have to include a different set of files for each platform version when we package for distribution. With most AIR applications, this also means doing some specific things in your code to account for differences in file path syntax across operating systems as well.

HandBrake CLI

For those who have never used HandBrake, it is an excellent video transcoding tool which allows users to convert a wide variety of file formats to H.264 with great precision through the use of a variety of fine-grained setting configurable from the GUI version, or passed in as arguments through the command line interface. At the University of Denver, we use HandBrake to process video for use with the CourseMedia application and for streaming through our Flash Media Server instances. The HandBrake GUI, however, does not allow for the concept of configurable "watch" and "destination" folders for automating the video transcode process.

Extending HandBrake with Dropfolders

At times, laziness breeds innovation. Normally, we can set someone up with HandBrake and some custom settings pretty quickly and they go off and do their thing. In one particular case though, we encountered a group who wanted absolutely nothing to do with the transcode process. Uh oh- what to do? In this case, they simply wanted to drop some files directly from their video device onto a network share and be done with it. We built DropFolders to enable that sort of workflow both on the desktop and within a server environment. Setting it up on a server is a bit more work (and I won't get into it here!) but the idea is the same in either case; define a watch folder that is constantly monitored by the application, as new files are detected- process them according to specific presets and place the derivative files into a directory where a separate display application can find them.

 

Code Example

Here's an example of the ActionScript from DropFolders that sets up a connection to HandBrake and builds some communications gateways with the process. Most of this is based on the application's TranscodeController class.

You'll need to import the following classes:

import flash.filesystem.File;

import flash.desktop.NativeProcess;

import flash.desktop.NativeProcessStartupInfo;

import flash.events.NativeProcessExitEvent;

import flash.events.ProgressEvent;

import flash.events.IOErrorEvent;

import mx.controls.Alert;

Declare some variables and setup your environment based on OS:

 

private var nativeProcess:NativeProcess; 

private var nativeProcessStartupInfo:NativeProcessStartupInfo; 

private var hb:File; 

private var hbCLI:String; 

private var pathDivider:String; 

private var os:String; 

private function setupController() {

  os = Capabilities.os.substr(0, 3).toLowerCase();

  if(NativeProcess.isSupported){

     if(os == "win"){

       pathDivider = "";

       hbCLI = "HandBrake.exe"

      }else{

         pathDivider = "/";

         hbCLI = "HandBrake.dmg"

      } establishConnection();

    }else{ Alert.show("Not Good."); }

 } <div> 

Now setup the NativeProcess associations:


private function establishConnection():void {         
        hb = File.applicationDirectory;         
        hb = hb.resolvePath("HandBrake" + pathDivider + hbCLI);         
        nativeProcess = new NativeProcess();         
        nativeProcessStartupInfo = new NativeProcessStartupInfo(); 
}

Now all we need to do is point to HandBrake, set up our arguments within a Vector object, add some event listeners, and start the NativeProcess itself:


public function invokeHandBrake(i:String, o:String, a:String):void { 
        nativeProcessStartupInfo.executable = hb; 
        var processArgs:Vector.<String> = new Vector.<String>(); 
        processArgs.push("-i"); processArgs.push(i); processArgs.push("-o"); 
        processArgs.push(o); var argArray:Array = a.split(" "); 
        for(var j:int = 0; j<argArray.length-1; j++)
        { 
                processArgs.push(argArray[j]); 
        } 
        nativeProcessStartupInfo.arguments = processArgs; 
        nativeProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onStandardOutputData); 
        nativeProcess.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onStandardErrorData); 
        nativeProcess.addEventListener(NativeProcessExitEvent.EXIT, onExit); 
        nativeProcess.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onOutputIOError);
        nativeProcess.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onStandardIOError); 
        nativeProcess.start(nativeProcessStartupInfo); 
}
few notes about the arguments here, in the case of DropFolders; "i" is our input file, "d" is the file that will be created through the transcode process, and "a" is a string of arguments that HandBrake expects in order to perform a proper transcode. This is normally in a format similar to "-e x264 -b 1500 -a 1 -E faac -B 160 -R Auto -6 dpl2 -f mp4 -p -m -2 -T -x ref=2:bframes=2:me=umh". The temporary Array being used exists simply to isolate each argument so that it can be easily passed into the constructed Vector object and passed through our NativeProcess.

Conclusion

AIR 2.0 is a major upgrade from AIR 1.5 and the addition of NativeProcess alone opens up so many more possibilities. Without access to AIR 2.0, think of the amount of work it would take to get something like this working across operating systems. One of the major strengths of the Flash Platform is that it allows a capable developer to efficiently work across platforms and devices. These new hooks into native operating system processes has taken this to a whole new level.

Thanks to InsideRIA.com