Video example: Video Jukebox
The following example builds a simple video jukebox which dynamically loads a list of videos to play back in a sequential order. This allows you to build an application that lets a user browse through a series of video tutorials, or perhaps specifies which advertisements should be played back before delivering the user's requested video. This example demonstrates the following features of ActionScript 3.0:
Updating a playhead based on a video file's playback progress
Listening for and parsing a video file's metadata
Handling specific codes in a net stream
Loading, playing, pausing, and stopping a dynamically loaded FLV
Resizing a video object on the display list based on the net stream's metadata
To get the application files for this sample, see FlashPlatformAS3DevGuideExamples.zip. The Video Jukebox application files can be found in the folder Samples/VideoJukebox. The application consists of the following files:
File | Description |
---|---|
VideoJukebox.fla or VideoJukebox.mxml | The main application file for Flex (MXML) or Flash (FLA). |
VideoJukebox.as | The class that provides the main functionality of the application. |
playlist.xml | A file that lists which video files will be loaded into the video jukebox. |
Loading an external video playlist file
The external playlist.xml file specifies which videos to load, and the order to play them back in. In order to load the XML file, you need to use a URLLoader object and a URLRequest object, as seen in the following code:
uldr = new URLLoader();
uldr.addEventListener(Event.COMPLETE, xmlCompleteHandler);
uldr.load(new URLRequest(PLAYLIST_XML_URL));
This code is placed in the VideoJukebox class's constructor so the file is
loaded before any other code is run. As soon as the XML file has finished
loading, the xmlCompleteHandler()
method is called which parses the external
file into an XML object, as seen in the following code:
private function xmlCompleteHandler(event:Event):void
{
playlist = XML(event.target.data);
videosXML = playlist.video;
main();
}
The playlist
XML object contains the raw XML from the external file, whereas
the videos XML is an XMLList object which contains just the video nodes. A
sample playlist.xml file can be seen in the following snippet:
<videos>
<video url="video/caption_video.flv" />
<video url="video/cuepoints.flv" />
<video url="video/water.flv" />
</videos>
Finally, the xmlCompleteHandler()
method calls the main()
method which sets
up the various component instances on the display list, as well as the
NetConnection and NetStream objects which are used to load the external FLV
files.
Creating the user interface
To build the user interface you need to drag five Button instances onto the
display list and give them the following instance names: playButton
,
pauseButton
, stopButton
, backButton
, and forwardButton
.
For each of these Button instances, you'll need to assign a handler for the
click
event, as seen in the following snippet:
playButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
pauseButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
stopButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
backButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
forwardButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
The buttonClickHandler()
method uses a switch statement to determine which
button instance was clicked, as seen in the following code:
private function buttonClickHandler(event:MouseEvent):void
{
switch (event.currentTarget)
{
case playButton:
ns.resume();
break;
case pauseButton:
ns.togglePause();
break;
case stopButton:
ns.pause();
ns.seek(0);
break;
case backButton:
playPreviousVideo();
break;
case forwardButton:
playNextVideo();
break;
}
}
Next, add a Slider instance to the display list and give it an instance name of
volumeSlider
. The following code sets the slider instance's liveDragging
property to true
and defines an event listener for the slider instance's
change
event:
volumeSlider.value = volumeTransform.volume;
volumeSlider.minimum = 0;
volumeSlider.maximum = 1;
volumeSlider.snapInterval = 0.1;
volumeSlider.tickInterval = volumeSlider.snapInterval;
volumeSlider.liveDragging = true;
volumeSlider.addEventListener(SliderEvent.CHANGE, volumeChangeHandler);
Add a ProgressBar instance to the display list and give it an instance name of
positionBar
. Set its mode
property to manual, as seen in the following
snippet:
positionBar.mode = ProgressBarMode.MANUAL;
Finally add a Label instance to the display list and give it an instance name of
positionLabel
. This Label instance's value will be set by the timer instance
Listening for a video object's metadata
When Flash Player encounters metadata for each of the loaded videos, the
onMetaData()
callback handler is called on the NetStream object's client
property. The following code initializes an Object and sets up the specified
callback handler:
client = new Object();
client.onMetaData = metadataHandler;
The metadataHandler()
method copies its data to the meta property defined
earlier in the code. This allows you to access the metadata for the current
video anytime throughout the entire application. Next, the video object on the
Stage is resized to match the dimensions returned from the metadata. Finally,
the positionBar progress bar instance is moved and resized based on the size of
the currently playing video. The following code contains the entire
metadataHandler()
method:
private function metadataHandler(metadataObj:Object):void
{
meta = metadataObj;
vid.width = meta.width;
vid.height = meta.height;
positionBar.move(vid.x, vid.y + vid.height);
positionBar.width = vid.width;
}
Dynamically loading a video
To dynamically load each of the videos, the application uses a NetConnection and
a NetStream object. The following code creates a NetConnection object and passes
null
to the connect()
method. By specifying null
, Flash Player connects to
a video on the local server instead of connecting to a server, such as a Flash
Media Server.
The following code creates both the NetConnection and NetStream instances,
defines an event listener for the netStatus
event, and assigns the client
Object to the client
property:
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
ns.client = client;
The netStatusHandler()
method is called whenever the status of the video is
changed. This includes when a video starts or stops playback, is buffering or if
a video stream cannot be found. The following code lists the
netStatusHandler()
event:
private function netStatusHandler(event:NetStatusEvent):void
{
try
{
switch (event.info.code)
{
case "NetStream.Play.Start":
t.start();
break;
case "NetStream.Play.StreamNotFound":
case "NetStream.Play.Stop":
t.stop();
playNextVideo();
break;
}
}
catch (error:TypeError)
{
// Ignore any errors.
}
}
The previous code evaluates the code property of the info object and filters whether the code is "NetStream.Play.Start", "NetStream.Play.StreamNotFound", or "NetStream.Play.Stop". All other codes will be ignored. If the net stream is starting the code starts the Timer instance which updates the playhead. If the net stream cannot be found or is stopped, the Timer instance is stopped and the application attempts to play the next video in the playlist.
Every time the Timer executes, the positionBar
progress bar instance updates
its current position by calling the setProgress()
method of the ProgressBar
class and the positionLabel
Label instance is updated with the time elapsed
and total time of the current video.
private function timerHandler(event:TimerEvent):void
{
try
{
positionBar.setProgress(ns.time, meta.duration);
positionLabel.text = ns.time.toFixed(1) + " of " meta.duration.toFixed(1) + " seconds";
}
catch (error:Error)
{
// Ignore this error.
}
}
Controlling the volume of the video
You can control the volume for the dynamically loaded video by setting the
soundTransform
property on the NetStream object. The video jukebox application
allows you to modify the volume level by changing the value of the
volumeSlider
Slider instance. The following code shows how you can change the
volume level by assigning the value of the Slider component to a SoundTransform
object which is set to the soundTransform
property on the NetStream object:
private function volumeChangeHandler(event:SliderEvent):void
{
volumeTransform.volume = event.value;
ns.soundTransform = volumeTransform;
}
Controlling video playback
The rest of the application controls video playback when the video reaches the end of the video stream or the user skips to the previous or next video.
The following method retrieves the video URL from the XMLList for the currently selected index:
private function getVideo():String
{
return videosXML[idx].@url;
}
The playVideo()
method calls the play()
method on the NetStream object to
load the currently selected video:
private function playVideo():void
{
var url:String = getVideo();
ns.play(url);
}
The playPreviousVideo()
method decrements the current video index, calls the
playVideo()
method to load the new video file and sets the progress bar to
visible:
private function playPreviousVideo():void
{
if (idx > 0)
{
idx--;
playVideo();
positionBar.visible = true;
}
}
The final method, playNextVideo()
, increments the video index and calls the
playVideo()
method. If the current video is the last video in the playlist,
the clear()
method is called on the Video object and the progress bar
instance's visible
property is set to false
:
private function playNextVideo():void
{
if (idx < (videosXML.length() - 1))
{
idx++;
playVideo();
positionBar.visible = true;
}
else
{
idx++;
vid.clear();
positionBar.visible = false;
}
}