Since the end time…


Elapsed times since the Rapture per Wikipedia.

Making a layered brightcove player using the player api and beml



My goal was to build a Brightcove player that the layers the videoplayer and playlist in the same space with a way of toggling from the one to the other. I wanted to create minimal code external to the player and I didn’t want to do any custom Flash development.

Brightcove Experience Markup Language (BEML)

Brightcove offers their customers the ability to customize players via an XML extension called BEML which has a complete, if terse, DTDDoc.

I started with the BEML for a standard player template Video with Dropdown.

It contains the video player and the playlist layout I want to use. I basically want to stack the one on top of the other.

I duplicated it, renamed it and then edited the BEML.

BEML layout is pretty straightforward. It reminded me of java AWT. Components are laid out in canvasses with either vertical (VBox) or horizontal (HBox) arrangements.

<Runtime>
<Theme name=”Deluxe” style=”Light”/>
<Layout id=”application” boxType=”vbox”>
<VideoPlayer id=”videoPlayer” height=”275″ video=”{videoList.selectedItem}”/>
<Spacer height=”3″/>
<ComboBox id=”playlistCombo” height=”25″/>
<List id=”videoList” rowHeight=”75″ automaticAdvance=”true” data=”{playlistCombo.selectedItem.videoDTOs}” itemLeading=”2″ selectOnClick=”true”>
<ListItem boxType=”hbox”>
<Spacer width=”8″/>
<VBox width=”80″ height=”74″ vAlign=”middle”>
<ThumbnailButton height=”60″ data=”{currentItem}” source=”{currentItem.thumbnailURL}”/>
</VBox>
<Spacer width=”7″/>
<VBox>
<Spacer height=”3″/>
<TitleLabel height=”18″ text=”{currentItem.displayName}” truncate=”true”/>
<Label height=”52″ multiline=”true” text=”{currentItem.shortDescription}” truncate=”true”/>
</VBox>
<Spacer width=”3″/>
</ListItem>
</List>
</Layout>
</Runtime>

And there is an element called ViewStack which takes any child components and layers them one on top of the other.

So, the first change was to place the UI within a ViewStack and then make sure the children of that ViewStack are the layers I want (if I didn’t wrap the components correctly the playlist combobox would be one layer and the playlist another).

<Runtime>
<Theme name=”Deluxe” style=”Light”/>
<Layout id=”application” boxType=”vbox”>
<ViewStack id=”viewStack” selectedIndex=”0″>
<VBox>
<VideoPlayer id=”videoPlayer” height=”275″ video=”{videoList.selectedItem}”/>
<Spacer height=”3″/>
</VBox>
<VBox>
<ComboBox id=”playlistCombo” height=”25″/>
<List id=”videoList” rowHeight=”75″ automaticAdvance=”true” data=”{playlistCombo.selectedItem.videoDTOs}” itemLeading=”2″ selectOnClick=”true”>
<ListItem boxType=”hbox”>
<Spacer width=”8″/>
<VBox width=”80″ height=”74″ vAlign=”middle”>
<ThumbnailButton height=”60″ data=”{currentItem}” source=”{currentItem.thumbnailURL}”/>
</VBox>
<Spacer width=”7″/>
<VBox>
<Spacer height=”3″/>
<TitleLabel height=”18″ text=”{currentItem.displayName}” truncate=”true”/>
<Label height=”52″ multiline=”true” text=”{currentItem.shortDescription}” truncate=”true”/>
</VBox>
<Spacer width=”3″/>
</ListItem>
</List>
</VBox>
</ViewStack>
</Layout>
</Runtime>

Next, I need a component that lets me toggle back and forth from the player to the playlist and back. I chose to use a simple link. I placed it directly below the ViewStack.

<VBox width=”100%” hAlign=”right” height=”24″>
<Spacer height=”3″/>
<Link id=”moreOrLessLink” text=”Show more video…”/>
</VBox>

