27 March 2006
You should be familiar with Flash Professional 8 and understand the basics of delivering video through Flash and Flash Media Server (FMS). You should also have a basic understanding of XML.
Intermediate
Note: This article is legacy content. For the latest information, check out the latest version of the article.
As developers, we are often bogged down with mundane tasks such as site maintenance and updates. If we want to pass these tasks on to clients or colleagues who are not tech-savvy, we have to develop sophisticated interfaces to simplify the process for them and to give them easy access to the data. XML gives the power to control content back to the client, freeing us developers to do what we do best.
High-profile sites, most notably Google Video and Amazon.com, are taking advantage of the integration of Flash video with XML. By editing one flat file, you too can update your video content dynamically.
At a MAX conference, Chris Hock and Srinivas Manapragada from Macromedia (now Adobe) presented an XML-based solution for creating content-driven media players called VideoSource. Using their code as a foundation, you will develop your own dynamic video playlist in this article. This XML-based approach is perfect if you ever need to display multiple video clips but you don't want to edit your Flash source code every time you add new video content. As an extra bonus, the application actually generates thumbnail previews on the fly.
Note: This tutorial shows you how to create a dynamic playlist for streaming Flash video. To find out how to build a similar playlist using progressive download Flash video, see my article, Creating a Dynamic Playlist for Progressive Flash Video.
The basic framework of the VideoSource application consists of the following (see Figure 1):
You can download all source files for this project.
This section explains the structure of the finished player and walks you through creating the interface. You also create a very simple custom component that displays your dynamic thumbnails within the playlist.
The video player is a relatively simple interface, consisting of an embedded video object for playback and a List component for the video (see Figure 2).
(+) View larger
The List component is populated automatically from the contents of your XML file. You can extend the List object class to include an automatically generated thumbnail of the video for each list item. Each item links to the appropriate Flash video (FLV) file, which plays in the FLVPlayback component when clicked.
Follow these steps to create your interface:
#include "VideoSource.as"
stop(); action to the first frame.Make sure that both the "Parameters Are Locked in Instances" and the "Display in Components Panel" options are checked.
You'll need to wait until after you write the VideoSource.as file (see Step 3 in the section "Writing the Client-Side ActionScript") to publish the SWF file, so keep the interface file handy.
That's all there is to the interface. Provided that you don't decide to change your layout, you won't need to edit the file again. You just make all future updates in your XML file. It's truly a thing of beauty.
The next section shows you how to set up the application on your server.
This article assumes that you are running Flash Media Server locally, but the application setup would be the same if you were using a remote server:
That's all the setup required on the server side.
In a bit, you'll construct the VideoSource.as ActionScript code. However, let's first take a look at the XML file you will use to dynamically build your video playlist.
In this section, I explain the structure of the sample XML data file, playlist-demo-1.xml, which is included in the sample file ZIP for this tutorial.
To add new videos to your playlist dynamically, simply edit this file:
<xml>
<listitem name="Wind Sculptures" url="rtmp://localhost/videosource/">
<stream name="wind" start="0" len="-1"/>
</listitem>
<listitem name="The Trike" url="rtmp://localhost/videosource/">
<stream name="trike_final" start="0" len="-1"/>
</listitem>
<listitem name="Fluffy Hammer" url="rtmp://localhost/videosource/">
<stream name="fluff_hammer" start="0" len="-1"/>
</listitem>
<listitem name="Fluffy Crash" url="rtmp://localhost/videosource/">
<stream name="fluffy_crash" start="0" len="-1"/>
</listitem>
<listitem name="SuperSkate" url="rtmp://localhost/videosource/">
<stream name="discodance" start="0" len="-1"/>
</listitem>
<listitem name="The Fish" url="rtmp://localhost/videosource/">
<stream name="fish" start="0" len="-1"/>
</listitem>
<menu>
<listitem name="Wind Sculptures"/>
<listitem name="The Trike"/>
<listitem name="Fluffy Hammer"/>
<listitem name="Fluffy Crash"/>
<listitem name="SuperSkate"/>
<listitem name="The Fish"/>
</menu>
</xml>
Notice that the file has a very basic XML structure, holding two main things:
The url attribute must point to your server running Flash Media Server. (For the purpose of this article, I assume you are doing so locally.) To point to your own FLV files, change the stream name to your FLV filename (omitting the .flv extension). These filenames should correspond to those you placed in the "streams" folder on the FMS server in the previous section. Just remember to leave off the .flv extension in the XML file, as shown in the example.
OK, ready for the fun part? The next section gets into the meat of the application—the client-side ActionScript.
This section explains how to parse the XML document and create the dynamic playlist.
As I mentioned, all the ActionScript you are using for this project is contained in external files. Begin by creating an ActionScript file called VideoSource.as in Dreamweaver or in your favorite text editor.
Assign values to the rowHeight and Selectable list properties, and then call the function VideoThumb to create the actual thumbnails:
list.rowHeight = 70;
list.cellRenderer = "VideoThumb";
list.selectable = true;
I address this function later in detail in the next section, "Generating Dynamic Thumbnails."
To detect when clips are selected from the playlist, you need a listener object. Create a new empty listener object called listListener:
//create new empty listener object
listListener = {};
Now create a function to control what happens when an item in the playlist is selected:
listListener.change = function( evtobj ) {
var nav = list.dataProvider[list.selectedIndex];
var reset = true;
for ( var i = 0; i < nav.childNodes.length; i++ ) {
var stream = nav.childNodes[i];
if ( stream.nodeName == "stream" ) {
attachMovie("FLVPlayback", "my_FLVPlybk", 10, {width:320,
height:240, x:90, y:100});
//center the FLVPlayback component when FLV is ready to play
var listenerObject:Object = new Object();
listenerObject.resize = function(eventObject:Object):Void {
//center video in playback area
newx = (460 - my_FLVPlybk.preferredWidth)/2;
newy = (470 - my_FLVPlybk.preferredHeight)/2;
my_FLVPlybk._x = newx;
my_FLVPlybk._y = newy;
};
my_FLVPlybk.addEventListener("resize", listenerObject);
listenerObject.ready = function(eventObject:Object):Void {
my_FLVPlybk.setSize(250, 350);
};
my_FLVPlybk.skin = "SteelExternalAll.swf";
my_FLVPlybk.clear();
my_FLVPlybk.contentPath = nav.attributes.url + "/_definst_/" + stream.attributes.name+".flv";
my_FLVPlybk.autoSize = true;
//trace(my_FLVPlybk.contentPath);
reset = false;
}
}
}
This function accomplishes the following:
nav variable, which holds the XML data for the selected list item, providing access to all attributes of the selected item.reset variable and initially sets its value to true. This variable will be used in the ns.play method three lines below. This causes the existing NetStream instance to be flushed, stopping any video currently being played and immediately playing the newly selected video. The reset variable is then set to false for the rest of the files in the playlist.If nodeName is stream, the clip is played. To implement the listListener function, you must associate it with the playlist. Then when it triggers, the listListener function is called, repopulating the list:
//Add an event listener on the list, when it triggers,
//run the listListener function to repopulate the list
list.addEventListener("change", listListener);
Now it's time to dive into the lengthiest function. This one accomplishes a lot:
I'll break it up and explain what happens in each operation.
Create a new XML object, called xmllist. Add the standard ignoreWhite=true command to strip out extra white space from the XML file. Finally, load the XML file into the XML object:
var xmllist = new XML(); //setup a variable to hold the XML
xmllist.ignoreWhite = true;
xmllist.load( "playlist-demo-1.xml" ); //load the XML file
Congratulations, you now have your XML data in Flash. But that's just the beginning.
You need to parse that data into a format that Flash can use to populate the playlist. The following function is called when the XML has been successfully loaded:
xmllist.onLoad = function( status ) {
if ( !status )
trace( status );
var entries = this.childNodes[0];
var playlists = {};
var nav = [];
The code passes status to this function and traces it for debugging purposes. Create three variables to hold the XML data for each video clip:
entriesplaylistsnavNext, create a for loop that steps through the XML and builds the data arrays:
for ( var i = 0; i < entries.childNodes.length; i++ ) {
var entry = entries.childNodes[i];
if ( entry.nodeName == "listitem" )
//builds array of video clip names
playlists[entry.attributes.name] = entry;
else if ( entry.nodeName == "menu" ) {
//builds array of available videos
for ( var j = 0; j < entry.childNodes.length; j++ )
nav[j] = playlists[entry.childNodes[j].attributes.name];
}//end else if
}//end for
As the loop steps through the XML, it builds an array of video clip names (playlists) and an array of available videos (nav). The array of videos is then sent to your List component for display purposes:
//sends the array of videos to the listbox UI
list.dataProvider = nav;
}//end xml onload
Save this file as VideoSource.as in the same folder as VideoSource2.fla. Also place a copy in your application's web-accessible directory.
You've now successfully imported your XML data, built the playlist, and implemented the clip selection function. The next operation tackles creating dynamic thumbnails.
The playlist so far is pretty snappy, but let's not stop there. Jazz it up even more with thumbnails generated on the fly.
The purpose of this external ActionScript file (videothumb.as) is to create thumbnails dynamically of each video in your list.
Because this article focuses mostly on using XML to deliver video dynamically, I won't go into too much detail about creating and extending the List component class. In this section, I include all the code you need to extend the normal List component to display the thumbnails and provide a brief description of how it works. If you'd like to find out more about creating and extending classes, read Exploring Version 2 of the Flash MX 2004 Component Architecture by Waleed Anbar.
This application uses the List component for displaying the playlist. In this bit of ActionScript, a class is created that extends mx.core.Uicomponent to include a thumbnail. Here is the code in its entirety:
class VideoThumb extends mx.core.UIComponent
{
static var symbolName = "VideoThumb";
var label : Object; // the new text label we'll use
var listOwner : Object; // reference to the tree - supplied by the tree
var thumb;
var nc; // NetConnection
var ns; // NetStream
var streamurl;
function VideoThumb() //define constructor
{
// nothing needed - we're extending v2;
}
function init() //initialize
{
// nothing needed - we don't have any instance variables to initialize
}
function createChildren(Void) : Void
{
var v = this.attachMovie( "VideoHolder", "thumb", 0 );
v._width = 80;
v._height = 60;
v.styleName = listOwner;
var c = createLabel("label", 1);
c.styleName = listOwner;
c.selectable = false;
}
// pass all sizing from the tree to the cell
function size(Void) : Void
{
label.setSize(label.getPreferredWidth(),label.getPreferredHeight());
label._x = thumb._width + 10;
label._y = thumb._height/2 - label._height/2;
}
function setValue(str : String, item, sel)
{
_visible = item != undefined;
if ( !_visible )
return;
label.setValue( item.attributes.name );
// Thumbnail is picked up as the first frame of the playlist
var url = item.attributes.url;
var stream = item.attributes.thumb;
var start = item.attributes.thumbpos;
// If explicit thumb is not specified in XML
// use the first frame of the video
if ( stream == undefined ) {
stream = item.childNodes[0].attributes.name;
start = item.childNodes[0].attributes.start;
}
// Give up if we still don't have valid thumb info
if ( stream == undefined )
return;
// Render the thumbnail only if necessary
if ( streamurl == url + "/" + stream )
return;
streamurl = url + "/" + stream;
//get first frame of video
nc = new NetConnection();
nc.connect( url );
ns = new NetStream(nc);
ns.onStatus = function(info) {
// if video has stopped playing, reset nc and ns
if ( info.code == "NetStream.Play.Stop" ) {
nc = null;
ns = null;
}
}
thumb.video.attachVideo(ns);
ns.connect();
//grab the first frame
//begin at the specified start point, and play one frame
ns.play( stream, start, 0 );
}
.
function getPreferredHeight()
{
return 60;
}
function getPreferredWidth()
{
return label.getPreferredWidth();
}
}
The basic purpose of VideoThumb.as is to specify the appearance and contents of the ListBox and create a thumbnail preview of the video file. The script performs these specific operations:
VideoHolder object and sets it to a default size of 80 x 60 pixels. This serves as the thumbnail object for the list item.setValue function begins the rendering of the list item. The script looks for a thumbnail specified in the XML. If there is none, as is true in our example, it creates one by grabbing the first frame of the associated movie.Save a copy of VideoThumb.as in the same folder as VideoSource.as and VideoSource2.fla. With all your ActionScript now in place, you can go back to VideoSource2.fla and publish the SWF file. Place a copy of your SWF file into your web-accessible directory.
The last step places your files on the server so you can watch VideoSource in action.
In the beginning of this tutorial, you copied your FLV files to your Flash Media Server installation. You will now need to place your ActionScript and interface files in your web-accessible folder for testing.
These three files should be in your web-accessible folder on your server or localhost:
Because you already copied your Flash video files to FMS earlier in the article, you are ready to test your application. Using your favorite browser, navigate to VideoSource2.swf to test your playlist, now magically complete with thumbnails. Figure 4 shows the live application in action, loading Flash video files and generating thumbnails.
You've explored simple video clip playlist navigation using XML, but you could extend this basic framework to other applications as well:
You have created a powerful XML playlist application that you can easily update, reuse, and reskin to your heart's content. Some very useful features that you could add include bandwidth detection, cue points, and video preloaders. Take this basic framework and be creative!
The following resources can help you get up to speed with Flash video and Flash Media Server: