A BEML Player with a Movie Rating Widget

Written by Bob de Wit. Posted in Brightcove, Developer Blog, Flex

This example Brightcove Player is a BEML template in which I’ve inserted a Flex widget to add behavior to support parental control. It addresses several things:
  • How to take control over the video player to insert the control behavior
  • How to alternate between the video player and widget display
  • How to use video asset tags to set up the video rating
  • How to handle “burned in” rating messages
Working demo here

An Introduction to Rating Systems

A motion picture rating system categorizes films with regard to suitability for audiences in terms of issues such as sex, violence, substance abuse, profanity, impudence or other types of mature content. A particular issued rating is called a certification. This helps parents decide whether a movie is suitable for their children. Also, in some jurisdictions a rating may impose on movie theaters the legal obligation of refusing the entrance of children or minors to the movie. Furthermore, where movie theaters do not have this legal obligation, they may enforce restrictions on their own. Ratings are often given in lieu of censorship. There are often debates as to the usefulness, strictness and enforcement of such systems. People may like content with a high rating. This includes children who may like to see content considered unsuitable for them (forbidden fruit phenomenon). “Unrated”, “uncut”, “uncensored”, and “director’s cut” versions of movies and TV shows released on DVD have become increasingly common. In countries such as Australia, an official government body decides on ratings; in other countries, such as the United States, it is done by industry committees with no official government status. In most countries, however, films that are considered morally offensive have been censored, restricted, or banned. Even if the film rating system has no legal consequences, and a film has not explicitly been restricted or banned, there are usually laws forbidding certain films, or forbidding minors to view them.

Regions and Rating Systems

As the introduction indicates, different regions and countries will have different rating systems. This poses an interesting challenge for global online video distribution, as publishers want to keep their assets under tight control and avoid having to manage duplicate assets where possible. This example code shows a possible approach to this challenge by defining the rating system as a coded array of ratings and behavior. I’ve taken the Motion Picture Association of America film rating system as an example, but the principle can easily be extended to other rating systems (and languages) when coupled with a GeoIP detection system.

Setting the Scene

For this example, I’ve created a fairly simple GUI in Flex consisting of the MPAA’s rating image, a message, and a confirmation checkbox and button:
 