Next, I added some text below the videoplayer for the title and short description of the video currently loaded into the player.

<HBox>
<Spacer width=”6″/>
<VBox>
<TitleLabel id=”maintitle” height=”18″ text=”{videoPlayer.video.displayName}” truncate=”true”/>
<Label multiline=”true” height=”54″ text=”{videoPlayer.video.shortDescription}” truncate=”true”/>
</VBox>
<Spacer width=”6″/>
</HBox>

Finally, some inline CSS as allowed within the BEML dtd in order to have the text fit in the TitleLabel I just added and here’s the complete BEML for the revised player:

<Runtime>
<Theme name=”Flat” style=”Light”/>
</Style>
<Style id=”maintitle”><![CDATA[ .titleText
{
font-size:16;
font-weight:bold;
}
]]></Style>
<Layout id=”application” boxType=”vbox”>
<ViewStack id=”viewStack” selectedIndex=”0″>
<VBox>
<VideoPlayer id=”videoPlayer” height=”275″ video=”{videoList.selectedItem}”/>
<Spacer height=”3″/>
<HBox>
<Spacer width=”6″/>
<VBox>
<TitleLabel id=”maintitle” height=”18″ text=”{videoPlayer.video.displayName}” truncate=”true”/>
<Label multiline=”true” height=”54″ text=”{videoPlayer.video.shortDescription}” truncate=”true”/>
</VBox>
<Spacer width=”6″/>
</HBox>
</VBox>
<VBox>
<ComboBox id=”playlistCombo” height=”25″/>
<List id=”videoList” rowHeight=”75″ automaticAdvance=”true” data=”{playlistCombo.selectedItem.videoDTOs}” itemLeading=”2″ selectOnClick=”true”>
<ListItem boxType=”hbox”>
<Spacer width=”8″/>
<VBox width=”80″ height=”74″ vAlign=”middle”>
<ThumbnailButton height=”60″ data=”{currentItem}” source=”{currentItem.thumbnailURL}”/>
</VBox>
<Spacer width=”7″/>
<VBox>
<Spacer height=”3″/>
<TitleLabel height=”18″ text=”{currentItem.displayName}” truncate=”true”/>
<Label height=”52″ multiline=”true” text=”{currentItem.shortDescription}” truncate=”true”/>
</VBox>
<Spacer width=”3″/>
</ListItem>
</List>
</VBox>
</ViewStack>
<VBox width=”100%” hAlign=”right” height=”24″>
<Spacer height=”3″/>
<Link id=”moreOrLessLink” text=”Show more video…”/>
</VBox>
</Layout>
</Runtime>

I did the rest of the color customizations in the publishing tools in Brightcove Studio.

This only addresses the UI of the player. I now need to wire up events.

Brightcove Player API for Javascript

Now I need to enable toggling between the player and playlist when someone clicks on the link. Brightcove doesn’t enable this within BEML itself. You need to interact with the player externally using the Player API.

The first step is to make sure your player is enabled for the Javascript/Actionscript API. This is a checkbox on the Global Tab of the player settings. It is by default unchecked.

Now, you need to load the Brightcove Javascript library. I loaded the combined js file as described in the Brightcove help page.

Brightcove supplies an API document.

When a Brightcove player loads in a page, it calls an onTemplateLoaded method that you can place in the page to gain access to Brightcove’s objects and interact with the player. There are examples of this on a dozen sites.

function onTemplateLoaded(experienceID) { /*insert code here*/ }

For encapsulation, I’ll create my own Javascript object. Construct it in this method passing the experienceID.

function onTemplateLoaded(experienceID) { new SnsCompactListVideoPlayer(experienceID); }

In the constructor, I obtain a reference to the Brightcove module object, lookup the link element by its ID then add an event listener for click events.

