This customized player shows what I believe to be the simplest and most effective approach to a basic implementation of subtitling (or captioning) in normal and full screen mode.

Cue point definitions are not required, this example uses the video progression index and standard subtitling text formats. This means it’s really easy to use this with videos for which you have the subtitling text and time indexes available in a standard format like .srt.

The article also shows how to use a free subtitling tool to create subtitle files you can use with this approach.

The subtitling text size will automatically switch when in player or full screen mode.

And here’s how you do it…

Creating Subtitle Files

There are several standards for subtitle text files. What they all have in common is a combination of time codes and the text to display. In this rudimentary example, I’ll be using a plain text, format, which is widely used and has the advantage of having free subtitle editing tools available.

I often use a free tool called SubCreator, which you can download here. It’s a basic subtitle editor with nifty keyboard shortcuts that saves your subtitles in easily readable text files. I recommend you use Windows Media Player (WMP) as the viewer and download the FLV codec for WMP from the same site. That way, you can view Flash Streaming Video files from WMP and also in the subtitle editor.

The text file for the movie shown above looks something like this:

00:00:23.0:My Son
00:00:25.5:The day you were born -
00:00:26.7:the very forests of Lordareon whispered the name -
00:00:34.0:Arthas.
00:01:21.0:My child
00:01:24.0:I watched with pride as you grew into a weapon -
00:01:29.5:Of righteousness.
00:01:39.0:Remember - our line has always ruled
00:01:43.0:with wisdom, and strength.
00:01:48.5:And I know you will show restraint
00:01:51.6:when exercising your great power.
00:02:46.3:But the truest victory, my son,
00:02:49.7:is sturring the hearts of your people.
00:03:01.4:I tell you this
00:03:04.0:for when my days have come to an end,
00:03:08.4:You shall be king.

Pretty straightforward. So now let’s get down to integrating this with BrightCove 3.

Creating a Standard Flash Embedded Player

I’ve already covered the basics for this in several other articles, so here’s just a quick rundown:

  • Create a player in the BC3 Console
  • Copy the ActionScript code for the player from the console
  • Start a new Flash project
  • Save the pasted code as BrightcovePlayer.as withing your project

Then add the standard code to embed the player into your Flash project:

import BrightcovePlayer;
 
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
 
Security.allowDomain("*");
 
var bcp:BrightcovePlayer = new BrightcovePlayer();
bcp.addEventListener("templateLoaded", templateLoaded);
addChild(bcp);
 
var mPlayer:Object;
var mContent:Object;
var mExp:Object;
 
function templateLoaded(evt:Event):void
{
	mPlayer = bcp.getModule("videoPlayer");
	mContent = bcp.getModule("content");
    mExp = bcp.getModule("experience");
 
	mExp.addEventListener("templateReady", templateReady);
}

For details, please refer to this BC3 Player API article.

Reading the Subtitle File

Since this is a basic example, I’ll just insert the text code into an array of strings, but if you extend on the principle, the subtitle file could be served by a web server dynamically.

var subtitles:Array = new Array(16);
subtitles[0] = "00:00:23.0:My Son";
subtitles[1] = "00:00:25.5:The day you were born -";
...

Setting up for displaying subtitles

We’ll be creating two overlays: for normal andfull screen display. For this purpose, I’ve created two simple movieclips that contain a dynamic TextField with the appropriate font and color settings, and added these to the library. Why don’t I put them right into the Flash movie in a separate layer? Because the BC3 player is created dynamically and placed in the movie with the addChild() function. This puts the player on the top visible layer in your Flash movie and thus obscures everything that was created statically. So we’ll create the subtitle movieclips dynamically as well – after we’ve created the BC3 player. (Don’t forget to expose your movieclips with Linkage).

We’ll also need to know which text is displaying, and how long it’s been on the screen, so we can set a timeout.

The last thing we need is to know the timecode of the movie playing. Fortunately, all we need to do is add an event listener for the BC3 Player.

The code for all of this is pretty straightforward:

var cursub:String = "";
var secs:int = 0;
 
var st:Subtitle = new Subtitle();
addChild(st);
st.visible = false;
st.x = 0;
st.y = 360;
 
var ft:FSTitle = new FSTitle();
addChild(ft);
ft.visible = false;
 
mPlayer.addEventListener("videoProgress", videoProgress);

And now all we need to do is code the videoprogress event handler. This will check the time code and the state of the player (normal or full screen), and display the corresponding subtitle movieclip, while making the other invisible. To do so, we simply compare the time code of the stream to the timecode in the text. If they correspond, we set the Caption of the visible Subtitle movieclip and start a countdown of 4 seconds:

function videoProgress(evt:Object):void
{
 
	var time : Number = evt.position;
 
	var seconds : int = time % 60;
	var minutes : int = ( time / 60 ) % 60;
	var hours : int = (time / 3600 ) % 3600;
 
	var sText:String = addLeading( "0", String( hours ), 2 ) + ":" + addLeading( "0", String( minutes ), 2 ) + ":" + addLeading( "0", String( seconds ), 2 );
 
	for(var i:int = 0; i < 16; i++)
	{
		var ws:String = subtitles[i];
		if( ws.indexOf(sText) == 0 )
		{
			cursub = ws;
			secs = 0;
			break;
		}
	}
 
	secs++;
	if (secs >= 100 )
	{
		cursub = "";
	}
 
	st.Caption.text = cursub.substr(11,255);
	ft.Caption.text = st.Caption.text;
 
	if( stage.displayState == StageDisplayState.FULL_SCREEN)
	{
		ft.x = 0;
		ft.y = stage.stageHeight - 120;
		ft.Caption.width = stage.stageWidth;
		st.visible = false;
		ft.visible = true;
	}
	else
	{
		st.visible = true;
		ft.visible = false;
	}
}

