Programming

Embedded H.264 Video on iOS with a Flex Mobile Project

Flash Builder 4.6

For a current project I am working, I decided to use Flash Builder 4.6 to target iOS, Anrdoid, and Blackberry Playbook devices. I had used it before to target Android and Playbooks, but never iOS.

I got about a month’s worth of development done before I decided to actually test on my iPad. I was nervous because I was doing a lot unorthodox subclassing/ extending with various classes, but to my surprise everything worked as planned.

I can’t say I appreciate the testing process of having to constantly drag/ drop IPA’s onto iTunes.

Today I ran into my first roadblock, and it was big one. I almost thought I would have to abandon Flash Builder altogether and go native.

The application is essentially a video player. Turns out, playing video using Flash Builder on mobile devices is not so nice. Of course, the VideoPlayer object is still not mobile optimized. All my research pointed me to the recently available StageVideo, with Air 3.1 and Flash Player 11. StageVideo dumps processing over to the CPU and makes video playback much more efficient. Sounds great!

Of course, it wasn’t that easy. But for anyone trying the same thing, I am once and for all putting all the information in one place.

  1. Edit the project’s Flash Compiler settings and force the proper Flash player by adding “-swf-version=14 -target-player=11.0” to the Additional compiler arguments. (Right click on project, go to properties, then Flash Compiler on the left)
  2. In your App.xml set the rendermode to “direct”
  3. Set the views backgroundAlpha=0

Next comes the actual code. In your view you need to first make sure that StageVideo is available, so set the following EventListener:

stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, onStageVideoState);

And the listener:

private function onStageVideoState(event:StageVideoAvailabilityEvent):void {

    if (event.availability == StageVideoAvailability.AVAILABLE) { // This will return false in the desktop simulator, but works fine on the iOS device
        var path:String = new File(new File( "app:/" + path/to/file ).nativePath).url;

        var nc:NetConnection = new NetConnection();
        nc.connect(null);

        var ns:NetStream =  new NetStream(nc);
        var obj:Object = new Object();

        ns.client = obj;
        ns.client = obj;
        obj.onMetaData = onMetaData;

        var sv:StageVideo = stage.stageVideos[0];
        sv.viewPort = new Rectangle(0, 0, videoWidth , videoHeight );
        sv.attachNetStream(ns);
        ns.play( videoPath );
        navigator.contentGroup.visible = false // see below
    }

    protected function onMetaData(info:Object):void{
        trace(info.width, info.height, info.duration);
    }

}

As long as your video is in the proper format with the right codec, you should see your video playing. Hooray! If you don’t get anything to play, check the video format by making sure it plays on the device with the default video player. If it works there, make sure you are getting the right path to the file.

You can refactor this code and make it more robust by adding EventListeners  to the NetStream to detect various playback states, ie stop, pause, done and act accordingly.

I was happy to see the video, but I was not impressed due to two big caveats. One, since the StageVideo plays below all Flash objects, the entire view has to be hidden with the “navigator.contentGroup.visible = false” line at the end. This may be acceptable for you, but it wasn’t for me because I am using a custom background. The second caveat was that you don’t get the native iOS video player, with the interface that everyone is used to. No controls! No pause/ play, volume, etc. This was extremely unacceptable for me.

StageVideo left as fast it arrived, completely useless for my application. But I had one more options; StageWebView. With just a few lines of code, I was able to play the video with native video controls.

    
    var path:String = new File(new File( "app:/" + filename ).nativePath).url;
    var webView:StageWebView = new StageWebView();
    webView.stage = this.stage;
    webView.viewPort = new Rectangle(videoX, videoY, videoWidth, videoHeight);
    webView.loadURL(path);

Again, as long as your have the proper video format you should be playing video with those few lines. That’s it!

Now, to close the StageWebView after the video was done was causing the app to crash on the iPad. Looking at the crash logs I figured out that it was the player that was causing the crash. Apparently it doesn’t like the view taken away from it without properly releasing the player. Since I was directly loading the video, I could not access the video player so I came up with a work around of loading a blank page first, and then removing of the StageWebView.

    var htmlString:String = "<!DOCTYPE HTML><html><body style=background:#000000><p></p></body></html>";
    webView.loadString(htmlString);
    webView.reload();
    webView.viewPort = null;
    webView.dispose();
    webView = null;

StageWebView saved my life! I am not sure how it will work on the other two platforms, but for now I am just focused on iOS.

Previous ArticleNext Article

Leave a Reply

Your email address will not be published. Required fields are marked *