ActionScript 3.0 Wrapper for Chromeless Player

This article was written and submitted by an external developer. The YouTube APIs and Tools team thanks Matthew Richmond for his time and expertise.


Matthew Richmond, The Chopping Block, Inc.
October 2008

Introduction

In this article, I will present and briefly outline a reliable ActionScript 3.0 Wrapper for the Chromeless YouTube Player. The wrapper leverages ActionScript's ExternalInterface class and YouTube's JavaScript API.

Google's YouTube Player Tools and Chromeless Player have empowered designers/developers to quickly and easily integrate the power of YouTube into their online projects. This approach is ideal for small projects on fixed budgets that don't allow for hosting video, as well as large scale projects for clients wanting a custom end-user experience without distancing themselves from their YouYube audience.

ActionScript 3.0 Wrapper Screenshot
Figure 1: ActionScript 3.0 Wrapper Screenshot

YouTube's Flash API and embedded players are written in, and work quite well with ActionScript 2.0; if your project is ActionScript 3.0 however, seamless integration becomes a bit more complex. While it's easy to load the ActionScript 2.0 players into an ActionScript 3.0 swf, you can not directly communicate or pass any functional calls into the loaded player. This is further complicated because the swf file coming from YourTube's servers is loading a video into itself. Our wrapper needs to be fully aware of that fact and react accordingly. Fortunately the ActionScript language contains two plausible workarounds for reconnecting these nested yet disconnected pieces, the LocalConnection class or the ExternalInterface class. This demo is going to focus on the latter because the ExternalInterface works seamlessly with the well documented JavaScript API and therefore plays well with anything else within the XHTML page.

Important Resources

Before we begin, here is a list of related resources and files available to you. Many of the topics discussed in this article are further detailed within the links below.

Digging In

Wrapper Demo Overview

ActionScript Class Files
Figure 3: Javascript File
ActionScript Class Files
Figure 2: ActionScript Class Files

The ActionScript 3.0 Wrapper consists of essentially two interconnected parts, the ActionScript 3.0 class files located in src/choppingblock/video/ (Figure 2), and the 'youTubeLoader.js' JavaScript file located in deploy/_assets/js/ (Figure 3). Your Flash/Flex source file will create a instance of the YouTubeLoader ActionScript class; your XHTML page embeds the flash file and registers it with the functions within the youTubeLoader JavaScript file. It's critical to understand that from here, everything that the YouTubeLoader does within the flash file, is controlled via the JavaScript functions.

Since all of the functionality within Flash is controlled via the JavaScript API, you will NOT be able to load any YouTube content within Flash's 'Test Movie' player. It will only work when embedded into an XHTML page and properly connected to the youTubeLoader JavaScript functions.

Note: To test any of these calls, you must have your file running on a webserver or modify the Security Settings of your Flash Player, as the Flash player restricts calls between local files and the Internet.

Creating a YouTubeLoader Object

Before you can create an instance of the YouTubeLoader Object in your Flash/Flex project, you first must make sure the package (folder) of required files is in the same directory as your project (see Figure 2), or defined with your projects Classpath. Then you can import the package files:

import choppingblock.video.*;

This allows your ActionScript file access to the 'YouTubeLoader.as' and 'YouTubeLoaderEvent.as' classes. Now you are clear to create an instance of the YouTubeLoader class and the necessary event listeners:

import choppingblock.video.*;

public class YouTubeDemo extends Sprite {

  private var _youTubeLoader:YouTubeLoader;	
  
  // ------------------------------------
  // CONSTRUCTOR
  // ------------------------------------
  
  public function YouTubeDemo () {
    
    // create YouTubeLoader object
    _youTubeLoader = new YouTubeLoader();
    
    // add event listener to call youTubePlayerLoadedHandler method on load
    _youTubeLoader.addEventListener(YouTubeLoaderEvent.LOADED, youTubePlayerLoadedHandler);
    
    // create the actual loader 
    _youTubeLoader.create();
    
    // add object to the display stack
    addChild(_youTubeLoader);
  };
  
  // ------------------------------------
  // EVENT METHODS
  // ------------------------------------
  