function SnsCompactListVideoPlayer(experienceID)
{
var playerExperience = brightcove.getExperience(experienceID);
var experienceModule = playerExperience.getModule(APIModules.EXPERIENCE);

this.moreOrLessLink = experienceModule.getElementByID(“moreOrLessLink”); //the link that toggles between the videoplayer and the playlist
this.viewStack = experienceModule.getElementByID(“viewStack”);//the ui element that contains the videoplayer in one stack and the playlist in another
this.videoPlayer = experienceModule.getElementByID(“videoPlayer”);//the video player ui element
this.videoList = experienceModule.getElementByID(“videoList”);
if (this.moreOrLessLink != null && this.viewStack != null && this.videoPlayer != null)
{
//toggle between videoplayer and playlist based on clicks
this.moreOrLessLink.addEventListener(“elementClick”, bindMethodToObject(this.togglePlayer,this));
//when a video plays make sure the videoplayer element is visible
this.videoPlayer.addEventListener(BCMediaEvent.PLAY, bindMethodToObject(this.togglePlayerToPlay, this));
}
}

I had to bind the click event to the instance of the video player — otherwise when the event fires, the this keyword references the calling page not the player instance. I did this using the apply method.

function bindMethodToObject(method, object)
{
return function() { return method.apply(object, arguments); }
}

In the event handler, I check which layer of the ViewStack is topmost (selectedIndex) and switch to the other one making sure to pause the video if it is currently playing whenever someone clicks to view the playlist.

SnsCompactListVideoPlayer.prototype.togglePlayer = function(e)
{
if (this.viewStack.getSelectedIndex() == 0)
{
this.viewStack.setSelectedIndex(1);
this.moreOrLessLink.setText(“Return to player…”);
if (this.videoPlayer.isPlaying())
{
this.videoPlayer.pause();
}
}
else
{
this.viewStack.setSelectedIndex(0);
this.moreOrLessLink.setText(“Show more video…”);
}
}

I also wired an event to the video player to make sure that whenever the player plays, it becomes the selectedIndex in the ViewStack. This handles whens someone clicks on a video in the playlist.

/*
* Simon & Schuster Compact List Video Player
* Script by Ken Judy
* May 1, 2011
*/

function SnsCompactListVideoPlayer(experienceID)
{
var playerExperience = brightcove.getExperience(experienceID);
var experienceModule = playerExperience.getModule(APIModules.EXPERIENCE);

this.moreOrLessLink = experienceModule.getElementByID(“moreOrLessLink”); //the link that toggles between the videoplayer and the playlist
this.viewStack = experienceModule.getElementByID(“viewStack”);//the ui element that contains the videoplayer in one stack and the playlist in another
this.videoPlayer = experienceModule.getElementByID(“videoPlayer”);//the video player ui element
this.videoList = experienceModule.getElementByID(“videoList”);
if (this.moreOrLessLink != null && this.viewStack != null && this.videoPlayer != null)
{
//toggle between videoplayer and playlist based on clicks
this.moreOrLessLink.addEventListener(“elementClick”, bindMethodToObject(this.togglePlayer,this));
//when a video plays make sure the videoplayer element is visible
this.videoPlayer.addEventListener(BCMediaEvent.PLAY, bindMethodToObject(this.togglePlayerToPlay, this));
}
}

function bindMethodToObject(method, object)
{
return function() { return method.apply(object, arguments); }
}

SnsCompactListVideoPlayer.prototype.togglePlayer = function(e)
{
if (this.viewStack.getSelectedIndex() == 0)
{
this.viewStack.setSelectedIndex(1);
this.moreOrLessLink.setText(“Return to player…”);
if (this.videoPlayer.isPlaying())
{
this.videoPlayer.pause();
}
}
else
{
this.viewStack.setSelectedIndex(0);
this.moreOrLessLink.setText(“Show more video…”);
}
}
SnsCompactListVideoPlayer.prototype.togglePlayerToPlay = function(e)
{
if (this.viewStack.getSelectedIndex() == 1)
{
this.viewStack.setSelectedIndex(0);
this.moreOrLessLink.setText(“Show more video…”);
}
}