And created a simple BEML VideoPlayer with two layers, one for the VideoPlayer, one for the Widget:
width="100%" height="100%" x="0" y="0"
depth="2" visible="true"/>
width="100%" height="100%" x="0" y="0"
source="http://active6.com/blog/code/beml_rating/BEMLRatingWidget.swf"
depth="1" visible="true"/>
I’ve then created a simple class to hold the rating configurations:
public class RatingData
{
public var Rating:String;
public var Image:String;
public var Message:String;
public var ConfirmText:String;
public var Age:int;
public var MustConfirm:Boolean;

public function RatingData(rating:String, image:String, message:String, confirmtext:String, age:int, mustconfirm:Boolean)
{
Rating = rating;
Image = image;
Message = message;
ConfirmText=confirmtext;
Age = age;
MustConfirm = mustconfirm;
}
}
The first thing we’ll do when the Flex app initializes is populate a Ratings array:
Ratings[0] = new RatingData( "G", "general-audiences.gif", "General Audiences", "", 0, false );
Ratings[1] = new RatingData( "PG", "parental-guid.gif", "Parental Guidance Suggested", "Yes, I have my parent’s permission to view this video", 0, true );
Ratings[2] = new RatingData( "PG13", "parental-strongly.gif", "Parents Strongly Cautioned", "Yes, I am 13 years or older, or have my parent’s permission to view this video", 13, true);
Ratings[3] = new RatingData( "R", "restricted.gif", "Restricted", "Yes, I am 17 years or older, or one of my parents is present", 17, true);
Ratings[4] = new RatingData( "NC17", "nc-17.gif", "No One 17 And Under Admitted", "Yes, I am 18 years or older", 18, true);
The widget is then manifested based on a text value within the tags property of an individual video. For example, if you include [RATING:PG] as a tag, the following code will surface the appropriate behavior, in this case pausing the video and asking for an age confirmation. This behavior is triggered when the video stream starts for the first time within the player:
private function streamStart(evt:Object):void
{
//Check if the video’s tags contain a rating text
var video:Object = VideoPlayer.getCurrentVideo();
var tags:String = video.tags;
var rating:String = GetTagValue(tags, "[RATING:", "]");

if(rating != "")
{
for(var i:int = 0; i < 5; i++)
{
if( Ratings[i].Rating == rating )
{
RatingImage.source = ImageRoot + Ratings[i].Image;
RatingMessage.text = Ratings[i].Message;
if( Ratings[i].MustConfirm )
{
ConfirmAgeCheckBox.selected = false;
ConfirmAgeCheckBox.label = Ratings[i].ConfirmText;
ConfirmAgeCheckBox.visible = true;
ContinueButton.enabled = false;
ContinueButton.visible = true;
VideoPlayer.pause(true);
ShowWidget(true);
}
else
{
ContinueVideo();
}
}
}
}
}
A second possible tag value that the widget will search for is the [RATING_OFFSET:x] tag. This is used to skip “burned in” rating messages that may not correspond to the rating system governing the region the viewer is located in. In the example player, the video actually contains a UK rating, which I am skipping in favor of my “tagged” rating manifestation by simply specifying an offset in the video. The repositioning code is quite simple:
private function ContinueVideo():void
{
//Check if the video’s tags contain a rating offset
//If so, reposition the video to the offset
var video:Object = VideoPlayer.getCurrentVideo();
var tags:String = video.tags;
var soffset:String = GetTagValue(tags, "[RATING_OFFSET:", "]");
if( soffset != "" )
{
var offset:int = int(soffset);
VideoPlayer.seek(offset);
}
VideoPlayer.pause(false);
ShowWidget(false);
}
The full code is listed below:
<!--?xml version="1.0" encoding="utf-8"?-->
<![CDATA[

//Image URL Root
private var ImageRoot:String = "http://active6.com/blog/code/beml_rating/images/";

//Create the array of ratings settings we will use
private var Ratings:Array = new Array(5)

//Placeholder for BC Player Reference
private var BCPlayer: Object = null;

//Placeholder for VideoPlayer Reference
private var VideoPlayer: Object = null;

private function InitApp():void
{
//Initialize the Ratings Array
Ratings[0] = new RatingData( "G", "general-audiences.gif", "General Audiences", "", 0, false );
Ratings[1] = new RatingData( "PG", "parental-guid.gif", "Parental Guidance Suggested", "Yes, I have my parent’s permission to view this video", 0, true );
Ratings[2] = new RatingData( "PG13", "parental-strongly.gif", "Parents Strongly Cautioned", "Yes, I am 13 years or older, or have my parent’s permission to view this video", 13, true);
Ratings[3] = new RatingData( "R", "restricted.gif", "Restricted", "Yes, I am 17 years or older, or one of my parents is present", 17, true);
Ratings[4] = new RatingData( "NC17", "nc-17.gif", "No One 17 And Under Admitted", "Yes, I am 18 years or older", 18, true);
}

public function  setInterface(player:Object):void
{
//Save the passed BC player Object reference
BCPlayer = player;

//Retrieve the VideoPlayer Module references
VideoPlayer = BCPlayer.getModule("videoPlayer");

//Add a listener for a video being loaded
VideoPlayer.addEventListener ("streamStart", streamStart);

}

private function ShowWidget(showIt:Boolean):void
{
//If widget is to be shown, go to player view mode
if(showIt)
{
VideoPlayer.goFullScreen(false);
}
this.visible = showIt;
VideoPlayer.setVisible(!showIt);
}

//Handler for videoLoad events from the content module
private function streamStart(evt:Object):void
{
//Check if the video’s tags contain a rating text
var video:Object = VideoPlayer.getCurrentVideo();
var tags:String = video.tags;
var rating:String = GetTagValue(tags, "[RATING:", "]");

if(rating != "")
{
for(var i:int = 0; i < 5; i++)
{
if( Ratings[i].Rating == rating )
{
RatingImage.source = ImageRoot + Ratings[i].Image;
RatingMessage.text = Ratings[i].Message;
if( Ratings[i].MustConfirm )
{
ConfirmAgeCheckBox.selected = false;
ConfirmAgeCheckBox.label = Ratings[i].ConfirmText;
ConfirmAgeCheckBox.visible = true;
ContinueButton.enabled = false;
ContinueButton.visible = true;
VideoPlayer.pause(true);
ShowWidget(true);
}
else
{
ContinueVideo();
}
}
}

}
}

private function GetTagValue( text:String, pre:String, post:String):String
{
var result:String = "";
if( text.indexOf(pre) >= 0 )
{
result = text.substring(text.indexOf(pre) + pre.length );
result = result.substring(0, result.indexOf(post));
}
return result;
}

private function VerifyConfirm():void
{
ContinueButton.enabled = ConfirmAgeCheckBox.selected;
}

private function ContinueVideo():void
{
//Check if the video’s tags contain a rating offset
//If so, reposition the video to the offset
var video:Object = VideoPlayer.getCurrentVideo();
var tags:String = video.tags;
var soffset:String = GetTagValue(tags, "[RATING_OFFSET:", "]");
if( soffset != "" )
{
var offset:int = int(soffset);
VideoPlayer.seek(offset);
}
VideoPlayer.pause(false);
ShowWidget(false);
}

]]>

Trackback from your site.

Comments (1)

  • Cooker Hoods ·

    |

    movie rating is very important and i always look for a good movie rating before i watch it `

    Reply

Leave a comment