In this topic you will learn how to create a custom SWF component that counts the number of times a video is played in a player. Thanks to Alex Kieft from DigiNovations for this example.
This example was provided by Alex Kieft from DigiNovations. DigiNovations is a full service video production company, designing both media content and interactive experiences for the world wide web. Together with ChannelOne Marketing Group, DigiNovations offers a complete solution to creating, deploying and promoting video experiences on the internet and beyond.
This article describes how to create a custom hit counter component using the Video Cloud Player and Media APIs, BEML, and ActionScript 3. The component code has been written as the document class for a Flash CS4 or Flex 3 ActionScript project, but it could easily be adapted to work in other circumstances. The Brightcove Player SWC and AS3CoreLib SWC are also required.
The steps outlined in this topic are for Flash based players and will not work in HTML5 players and may prevent HTML5 players from functioning.
Video Cloud counts the number of times your videos are played. You can retrieve this number, or hit count, using the Media API. Then, the number of plays of a video can be displayed in your player using the sample BEML and custom SWF component provided in this example.
The goal of this topic is to build a custom Video Cloud player that will be able to display the number of views, or hits, that the playing video has received. In this case, we will be modifying a standard Video Player template to add a custom hit counter component to the media control bar. Here's the demo player for this example, which uses a BEML template with a custom SWF module to retrieve and display the hits (that is, the total plays) for the video.
This example has three components, which you can download as a .zip file:
The first thing you need to do is create a new custom player template in the Publishing module. While you are in the Publishing module, make sure to select the "Enable ActionScript/JavaScript APIs" option under the player settings. The BEML we use for the custom player used in this example is included in the .zip file. The BEML for this player is based on the BEML template for a standard single-title player. We've made three modifications to the standard single Video Player template:
Video metadata, such as the title or description, can typically be displayed in a Video Cloud player by simply modifying its BEML template. For example, to show the title of the video that is currently playing, you can bind the text property of a Label component to the displayName property of the video:
<Label text="{videoPlayer.video.displayName}" />
The number of times a video has been played isn't available for data binding in BEML in this way. However, we can retrieve that information using the Media API. We plan to display that information in a Label component identified as "hitCounter", with the text property left empty:
<Label id="hitCounter" width="56" height="17" vAlign="middle" hAlign="left" text="" />
The Module element loads our SWF into the player. Unlike the SWFLoader component, a Module will not appear as a visible element in the player. Instead, it allows us to provide logic to interact with the Player API and manipulate our BEML components, including our hit counter.
In addition to the SWF location, we need to supply an additional URL parameter that contains our Media API token. The SWF will use this token to connect to the Media API. Alternatively, you could choose to hard-code this token into your SWF, but passing it as a parameter is a more flexible approach.
<Modules> <Module file="http://<urlToTheSWFHostedbyYou>?token=[your Media API Token]"/> </Modules>
The advantage here of using a <Module> and a <Label> over using a <SWFLoader> BEML component to display the hits is that the <Label> can be easily formatted in Video Cloud Studio along with the other player components.
Now that the BEML template is ready, we need to build the SWF that the Module component will load. We are going to create an ActionScript project in FlexBuilder 3, but you could just as easily work in Flash CS4 or use the Flex SDK. The ActionScript source code for this component, hit-counter.as, is included in the example .zip file.
After creating a new project called HitCounter.as, we are going import two SWC libraries. The first library is the Brightcove Player SWC, which provides several useful classes for connecting to the Player API, as well as code-hinting in FlexBuilder.
The second library is the AS3CoreLib SWC, which we are just going to use for parsing the JSON responses that come back from the Media API.
For simplicity, all of our code will be contained in a single document class, HitCounter, which will extend the CustomModule class provided by the Player SWC. The nice thing about extending CustomModule is that it will automatically take care of connecting to the Player API for us, and then call its initialize function only after it is loaded and ready for interaction. We can override the initialize function and supply our own code to get things started.
package {
import com.brightcove.api.CustomModule;
public class HitCounter extends CustomModule {
public function HitCounter() {
}
/**
* Initializes the component once the Brightcove API is ready for access.
*/
override protected function initialize():void {
}
}
}
Before we start adding the logic to interact with the Player API, it is useful to create a few shortcuts to the modules we need. The CustomModule class already provides a reference to the player, so we can use that to retrieve the VideoPlayerModule and the ExperienceModule.
import com.brightcove.api.APIModules;
import com.brightcove.api.modules.ExperienceModule;
import com.brightcove.api.modules.VideoPlayerModule;
/**
* Returns a reference to the VideoPlayerModule.
*/
public function get videoPlayerModule():VideoPlayerModule {
return player.getModule(APIModules.VIDEO_PLAYER) as VideoPlayerModule;
}
/**
* Returns a reference to the ExperienceModule..
*/
public function get experienceModule():ExperienceModule {
return player.getModule(APIModules.EXPERIENCE) as ExperienceModule;
}
We are also going to create a shortcut to the Media API token that we passed to the SWF as a URL parameter. We'll need this when we start making calls to the Media API. While we're at it, let's also store the Media API service URL for later reference.
private var _mediaService:String = 'http://api.brightcove.com/services/library';
/**
* Gets the mediaKey from the token parameter in the SWF URL:
* ../HitCounter.swf?token=[Media API token]
*/
private function get _mediaKey():String {
return root.loaderInfo.parameters['token'];
}
Now that everything is set up to interact with the APIs, we are going to create a chain of events to update the hit counter. The steps in the chain are:
Every time a new video loads in the player, the VideoPlayerModule dispatches a "change" event. We'll listen for that event, and handle it by calling the next step in the chain.
/**
* Adds event listeners to the Brightcove Modules.
*/
private function addListeners():void {
videoPlayerModule.addEventListener(MediaEvent.CHANGE, handleMediaChange);
}
/**
* Handles any change of media in the player, which occurs when a new video loads.
*/
private function handleMediaChange(event:MediaEvent):void {
updateMedia();
}
The only time this will not work is when the first video loads, which does not dispatch a "change" event, so we want to make sure we call the updateMedia() function once at initialization as well.
/**
* Initializes the component once the Brightcove API is ready for access.
*/
override protected function initialize():void {
updateMedia();
addListeners();
}
Now that we know a new video has loaded, we need to retrieve its Video Cloud ID to pass along to the Media API. The current video's data object is available through a call the VideoPlayerModule's getCurrentVideo() function.
/**
* Whenever the media in the Video Cloud player has changed, a new request to the Media API needs to be
* made to retrieve metadata not available directly via the Player API.
*/
private function updateMedia():void {
var video:VideoDTO = videoPlayerModule.getCurrentVideo();
if (video) {
getPlaysTotal(video.id);
}
}
/**
* Generates a request to the Video Cloud Media API to retrieve the playsTotal for the video.
*/
private function getPlaysTotal(id:Number):void {
}
The next step is the most complicated step, because it involves communicating asynchronously with the Media API. To do so, we need to construct a URLRequest, send it with a URLLoader, and listen for the result from the server. We already have the URL of the Media API service stored in _mediaService, so now we need to attach the URLVariables containing the parameters for the API call.
var request:URLRequest = new URLRequest(_mediaService); var loader:URLLoader = new URLLoader();
For this application, we are going to invoke the find_video_by_id method from the Media API, and limit the result to the playsTotal field. We also need to pass the video ID and our Media API token with our request.
var variables:URLVariables = new URLVariables(); variables.token = _mediaKey; variables.command = 'find_video_by_id'; variables.video_id = id; variables.video_fields = 'playsTotal';
Finally, we put all the pieces together and try sending the request. We need to add a listener to handle the response, as well as a few error catchers in case something goes wrong.
request.data = variables;
request.method = URLRequestMethod.GET;
loader.dataFormat = URLLoaderDataFormat.TEXT;
loader.addEventListener(Event.COMPLETE, handleGetPlaysTotal);
try {
loader.load(request);
}
catch (error:ArgumentError) {
trace("An ArgumentError has occurred.");
}
catch (error:SecurityError) {
trace("A SecurityError has occurred.");
}
When the Media API request completes, the response is handled by a function that can parse the result from a JSON string to an ActionScript object, which will contain the playsTotal data needed for our hit counter. Decoding the JSON string is the job of the JSON utility in the AS3CoreLib SWC.
/**
* Handles the response from the Video Cloud Media API for the request for the playsTotal data.
*/
private function handleGetPlaysTotal(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
var response:Object = JSON.decode(loader.data);
if (response.playsTotal) {
updateHits(response.playsTotal);
}
}
Finally, we need to update our BEML hit counter to display the results we got back from the Media API.
Note: There is a delay in reporting information, and newly added videos will not return a hit count, even if everything is correctly set up.
We can use the ExperienceModule's getElementByID() method to get a reference to the hit counter we created in the BEML template, and then use the Label element's setText method to update the text.
/**
* Identifies the Label component in the Video Cloud player's BEML template designated as the
* "hitCounter" and updates its text to reflect the number of playsTotal retrieved from Media API.
*/
private function updateHits(hits:Number):void {
var hitCounter:Label = experienceModule.getElementByID('hitCounter') as Label;
if (hitCounter) {
hitCounter.setText(hits + ' views');
}
}
If you are happy with this functionality as is, you can re-use the SWF component directly. However, if you want to tweak it, you can download the ActionScript code, make your modifications, and then compile the new SWF file. Once you have the SWF, upload it to your web server and plug the SWF's URL into the BEML template.
To deploy the player, we need to:
And that's it!
This example could be extended in a variety of ways. For one, the hitCounter label could be placed anywhere in a BEML template, and not in the MediaControls as in our example. You could also retrieve and display other data using the Media API. For instance, if you wanted to give your users an option to download the H.264 source file of your videos, you could use the Media API to retrieve the location of that file, and then assign it as the url of a Link element. Or, instead of relying on Video Cloud's internal statistics, you could retrieve data back from your own analytics provider and display that in the player. There are endless possibilities!
Post new comment
Comments