  /**
  Called via player loaded event, lets you know you are all clear to send player commands.
  */
  private function youTubePlayerLoadedHandler (event:YouTubeLoaderEvent):void{
    //trace("YouTubeDemo: youTubePlayerLoadedHandler");
    
    // you are now clear to start making calls to the YouTubeLoader object
  };
};

Now, if the JavaScript file is in place and connected properly, the youTubePlayerLoadedHandler should be called successfully and we are ready to begin making requests.

Embedding the swf and Connecting the youTubeLoader JavaScript

You won't be able to successfully load content from YouTube until your swf file is embedded into an XHTML file and connected to the youTubeLoader JavaScript. We recommend using SWFObject to embed any players that will be accessed using the JavaScript API. This will allow you to detect the end user's Flash Player version (the JavaScript API requires Flash Player 8 or higher), and also will get rid of the 'Click to activate this control' box when using Internet Explorer to view the player.

Within the <head> portion of your XHTML file you connect the swfobject and youTubeLoader files:

<script type="text/javascript" src="_assets/js/swfobject.js"></script>
<script type="text/javascript" src="_assets/js/youTubeLoader.js"></script>

See below for an example of using SWFObject to embed your ActionScript 3.0 swf with the JavaScript API enabled, and then pass a reference to your swf to the youTubeLoader JavaScript API.

<script type="text/javascript">

  var flashvars = {};
  var params = {
    menu: "false",
    allowScriptAccess: "always",
    scale: "noscale"
  };
  var attributes = {
    id: "youtubewrapper"
  };

  swfobject.embedSWF("YouTubeIntegrationDemo.swf", "flashcontent", "960", "500", "9.0.0", "_assets/swf/expressInstall.swf", flashvars, params, attributes);
  
  //init the youTubeLoader JavaScript methods
  SWFID = "youtubewrapper"
    
</script>

The allowScriptAccess parameter in the code is needed to allow the player SWF to call functions on the containing HTML page, since the Chromeless Player is hosted on a different domain from the XHTML page.

The only attribute we're passing in is the id of the embed object — in this case, youtubewrapper. This id is what the youTubeLoader.js file will use to get a reference to the player using getElementById().

swfobject.embedSWF will load the player from YouTube and embed it onto your page.

swfobject.embedSWF(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj)

  • swfUrlStr - This is the URL of the SWF. Note that we have appended the enablejsapi and playerapiid parameters to the normal YouTube SWF URL to enable JavaScript API calls.
  • replaceElemIdStr - This is the HTML DIV id to replace with the embed content. In the example above, it is ytapiplayer.
  • widthStr - Width of the player.
  • heightStr - Height of the player.
  • swfVersionStr - The minimum required version for the user to see the content. In this case, version 8 or above is needed. If the user does not have 8 or above, they will see the default line of text in the HTML DIV.
  • xiSwfUrlStr - (Optional) Specifies the URL of your express install SWF. Not used in this example.
  • flashVarsObj - (Optional) Specifies your FlashVars in name:value pairs. Not used in this example.
  • parObj - (Optional) The parameters for the embed object. In this case, we've set allowScriptAccess.
  • AttObj - (Optional) The attributes for the embed object. In this case, we've set the id to myytplayer.

See the SWFObject documentation for further explanation.

SWFID ≡ will store a reference to the id of the embed object for the JavaScript API to use. You must use the same id of the embed object that you gave the swf.

SWFID ≡ "youtubewrapper"

At this point you should be able to successfully test your project. The YouTubeLoader object should load the Chromeless Player and the YouTubeLoaderEvent.LOADED event handler should be successfully called. Now we are ready to make a video request and interact with the YouTubeLoader object.

Interacting With The Player

Since this approach to creating an ActionScript 3.0 Wrapper for the Chromeless Player leverages ActionScript's ExternalInterface class, we can now use any of the Operations within the YouTube JavaScript Player API to control our loaded player. If you look within the 'youTubeLoader.js' JavaScript file located in deploy/_assets/js/ (Figure 3) you will find that it contains most of the functions available. Each operation function first checks against the checkObj function to verify that the SWFID variable has been properly set before it executes.

//------------------------------------
// UTILITY METHODS
//------------------------------------

function checkObj () {
  // alert("youTubeLoader.js : checkObj");
  if (SWFID) {
    createObj();
    return true;
  } else{
    alert("YouTubeLoader: In order to call methods within a swf, you must first set the variable \"SWFID\"!");
    return false;
  };
}
  
