Wednesday, January 7, 2009

Flex + the Embedded Player: A Christmas Story

Posted by Pamela Fox, Maps API Team

Hey YouTube folks! My name is Pamela Fox, and I hail from the Maps API world. I was charged with the task this year of creating the map for the NORAD Santa Tracker (yes, Google even indexes Santa), and I decided to convert last year's JavaScript map into a Flex map. This meant converting every feature into Flex, including arguably the most important feature: displaying YouTube-hosted Santa Cam videos in the infowindows. The kiddies get a kick out of seeing Santa soaring, tumbling, and freewheeling through the endless diamond sky.

I wanted what I thought was a basic feature: using the embedded player to play 23 different YouTube videos in the same Flex app. I started with a straightforward implementation - using the SWFLoader Flex component with the source set to the URL of the YouTube video. But I noticed some funny behavior - the first video would load, but none after that. After some searching around, I discovered the reason for this: if the first loaded YouTube SWF isn't properly removed by calling its destroy method(), any subsequently loaded YouTube SWF won't play correctly. The solution is to call destroy() on the YouTube SWF - but wait a second, it's not that easy. Flex/AS3 SWFs cannot call methods on AS2 SWFs, and YouTube SWFs are indeed written in AS2.

After harassing various YouTube engineers and reading through the AS3 Chromeless Player wrapper code, I came up with the solution shown in the diagram below.



The Flex app uses SWFLoader to load in an AS2 SWF, which I call the "YouTube bridge". The YouTube bridge creates a LocalConnection, which is a way for any SWF to invoke methods on any other SWF that's open simultaneously on a computer. The bridge specifies what connection it can receive commands from, and then defines two functions that other SWFs can call - loadMovie() and dispose(). The loadMovie() function takes in a YouTube ID, creates a MovieClip and loads the YouTube movie into it. The dispose() function calls destroy() on that MovieClip, and then does various other cleanup operations. The Flex app then tells that same connection what method names it wants to invoke, either "loadMovie" or "dispose". Some snippets of the LocalConnection code are shown below:


// Flex
outBox = new LocalConnection();
outBox.send(outBoxName, "dispose");
outBox.send(outBoxName, "loadMovie", ids[counter]);

// AS2
var inbox = new LocalConnection();
inbox.connect(_root.boxName);
inbox.dispose = function () {
youtubeMC.destroy();
...
};


When I first coded this, I hard-coded a name for the connection name in the Flex + AS2. This meant that my app couldn't be used in multiple browsers at the same time, since all instances of the app would be sending and receiving messages from the same inbox, and they'd get awfully confused. To remedy that, I generate a random ID in the Flex app, pass that into the query string when loading the YouTube bridge, and then have the bridge use the value of the query parameter as the inbox name. A snippet of that code is shown below:


// Flex
outBoxName = String(new Date().getUTCMilliseconds());
swfLoader.load("youtubebridge.swf?boxName=" + outBoxName);

// AS2
inbox.connect(_root.boxName);


The full code (MXML + FLA) is available, and you can test out the demo app. Now that I've made it easy for you all, I hope to see some nifty YouTube/Flex mashups.. and maybe even some Youtube/Flex/Maps mashups!