Creating Custom Player Components

Applies to Roles
Developer
Version
Brightcove 4
Modules
BEML
Edition
Pro, Enterprise

Custom player templates are available only for Brightcove Pro and Enterprise customers. If you are interested in upgrading your Brightcove account, please contact Brightcove for more information.

Brightcove player templates enable you to create your own custom components. Custom components can be visual components, like a sidebar listing popular videos or a banner displaying a promotion, or non-visual components, like an analytics SWF.

Main Steps

The main steps in developing and deploying a custom player component are:

  1. Create a Flash file.
  2. Compile your Flash file into a SWF.
  3. Host the SWF at a URL.
  4. Incorporate the SWF into your player by referring to it in your player template in a <SWFLoader> or <Module> element.

This topic includes the following sections:

Developing a custom component

A custom component is created as a Flash file that you compile into a SWF. You can develop a custom component using ActionScript 3 and the Brightcove Player API. Note that an ActionScript 2 SWF will not be able to interact with the player.

There are two tags available through BEML that allow you to specify SWFs to be loaded by the Brightcove player. The first, SWFLoader, is an element that is added to the display list and positioned by the player, loading your SWF as a child. The second, Module, specifies a SWF that is not added to the display list, but is a non-rendering element that can be used to add logic to the player, such as calling a custom analytics server, providing different logic for rendition selection, etc. In both cases, how you set up your SWF for interaction with the Brightcove Player ActionScript API is the same. Let's look at the recommended code you should use when starting development of your application.

Using BrightcoveModuleWrapper