//------------------------------------
// YOUTUBE METHODS
//------------------------------------

function loadVideoById(id, startSeconds) {
  // alert("youTubeLoader.js : loadVideoById");
  if (checkObj()) {
    obj.loadVideoById(id,startSeconds);
  };
};

function cueNewVideo(id, startSeconds) {
  // alert("youTubeLoader.js : loadVideoById");
  if (checkObj()) {
    obj.cueVideoById(id, startSeconds);
  }
}

function clearVideo() {
  // alert("youTubeLoader.js : clearVideo");
  if (checkObj()) {
    obj.clearVideo();
  }
}

// plus 17 more...

Given that the end goal of our ActionScript 3.0 Wrapper for the Chromeless Player is to offer seamless interaction with the YouTube API from within an ActionScript 3.0 Flash/Flex project, we have added the exact same public methods to the the 'YouTubeLoader.as' class file located in src/choppingblock/video/ (Figure 2). This means you can call the exact same operations directly to the YouTubeLoader Object within Flash/Flex. If you look within the class file you will find:

// ------------------------------------
// YOUTUBE METHODS
// ------------------------------------

public function loadVideoById (id:String, startSeconds:Number = 0):void{
  //trace("YouTubeLoader: loadVideoById");
  ExternalInterface.call("loadVideoById", id, startSeconds);
};

public function cueNewVideo (id:String, startSeconds:Number = 0):void{
  //trace("YouTubeLoader: cueNewVideo");
  ExternalInterface.call("cueNewVideo", id, startSeconds);
};

public function clearVideo ():void{
  //trace("YouTubeLoader: clearVideo");
  ExternalInterface.call("clearVideo");
};

// plus 17 more...

The ActionScript methods use the ExternalInterface class to simply call the appropriate function within the JavaScript API.

Requesting a Video

You can now request a video from within your ActionScript 3.0 file by calling functions using the player reference. For example, if you wanted to play the video when a user clicked a button, you would add a MouseEvent.CLICK event listener to your button. Like this:

// assuming your button was called 'myButton'
myButton.addEventListener(MouseEvent.CLICK, youtubeLoadVideoHandler);

And create an event handler method to handle the request. Like this:

private function youtubeLoadVideoHandler (event:MouseEvent):void{
  
  // assuming that '_youTubeLoader' is a reference to your YouTubeLoader object
  _youTubeLoader.loadVideoById( "u1zgFlCw8Aw" );
};

Outside of your Flash/Flex file you can optionally request a video by calling the corresponding JavaScript function directly. Like this:

 <a href="javascript:loadVideoById('u1zgFlCw8Aw')">Play</a> 

Issuing Additional Calls

Additional calls work exactly like Requesting a Video; from within ActionScript 3 you can simply call methods using the player reference. A full list of available methods is provided below.

Subscribing to Events

Subscribe to events by adding an event listener to the player reference. For example, to get notified when the player's state changes, add an event listener for YouTubeLoaderEvent.STATE_CHANGE. Like this:

// assuming that '_youTubeLoader' is a reference to your YouTubeLoader object
_youTubeLoader.addEventListener(YouTubeLoaderEvent.STATE_CHANGE, youTubePlayerStateChangeHandler);

And create an event handler method to handle the request. Like this:


private function youTubePlayerStateChangeHandler (event:YouTubeLoaderEvent):void{
  //trace("YouTubeIntegrationDemo: youTubePlayerStateChangeHandler");
  
  _stateField.text = event.state;
};

Available Operations

In order to call the YouTubePlayer API methods, you must first create an instance of the YouTubePlayer class within your ActionScript file and store a reference to the YouTubePlayer object you wish to control. This can be done by calling:

var _youTubeLoader:YouTubeLoader;
_youTubeLoader = new YouTubeLoader();

Public Methods

