Creating Custom Player Components

In this topic, you will learn how to develop a custom player component that you can incorporate in a BEML player template or add to a player as a plug-in. Custom player templates are available only for Video Cloud Pro and Enterprise customers. If you are interested in upgrading your Video Cloud account, please contact Brightcove for more information.

About custom components

Video Cloud 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. There are two basic types of components you can use with Video Cloud players:

  • Modules, which are defined in a BEML player template with a <Module> element or else added to a player using the Plug-ins setting in the Publishing module. Modules are best suited for components that don't need to be rendered in the player, but can use the Player API to add itself to the player display. Modules are loaded into the player before the player content, so you can be sure they are present and loaded before the video begins to play.
  • SWFLoader components, which are defined in a BEML player template with a <SWFLoader> element. SWFLoader components are best suited for components that you do want to be rendered in the player. The <SWFLoader> element has size and positioning attributes that make it easy to specify how the component interacts visually with the rest of the player template; it also has an includeInLayout attribute that you can set to false if you don't want the component to be rendered. A SWFLoader component loads asynchronously; the player does not wait for it to load completely before loading the video content.

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, as described in Adding a Custom Component to a Player Template.

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 Video Cloud 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 SWF files to be loaded by the Video Cloud player. The first BEML tag, SWFLoader, is an element that is added to the display list and positioned by the player, loading your SWF as a child. The second tag, Module, specifies a SWF that is not added to the display list, but is a non-rendering element that you can use to add logic to the player, such as calling a custom analytics server, providing different logic for rendition selection, and so forth. In both cases, how you set up your SWF for interaction with the Video Cloud Player ActionScript API is the same. Let's look at the recommended code to use when developing your application.

Using BrightcoveModuleWrapper

When interacting directly with the Video Cloud 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. Download the Video Cloud Player SDK. This download includes a SWC file 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. This code checks 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 simply checks to see whether the ExperienceModule was present (which it should be, or the handler wouldn't get called) and call the checkReady() method, which will be explained in the next steps.
  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 must perform another important check that should be present in any SWFLoader or Module SWF, and that is whether the player has fired the TEMPLATE_READY event yet. The ExperienceModule has a getReady() method you can use to check this. If the player is not ready, 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 simply calls the same initialize() method we had at 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 SWF files, you can ensure that the API is ready and you can interact freely with the player instance into which your SWF loads. Since this code is recommended and is unlikely to change from module to module, the Video Cloud Player API SWC also provides this code in an abstract class that you can extend, as demonstrated in the following section.

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 Video Cloud 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, download the Video Cloud Player SDK. This includes a SWC file 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 instead of Sprite.
  3. package {
      import com.brightcove.api.CustomModule;
      public class Module extends CustomModule {
      }
    }
  4. You must 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 you invoke the initialize() method, 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 the following 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 Creating a Player with a A 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 file="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.

Post new comment

The content of this field is kept private and will not be shown publicly.
0

Comments