When interacting directly with the Brightcove player, you do not have access to the internal classes to compile against. Because of this, the code you write will have to cast all the API classes to Object instances. This prevents any useful strong typing, code hinting or compile-time checking that an IDE like Flex Builder offers. To get around this limitation, you can use special classes to wrap the internal player classes.

  1. This first thing you will want to do is download the Brightcove Player SDK from this page. This includes a SWC that contains classes for wrapping the Player API classes, giving you code hinting and strong typing, as discussed in this article.
  2. Any Module or SWFLoader SWF will need a setInterface() method defined if you wish to gain access to the API (which might not always be required by a SWFLoader that doesn't interact directly with the player). The object that is passed to the method implements flash.events.IEventDispatcher and can be wrapped in the com.brightcove.api.BrightcoveModuleWrapper class in order to get strong typing and code hints.
  3. // be sure to import com.brightcove.api.BrightcoveModuleWrapper 
                         
    private var _player:BrightcoveModuleWrapper; 
                         
    public function setInterface(player:IEventDispatcher):void { 
      _player = new BrightcoveModuleWrapper(player); 
    }
  4. The first thing to check is whether the ExperienceModule in the player is available. If it is not, it means that the API was not enabled for the player instance and so the required API classes were not loaded. You can force the loading of these classes through a call to loadModules(), listening for when MODULES_LOADED gets dispatched.
  5. // be sure to import com.brightcove.api.APIModules 
    // be sure to import com.brightcove.api.events.ExperienceEvent 
    // be sure to import com.brightcove.api.modules.ExperienceModule 
                         
    private var _player:BrightcoveModuleWrapper; 
    private var _experienceModule:ExperienceModule; 
                         
    public function setInterface(player:Object):void { 
      _player = new BrightcoveModuleWrapper(player); 
      _experienceModule = _player.getModule(APIModules.EXPERIENCE) as ExperienceModule; 
      if (_experienceModule == null) { 
        _player.addEventListener(ExperienceEvent.MODULES_LOADED, onModulesLoaded); 
        _player.loadModules(); 
      } else { 
         checkReady(); 
      } 
    }
  6. In the last step, we added an onModulesLoaded() handler. This would simply check to see whether the ExperienceModule was present (which it should be, or the handler wouldn't get called) and call the checkReady() method, which I've yet to present.
  7. private function onModulesLoaded(event:ExperienceEvent):void { 
      _player.removeEventListener(ExperienceEvent.MODULES_LOADED, onModulesLoaded); 
      _experienceModule = _player.getModule(APIModules.EXPERIENCE) as ExperienceModule; 
      if (_experienceModule != null) { 
        checkReady(); 
      } 
    }
  8. The checkReady() method needs to do another important check that should be present in any SWFLoader or Module SWF, and that is that the TEMPLATE_READY event has been fired by the player. The ExperienceModule has a getReady() method you can use to check this. If the player is not ready, you should establish a listener for the TEMPLATE_READY event.
  9. private function checkReady():void { 
      if (_experienceModule.getReady()) { 
        initialize(); 
      } else { 
        _experienceModule.addEventListener(ExperienceEvent.TEMPLATE_READY, onTemplateReady); 
      } 
    }
  10. The onTemplateReady() handler would simply call the same initialize() method we had in checkReady(), which I will present last.
  11. private function onTemplateReady(event:ExperienceEvent):void { 
      _experienceModule.removeEventListener(ExperienceEvent.TEMPLATE_READY, onTemplateReady); 
      initialize(); 
    }
  12. The final step is to code the initialize() method. At this point, the player is ready and the API has been loaded. You are free to do all of the API interaction.
  13. private function initialize():void { 
      // all custom API interaction code 
    }
  14. The full code listing for a class that uses these recommended steps follows.
  15. package { 
                         
      import com.brightcove.api.APIModules; 
      import com.brightcove.api.BrightcoveModuleWrapper; 
      import com.brightcove.api.events.ExperienceEvent; 
      import com.brightcove.api.modules.ExperienceModule; 
                         
      import flash.display.Sprite; 
      import flash.events.IEventDispatcher; 
                         
      public class Module extends Sprite { 
                         
        private var _player:BrightcoveModuleWrapper; 
        private var _experienceModule:ExperienceModule; 
                         
        private function initialize():void { 
          // all custom API interaction code 
        } 
                         
        private function checkReady():void { 
          if (_experienceModule.getReady()) { 
            initialize(); 
          } else { 
            _experienceModule.addEventListener(ExperienceEvent.TEMPLATE_READY, onTemplateReady); 
          } 
        } 
                         
        private function onTemplateReady(event:ExperienceEvent):void { 
          _experienceModule.removeEventListener(ExperienceEvent.TEMPLATE_READY, onTemplateReady); 
          initialize(); 
        } 
                         
        private function onModulesLoaded(event:ExperienceEvent):void { 
          _player.removeEventListener(ExperienceEvent.MODULES_LOADED, onModulesLoaded); 
          _experienceModule = _player.getModule(APIModules.EXPERIENCE) as ExperienceModule; 
          if (_experienceModule != null) { 
            checkReady(); 
          } 
        } 
     
        public function setInterface(player:IEventDispatcher):void { 
          _player = new BrightcoveModuleWrapper(player); 
          _experienceModule = _player.getModule(APIModules.EXPERIENCE) as ExperienceModule; 
          if (_experienceModule == null) { 
            _player.addEventListener(ExperienceEvent.MODULES_LOADED, onModulesLoaded); 
            _player.loadModules(); 
          } else { 
            checkReady(); 
          } 
        }  
      }  
    }

By using this as the initial code for your SWFLoader and Modules SWFs, you can ensure that the API is ready and you can interact freely with the player instance into which your SWF loads. Of course, since this code is recommended and will unlikely change from module to module, the Brightove Player API SWC also provides this code in an abstract class which you can extend. The following section demonstrates how.

Using CustomModule

The code in the previous section is a simple template for you to work from when developing custom modules. Since the code will most likely remain consistent, the Brightcove API SWC offers an additional class, CustomModule, which you can extend to get this exact functionality. All that is required is that you extend a single method, initialize(). The following steps show how.

  1. Just as with the previous section, the first thing you will want to do is download the Brightcove Player SDK from this page. This includes a SWC that contains classes for wrapping the Player API classes, giving you code hinting and strong typing, as discussed in this article.
  2. When you set up your class, extend CustomModule as opposed to Sprite.
  3. package {  
      import com.brightcove.api.CustomModule;  
      public class Module extends CustomModule { 
      }  
    }
  4. You need to override a single protected method, initialize(), which takes no parameters and returns no value.
  5. package {  
      import com.brightcove.api.CustomModule;  
      public class Module extends CustomModule {  
        override protected function initialize():void { 
        }  
      }  
    }
  6. When the initialize() method is invoked, you are free to carry out all of your required interaction. You can gain access to the player through the player property, which contains a BrightcoveModuleWrapper instance. The following shows how you might get access to the ExperienceModule.
  7. package { 
     
      import com.brightcove.api.APIModules; 
      import com.brightcove.api.CustomModule; 
      import com.brightcove.api.modules.ExperienceModule; 
     
      public class Module extends CustomModule { 
     
        private var _experienceModule:ExperienceModule; 
     
        override protected function initialize():void { 
          _experienceModule = player.getModule(APIModules.EXPERIENCE) as ExperienceModule; 
        }  
      }  
    }

Using CustomModule as the base class for your Module or SWFLoader SWF will help ensure that you always have access to the API and that you do not interact with the player until it is ready. Because it also exposes a player property that is a BrightcoveModuleWrapper instance, you will get code hints and strong typing as you interact.

Custom component example

Let's look at an example of how to create a custom component and use it in a player template. This example includes these files:

  • a component that loads content and manipulates the Video Player component (download the source for ThumbGrid.fla)
  • a player template that incorporates the ThumbGrid component (thumb_grid.xml)
  • an HTML page that includes the player publishing code and a JavaScript fragment that passes a playlist ID to the ThumbGrid component (thumb_grid.html)

The ThumbGrid custom component

The ThumbGrid custom component uses the Player API to load content and manipulate the video player component. You can download the source code for this component. The ThumbGrid component defines the following functions:

  • onPlaylistLoad - loads the Content module of the Player API and calls showPlaylist to display a playlist.
  • onTemplateReady - gets the playlist ID that is loaded in by JavaScript in the HTML page.
  • onThumbClick - when the viewer clicks on a thumbnail in the component, loads the selected video into the video player.
  • showPlaylist - displays the name and thumbnail for each of the videos in the playlist.
  • setInterface - the interface that enables the component to interact with the Player API.
  • setSize - uses the setSize function of the TileList component to set the width and height.

The ThumbGrid player template

The ThumbGrid player template is very simple; it incorporates two components, a standard VideoPlayer component and the custom ThumbGrid component, loaded using the SWFLoader element. The player template looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Runtime SYSTEM "http://admin.brightcove.com/dtds/beml_rt.dtd">
<Runtime>
  <Layout width="526" height="412">
    <VideoPlayer id="videoPlayer" x="3" y="3" 
                 width="486" height="406" depth="1"/>
    <SWFLoader width="200" height="406" x="492" y="3" 
               source="https://help.brightcove.com/developer/docs/playerapi/ThumbGrid.swf"
               depth="2"/>
  </Layout>
</Runtime>

The attributes of the SWFLoader element specify the size and positioning of the ThumbGrid component within the player layout.

The player HTML page

The example HTML page that loads the player includes the player publishing code and a JavaScript snippet that passes a playlist to the player:

<script language="JavaScript" type="text/javascript">
  function getPlaylistID() {
    return 60818943;  
  }
</script>

For another example of a custom player component, see Custom Component Sample: Social Sharing Widget.

Passing values to a component at runtime

Some custom components may need to be provided with configuration values from the page or the player at runtime. For example, a component may require an id to be supplied so it can properly associate tracking data, or perform real-time lookups for other settings.

One solution to this problem is to leverage Flash's ability to call out to JavaScript. For example, you might have a playlist id specified in the HTML page by JavaScript and retrieve that id in the component's SWF. The JavaScript fragment in the HTML page would look like this:

<script language="JavaScript" type="text/javascript">
  function getPlaylistID() {
  return 60818943;
  }
</script>

In the Flash component, this playlist ID value is retrieved and used to load a playlist through the Player API:

function onTemplateReady(event:Event):void {
  var contentModule:Object = mPlayer.getModule("content");
  if (contentModule) {
      contentModule.addEventListener("playlistLoad", onPlaylistLoad);
      contentModule.getPlaylistAsynch(ExternalInterface.call("getPlaylistID"));
  }
}

This strategy may not work if you cannot rely upon the use of JavaScript in the page. An alternative strategy is to specify query parameters for the URL to the external SWF in the <SWFLoader> or <Module> declaration. Using standard Flash development practices, you can then access those URL values and the component can dynamically adjust its behavior, using the values in the query parameters. For example:

<Modules>
  <Module source="http://hostname.com/player/foo.swf?playlistID='1234'&amp;HAlign='left'"/>
</Modules>

Note how the ampersand (&) that joins the query parameters is escaped as &amp; – the Module element, like everything in your player template, needs to be valid XML.

Another way to pass a value to a component is by using the data attribute in the Module or SWFLoader BEML element. For example, if you just need to pass a single string to a loaded SWF, you can put the value in a file and reference it in the BEML like this:

<SWFLoader source="http://my.example.com/path/to/swf/A.swf"
           data="http://my.example.com/content/data.xml" />

In your SWF, create implicit get and set functions for the property data:

public function get data():String {}
public function set data(value:String):void {}

When the player loads your SWF, the data from the file set in the BEML will be passed to the implicit setter function if the function exists.

Accessing the Player API from a custom component

When a player loads a custom component SWF, the custom component can access the Player API using the loadModules() method. First, use the player's getModule() method to test whether the player has already loaded the Player API, then use loadModules() to load the Player API if needed. For example:

public function setInterface(player:Object):void {
    _player = player;
    _experienceModule = _player.getModule("experience");
    // if experience module is defined, the API for player is enabled
    if (_experienceModule) {
      checkTemplateReady();
   } else {
   // if experience module is not defined, the API for player is disabled
   // and needs to be loaded
      _player.addEventListener(Event.COMPLETE, onModulesLoaded);
      _player.loadModules();
   }
}

When setInterface() is called and the player is passed, the event listener listens for Event.COMPLETE fired by the player, then invokes loadModules(). This causes the API module SWF to be loaded by the player, allowing access to the Player API.

Note: This technique works only for custom component SWFs loaded into a player, not for wrapper applications, which still need to enable the Player API through the Publishing module.

Tags
custom component, widget