player.loadVideoById(id:String, startSeconds:Number = 0):void
Loads and plays video based on specified id.
player.cueNewVideo(id:String, startSeconds:Number = 0):void
Loads but does not automatically play video based on specified id.
player.clearVideo():void
Clears currently cued/loaded video.
player.setSize(w:Number, h:Number):void
Sets the size of YouTubePlayer instance.
player.play():void
Plays the currently cued/loaded video.
player.pause():void
Pauses the currently cued/loaded video.
player.stop():void
Stops the currently cued/loaded video.
player.seekTo(seconds:Number):void
Seeks to specified time within the currently cued/loaded video.
player.getPlayerState():String
Returns the current state of the currently cued/loaded video.
player.getBytesLoaded():Number
Returns the value of current bytes loaded of the currently cued/loaded video.
player.getBytesTotal():Number
Returns the value of total bytes loaded of the currently cued/loaded video.
player.getCurrentTime():Number
Returns the current position in time of the currently cued/loaded video.
player.getDuration():Number
Returns the current duration of the currently cued/loaded video.
player.getStartBytes():Number
Returns the start bytes of the currently cued/loaded video.
player.setVolume(newVolume:Number):void
Sets the volume of the currently cued/loaded video.
player.getVolume():Number
Returns the current volume of the currently cued/loaded video.
player.mute():void
Stores the current volume and changes the volume of the currently cued/loaded video to 0.
player.unmute():void
Returns the volume of the currently cued/loaded video to the last stored value when muted.
player.getEmbedCode():String
Returns the current YouTube embed code of the currently cued/loaded video.
player.getVideoUrl():String
Returns the current YouTube video url of the currently cued/loaded video.

Events

YouTubeLoaderEvent.LOADED
Fired once the Chromeless Player has successfully completed loading and is ready to accept operations calls.
YouTubeLoaderEvent.STATE_CHANGE
Fired whenever the player's state changes. The YouTubeLoader class translates the JavaScript API numbers to their related string values, the YouTubeLoaderEvent class stores the current event in a variable called state. Possible values are unstarted, ended, playing, paused, buffering, video cued. When the SWF is first loaded, it will broadcast an unstarted event. When the video is cued and ready to play, it will broadcast a video cued event.
YouTubeLoaderEvent.IO_ERROR
Fired when an error in the player occurs. There are two error codes possible: 100 is broadcasted when the video requested is not found. This occurs when a video has been removed (for any reason), or it has been marked as private. 101 is broadcasted when the video requested does not allow playback in the embedded players.

Notes On The Demo

For demonstration purposes we wanted to include the XHTML form fields, buttons and display UI below the embedded ActionScript 3 Wrapper. In order to have the swf file AND the XHTML update at the same time, we had to include two lines of code within the the 'youTubeLoader.js' JavaScript file located in 'deploy/_assets/js/' (Figure 3). You will want to remove the following two lines [69, 79] when you integrate this file into your project:

//------------------------------------
// SPECIAL YOUTUBE EVENT METHODS
//------------------------------------

function onYouTubePlayerReady(playerId) {

  if (checkObj()) {	
    obj.addEventListener("onStateChange", "onytplayerStateChange");
  };

  // PLEASE NOTE: For the purpose of this demo:
  // This calls a secondary method located in the index.html file allowing the html display to update.
  // You will most likely not need this, it's gross, remove this when you implement this code.
  secondaryOnYouTubePlayerReady(playerId);
}

function onytplayerStateChange(newState) {
    //alert("Player's new state: " + newState);
  obj.playerStateUpdateHandler(newState);

  // PLEASE NOTE: For the purpose of this demo:
  // This calls a secondary method located in the index.html file allowing the html display to update.
  // You will most likely not need this, it's gross, remove this when you implement this code.
  secondaryOnytplayerStateChange(newState)
}

The demo file and included ActionScript libraries are free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License. These files are distributed in the hope that it will be useful but without any warranty.

Conclusion

This article, demo and source files should provide you a solid overview of one relatively simple and reliable solution for integrating the YouTube API and embedded players into ActionScript 3 environments using the wrapper library we developed for our own projects. Since I've commented much of the code, it will ideally be fairly straightforward to explore and repurpose the libraries. It's not without limitations, and there is always room for improvement, re-factoring and enhancement. If you have any thoughts on this, feel free to contact me.

Author Bio


Matthew Richmond

Matthew Richmond boasts 14 years of interactive design, development and architecture experience. When not in the studio, he can be found teaching digital illustration/photography techniques and advanced ActionScript at the School of Visual Arts. Matthew is a founding partner and designer at choppingblock.com.