That’s it!

Full Code Listing

import BrightcovePlayer;
import flash.display.Stage;
import flash.display.StageDisplayState;
import flash.display.*;
import flash.events.*;
import flash.geom.Rectangle;
 
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
 
Security.allowDomain("*");
 
var bcp:BrightcovePlayer = new BrightcovePlayer();
bcp.addEventListener("templateLoaded", templateLoaded);
addChild(bcp);
 
var st:Subtitle = new Subtitle();
addChild(st);
st.visible = false;
st.x = 0;
st.y = 360;
 
var ft:FSTitle = new FSTitle();
addChild(ft);
 
ft.visible = false;
 
var mPlayer:Object;
var mContent:Object;
var mExp:Object;
 
var subtitles:Array = new Array(16);
subtitles[0] = "00:00:23.0:My Son";
subtitles[1] = "00:00:25.5:The day you were born -";
subtitles[2] = "00:00:26.7:the very forests of Lorderon whispered the name -";
subtitles[3] = "00:00:34.0:Arthas.";
subtitles[4] = "00:01:21.0:My child";
subtitles[5] = "00:01:24.0:I watched with pride as you grew into a weapon - ";
subtitles[6] = "00:01:29.5:Of righteousness.";
subtitles[7] = "00:01:39.0:Remember - our line has always ruled";
subtitles[8] = "00:01:43.0:with wisdom, and strength.";
subtitles[9] = "00:01:48.5:And I know you will show restraint";
subtitles[10] = "00:01:51.6:when exercising your great power.";
subtitles[11] = "00:02:46.3:But the truest victory, my son,";
subtitles[12] = "00:02:49.7:is sturring the hearts of your people.";
subtitles[13] = "00:03:01.4:I tell you this";
subtitles[14] = "00:03:04.0:for when my days have come to an end,";
subtitles[15] = "00:03:08.4:You shall be king.";
 
var cursub:String = "";
var secs:int = 0;
 
stop();
 
function templateLoaded(evt:Event):void
{
 
    trace( "Template Loaded");
	mPlayer = bcp.getModule("videoPlayer");
	mContent = bcp.getModule("content");
    mExp = bcp.getModule("experience");
 
	mExp.addEventListener("templateReady", templateReady);
	mExp.addEventListener("enterFullScreen", enterFullScreen);
	mExp.addEventListener("exitFullScreen", exitFullScreen);
    mContent.addEventListener("videoLoad", videoLoad);
	mPlayer.addEventListener("videoStart", videoStart);
	mPlayer.addEventListener("endBuffering", endBuffering);
	mPlayer.addEventListener("videoProgress", videoProgress);
 
}
 
function enterFullScreen(evt:Event):void
{
	trace( "enterFullScreen");
}
 
function exitFullScreen(evt:Event):void
{
	trace( "exitFullScreen");
}
 
function templateReady(evt:Event):void
{
	trace( "Template Ready");
 
}
 
function videoLoad(evt:Event):void
{
	trace( "videoLoad Ready");
 
}
 
function videoStart(evt:Event):void
{
	trace( "videoStart Ready");
}
 
function videoProgress(evt:Object):void
{
 
	var time : Number = evt.position;
 
	var seconds : int = time % 60;
	var minutes : int = ( time / 60 ) % 60;
	var hours : int = (time / 3600 ) % 3600;
 
	var sText:String = addLeading( "0", String( hours ), 2 ) + ":" + addLeading( "0", String( minutes ), 2 ) + ":" + addLeading( "0", String( seconds ), 2 );
 
	for(var i:int = 0; i < 16; i++)
	{
		var ws:String = subtitles[i];
		//trace(ws);
		if( ws.indexOf(sText) == 0 )
		{
			cursub = ws;
			secs = 0;
			break;
		}
	}
 
	secs++;
	if (secs >= 100 )
	{
		cursub = "";
	}
 
	st.Caption.text = cursub.substr(11,255);
	ft.Caption.text = st.Caption.text;
 
	if( stage.displayState == StageDisplayState.FULL_SCREEN)
	{
		ft.x = 0;
		ft.y = stage.stageHeight - 120;
		ft.Caption.width = stage.stageWidth;
		st.visible = false;
		ft.visible = true;
	}
	else
	{
		st.visible = true;
		ft.visible = false;
	}
}
 
function endBuffering(evt:Event):void
{
	trace( "endBuffering Ready");
}
 
function addLeading( leading : String, str : String, len : int ) : String{
    var limit : int = len - str.length;
    for( var i : int = 0 ; i < limit ; i++ ){
        str = leading + str;
    }
   return str;
}
Leave a Reply

You must be logged in to post a comment. Login »