Menu

Spark VideoPlayer

SourceForge Editorial Staff

Spark VideoPlayer - Functional and Design Specification


Glossary


FLVPlayback VideoPlayer - fl.video.VideoPlayer is the underlying Video class that is used for FLVPlayback. This is the underlying Video class we will use and wrap in the spark video components.

VideoElement - spark.primitives.VideoElement (or sparks.component.VideoElement) - This will be the chromeless video component in Flex. It extends GraphicElement and can be dropped in to a Group and be laid out.

Spark VideoPlayer - spark.components.VideoPlayer - This will be the skinnable video component in Flex. It extends SkinnableComponent and will provide skin parts for many common use-cases.

Embedded Video - Playing a video by that's included in the main SWF by embedding the video. The Spark video components will not support embedded video.

Progressive Video - Playing a video by downloading directly from a server, issuing a typical HTTP request.

Streaming Video - Playing a video from a special media server, like Flash Media Server, which streams the video from the server to the client through a persistent connection to the video server. If the developer specified multiple streams with different bit-rates (multi-bitrate), the server can use bandwidth detection to figure out what's the best stream to play. Through streaming, you can support live video content.

Summary and Background


The Flash player ships with a Video control, but in order to use it in Flex, one has to do a lot of work. The FMS and Flash Authoring team have provided FLVPlayback, which is a video component for Flash Authoriring. However, that component cannot be directly dropped into Flex and laid out properly. In order to do that, we are going to provide wrapper classes in Flex that wrap the VideoPlayer class from the FLVPlayback component. These are the video classes that Flex developers will use.

There are two video classes we are going to ship: VideoElement and Spark VideoPlayer. VideoElement is a chromeless Video component. You can set properties, call methods directly on it, and listen for events; but there is no UI for interacting with the video itself. Spark VideoPlayer is a skinnable wrapper for VideoElement. It provides the same set of methods, properties, and events, but it has a skinnable UI associated with it.

As the Strobe team works on a new Video component, we want to be able to swap out the underlying VideoPlayer class and use the new Strobe VideoPlayer class instead. We'd like to do this all without breaking compatibility with the VideoElement and Spark VideoPlayer classes. In order to do this, we need to make sure what we're exposing from the VideoPlayer class will still be available in the Strobe video class. Also, when we switch over to the Strobe component, we can add the new features.

Usage Scenarios


Jane wants to drop in a new corporate video into her Flex application. She doesn't want any UI on it but just wants to drop it in and have the video play. For this, she uses VideoElement with progressive download.

Joe wants to include a quick animation he's done in his portfolio. He uses the Spark VideoPlayer component with progressive download to show the video and then reskins the component to fit in with the overall look and feel of the website.

John is build a small website to keep an eye on his dog when he's away. He sets up a video camera and a video server. On the client side he decides to use Flex and uses the Spark VideoPlayer component, which supports live streaming.

James is creating a trailer for an Indie documentary he is filming. He encodes the video three times with three different bitRates to support multiple quality levels. He sets up Flash Media Server on the server-side, and on the client side he decides to use Flex and uses the Spark VideoPlayer component, which supports multi-bitrate video.

Detailed Description


The video components in Flex will supportprogressive download, streaming, live streaming, and streaming with multi-bit rate. DVR support will be added later on. In order to support this, the source property on VideoElement and VideoPlayer will be typed as an Object in order to support Strings and a StreamingVideoSource object. The StreamingVideoSource object will handle all of the streaming cases (regular streaming, live streaming, and multi-bitrate streaming). This differs from FLVPlayback, which has play(source, totalTime, live) and play2(dyanmicStreamObject), but it fits in better with the MXML model of Flex.

Example 1: Progressive Download

<s:VideoPlayer source="assets/phone.flv" autoPlay="false"/>

Example 2: Multi-bit rate streaming, chromeless

<s:VideoElement>
    <s:source>
        <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/">
            <s:streamItems>
                <s:StreamItem streamName="MP4:sample1_500kbps.f4v" bitRate="500" />
                <s:StreamItem streamName="MP4:sample1_700kbps.f4v" bitRate="700" />
                <s:StreamItem streamName="MP4:sample1_1000kbps.f4v" bitRate="1000" />
            </s:streamItems>
        </s:StreamingVideoSource>
    </s:source>
</s:VideoElement>

Example 3: Streaming Video

<s:VideoElement>
    <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/">
        <s:StreamItem streamName="myStream" />
    </s:StreamingVideoSource>
</s:VideoElement>

Example 4: Live Streaming Video

<s:VideoElement>
    <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/" live="true">
        <s:StreamItem streamName="myStream" />
    </s:StreamingVideoSource>
</s:VideoElement>

The skinnable Spark VideoPlayer component will have the following skin parts defined:

  • videoElement:VideoElement
  • playerControls:DisplayObject (all of the video player UI controls)
  • playButton:ButtonBase
  • pauseButton:ButtonBase
  • playPauseButton:ToggleButtonBase
  • stopButton:ButtonBase
  • muteButton:ToggleButtonBase
  • volumeBar:VideoPlayerVolumeBar
  • scrubBar:VideoPlayerScrubBar
  • fullScreenButton:ButtonBase;
  • totalTimeLabel:TextGraphicElement
  • playheadTimeLabel:TextGraphicElement

For the playPauseButton, usually someone would either have a playButton and a pauseButton; or they will have a playPauseButton that toggles between playing and pausing. All of the parts are optional, except for videoElement.

The skin states that are supported are a subset of the "states" that the FLVPlayback VideoPlayer reports (fullScreen and normal versions of them). These skin states are:

  • disconnected
  • stopped
  • playing
  • loading
  • connectionError
  • disabled
  • fullScreenDisconnected
  • fullScreenStopped
  • fullScreenPlaying
  • fullScreenLoading
  • fullScreenConnectionError
  • fullScreenDisabled

The states reported from the underlying VideoPlayer are disconnected, stopped, playing, loading, connectionError, buffering (corresponds to skin state of "loading"), paused (maps to "stopped"), rewinding (not present), resizing (not present), seeking (not present), and exexQueuedCmd (not present).

For states, sometimes you want to know whether you are "playing and loading" versus "paused and loading." Because of this, there's an extra read-only property, playing, which is true if the video is currently playing or the video is trying to play but is buffering, loading, or seeking.

API Description


In order to switch to Strobe later on, we are trying to keep APIs on the Spark VideoElement/VideoPlayer to a minimum. However, we do want more advanced users to be able to get at the underlying VideoPlayer object. To allow this, there's an mx_internal videoPlayer variable defined on VideoElement.

In addition to this API description for the new classes below, there's a list of API differences between the Halo VideoPlayer component, FLVPlayback, the underlying VideoPlayer, and the new Spark VideoPlayer component. These differences are enumerated in this excel spreadsheet.

package spark.primitives
{

//--------------------------------------
//  Events
//--------------------------------------

/**
 *  Dispatched when the <code>NetConnection</code> is closed,
 *  whether by being timed out, by calling the <code>close()</code> method, 
 *  or by loading a new video stream.  This event is only dispatched 
 *  with RTMP streams, never HTTP.
 *
 *  @eventType spark.events.VideoEvent.CLOSE
 */
<a href="Event%28name%3D%26quot%3Bclose%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="close", type="spark.events.VideoEvent")</a>

/**
 * Dispatched when playing completes because the player reached the end of the FLV file. 
 * The component does not dispatch the event if you call the <code>stop()</code> or 
 * <code>pause()</code> methods 
 * or click the corresponding controls. 
 *
 *  @eventType spark.events.VideoEvent.COMPLETE
 */
<a href="Event%28name%3D%26quot%3Bcomplete%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="complete", type="spark.events.VideoEvent")</a>

/**
 *  Dispatched the first time the FLV file's metadata is reached.
 *  The event object has an <code>info</code> property that contains the 
 *  info object received by the <code>NetStream.onMetaData</code> event callback.
 * 
 *  @eventType spark.events.VideoEvent.METADATA_RECEIVED
 */
<a href="Event%28name%3D%26quot%3BmetadataReceived%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="metadataReceived", type="spark.events.VideoEvent")</a>

/**
 *  Dispatched every 0.25 seconds while the 
 *  video is playing.  This event is not dispatched when it is paused 
 *  or stopped, unless a seek occurs.
 *
 *  @eventType spark.events.VideoEvent.PLAYHEAD_UPDATE
 */
<a href="Event%28name%3D%26quot%3BplayheadUpdate%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="playheadUpdate", type="spark.events.VideoEvent")</a>

/**
 *  Indicates progress made in number of bytes downloaded. Dispatched starting 
 *  when the load begins and ending when all bytes are loaded or there is a network error. 
 *  Dispatched every 0.25 seconds starting when load is called and ending
 *  when all bytes are loaded or if there is a network error. Use this event to check 
 *  bytes loaded or number of bytes in the buffer. 
 *
 *  <p>Dispatched only for a progressive HTTP download. Indicates progress in number of 
 *  downloaded bytes. The event object has the <code>bytesLoaded</code> and <code>bytesTotal</code>
 *  properties</p>
 * 
 *  @eventType flash.events.ProgressEvent.PROGRESS
 */
<a href="Event%28name%3D%26quot%3Bprogress%26quot%3B%2C%20type%3D%26quot%3Bflash.events.ProgressEvent%26quot%3B%29">Event(name="progress", type="flash.events.ProgressEvent")</a>

/**
 *  Dispatched when the video is loaded and ready to display.
 *
 *  <p>This event is dispatched the first time the VideoPlayer
 *  enters a responsive state after a new FLV is loaded
 *  with the <code>play()</code> or <code>load()</code> method.
 *  It is dispatched once for each FLV loaded.</p>
 *
 *  @eventType spark.events.VideoEvent.READY
 */
<a href="Event%28name%3D%26quot%3Bready%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="ready", type="spark.events.VideoEvent")</a>

//--------------------------------------
//  Other metadata
//--------------------------------------

<a href="DefaultProperty%28%26quot%3Bsource%26quot%3B%29">DefaultProperty("source")</a>

public class VideoElement extends GraphicElement
{

    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    // TODO: where to put this (style, property, ...)?
    /**
     *  @private
     *  The default value that we wait in fullscreen mode with no user-interaction 
     *  before the play controls go away.
     *
     *  @default 3000
     */
    private static const FULL_SCREEN_HIDE_CONTROLS_DELAY:Number = 3000;

    /**
     *  Constructor.
     *   
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function VideoElement();

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    mx_internal var videoPlayer:VideoPlayer;

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  autoPlay
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(category="General", defaultValue="true")</a>

    /**
     *  Specifies whether the video should start playing immediately when the
     *  <code>source</code> property is set.
     *  If <code>true</code>, the video file immediately begins to buffer and
     *  play.
     *
     *  <p>Even if <code>autoPlay</code> is set to <code>false</code>, Flex
     *  starts loading the video after the <code>initialize()</code> method is
     *  called.
     *  For Flash Media Server, this means creating the stream and loading
     *  the first frame to display.
     *  In the case of an HTTP download, Flex starts downloading the stream
     *  and shows the first frame.</p>
     *  
     *  @default true
     */
    public function get autoPlay():Boolean;
    public function set autoPlay(value:Boolean):void;

    //----------------------------------
    //  autoRewind
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(category="General", defaultValue="true")</a>

    /**
     *  Specifies whether the FLV file should be rewound to the first frame
     *  when play stops, either by calling the <code>stop()</code> method or by
     *  reaching the end of the stream.
     *
     *  This property has no effect for live streaming video.
     *
     *  @default true
     */
    public function get autoRewind():Boolean;    
    public function set autoRewind(value:Boolean):void;

    //----------------------------------
    //  maintainAspectRatio
    //----------------------------------

    <a href="Inspectable%28Category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(Category="General", defaultValue="true")</a>

    /**
     *  Specifies whether the control should maintain the original aspect ratio
     *  while resizing the video.
     *
     *  @default true
     */
    public function get maintainAspectRatio():Boolean;
    public function set maintainAspectRatio(value:Boolean):void;

    //----------------------------------
    //  muted
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bfalse%26quot%3B%29">Inspectable(category="General", defaultValue="false")</a>

    /**
     *  Set to <code>true</code> to mute the video, <code>false</code> 
     *  to unmute the video.
     */
    public function get muted():Boolean;
    public function get muted():Boolean;

    //----------------------------------
    //  playheadTime
    //----------------------------------

    <a href="Bindable%28%26quot%3BplayheadUpdate%26quot%3B%29">Bindable("playheadUpdate")</a>    
    <a href="Inspectable%28Category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3B0%26quot%3B%29">Inspectable(Category="General", defaultValue="0")</a>

    /**
     *  Playhead position, measured in seconds, since the video starting
     *  playing. 
     *  The event object for many of the VideoPlay events include the playhead
     *  position so that you can determine the location in the video file where
     *  the event occurred.
     * 
     *  <p>Setting this property to a value in seconds performs a seek
     *  operation. 
     *  If the video is currently playing,
     *  it continues playing from the new playhead position.  
     *  If the video is paused, it seeks to
     *  the new playhead position and remains paused.  
     *  If the video is stopped, it seeks to
     *  the new playhead position and enters the paused state.  
     *  Setting this property has no effect with live video streams.</p>
     *
     *  <p>If the new playhead position is less than 0 or NaN, 
     *  the control throws an exception. If the new playhead position
     *  is past the end of the video, or past the amount of the video file
     *  downloaded so far, then the control still attempts the seek.</p>
     *
     *  <p>For an FLV file, setting the <code>playheadTime</code> property seeks 
     *  to the keyframe closest to the specified position, where 
     *  keyframes are specified in the FLV file at the time of encoding. 
     *  Therefore, you might not seek to the exact time if there 
     *  is no keyframe specified at that position.</p>
     *
     *  <p>This property throws an exception if set when no stream is
     *  connected.  Use the <code>stateChange</code> event and the
     *  <code>connected</code> property to determine when it is
     *  safe to set this property.</p>
     *
     *  @default NaN
     */
    public function get playheadTime():Number;    
    public function set playheadTime(value:Number):void;

    //----------------------------------
    //  playing
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>

    /**
     *  Returns true if the video is playing or is attempting to play.
     *  
     *  <p>The video may not be currently playing, as it may be seeking 
     *  or buferring, but the video is attempting to play.<p> 
     *
     *  @see #play()
     *  @see #pause()
     *  @see #stop()
     *  @see #autoPlay
     */
    public function get playing():Boolean;

    //----------------------------------
    //  source
    //----------------------------------

    <a href="Bindable%28%26quot%3BsourceChanged%26quot%3B%29">Bindable("sourceChanged")</a>
    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bnull%26quot%3B%29">Inspectable(category="General", defaultValue="null")</a>

    /**
     *  For progressive download, the source is just a path or URL pointing 
     *  to the video file to play.  For streaming (streaming, live streaming, 
     *  or multi-bitrate streaming), the source property is a 
     *  StreamingVideoSource object.
     */
    public function get source():Object;
    public function set source(value:Object):void;

    //----------------------------------
    //  totalTime
    //----------------------------------

    <a href="Bindable%28%26quot%3Bcomplete%26quot%3B%29">Bindable("complete")</a>
    <a href="Bindable%28%26quot%3BmetadataReceived%26quot%3B%29">Bindable("metadataReceived")</a>
    <a href="Inspectable%28defaultValue%3D%26quot%3BNaN%26quot%3B%29">Inspectable(defaultValue="NaN")</a>

    /**
     *  Total time for the video feed.  -1 means that property
     *  was not pass into <code>play()</code> or
     *  we were unable to detect the total time automatically,
     *  or have not yet.
     *
     *  @return The total running time of the FLV in seconds
     */
    public function get totalTime():Number;

    //----------------------------------
    //  videoObject
    //----------------------------------

    /**
     *  The underlying flash player flash.media.Video object
     */
    public function get videoObject():Video;

    //----------------------------------
    //  volume
    //----------------------------------

    <a href="Bindable%28%26quot%3BvolumeChanged%26quot%3B%29">Bindable("volumeChanged")</a>
    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3B0.75%26quot%3B%29">Inspectable(category="General", defaultValue="0.75")</a>

    /**
     *  The volume level, specified as an value between 0 and 1.
     * 
     *  @default 0.75
     */
    public function get volume():Number;
    public function set volume(value:Number):void;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Pauses playback without moving the playhead. 
     *  If playback is already is paused or is stopped, this method has no
     *  effect.  
     *
     *  <p>To start playback again, call the <code>play()</code> method.</p>
     */
    public function pause():void;

    /**
     *  Causes the video to play.  Can be called while the video is
     *  paused, stopped, or while the video is already playing.
     *
     *  @param startTime Time to start playing the clip from.  
     *  Pass in NaN to start at the beginning or the 
     *  current spot in the clip if paused.  Default is NaN.
     *  
     *  @param duration Duration, in seconds, to play.  Pass in NaN 
     *  to automatically detect length from metadata, server
     *  or xml.  Default is NaN.
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function play(startTime:Number=NaN, duration:Number=NaN):void;

    /**
     *  Seeks to given second in video.  If video is playing,
     *  continues playing from that point.  If video is paused, seek to
     *  that point and remain paused.  If video is stopped, seek to
     *  that point and enters paused state.  Has no effect with live
     *  streams.
     *
     *  <p>If time is less than 0 or NaN, throws exeption.  If time
     *  is past the end of the stream, or past the amount of file
     *  downloaded so far, then will attempt seek and when fails
     *  will recover.</p>
     *
     *  @param time seconds
     */
    public function seek(time:Number):void;

    /**
     *  Stops video playback.  If <code>autoRewind</code> is set to
     *  <code>true</code>, rewinds to first frame.  If video is already
     *  stopped, has no effect.  To start playback again, call
     *  <code>play()</code>.
     *
     *  @see #autoRewind
     *  @see #play()
     */
    public function stop():void;
}
}

\
\

package spark.components
{

//--------------------------------------
//  Events
//--------------------------------------

/**
 *  Dispatched when the <code>NetConnection</code> is closed,
 *  whether by being timed out, by calling the <code>close()</code> method, 
 *  or by loading a new video stream.  This event is only dispatched 
 *  with RTMP streams, never HTTP.
 *
 *  @eventType spark.events.VideoEvent.CLOSE
 */
<a href="Event%28name%3D%26quot%3Bclose%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="close", type="spark.events.VideoEvent")</a>

/**
 * Dispatched when playing completes because the player reached the end of the FLV file. 
 * The component does not dispatch the event if you call the <code>stop()</code> or 
 * <code>pause()</code> methods 
 * or click the corresponding controls. 
 *
 *  @eventType spark.events.VideoEvent.COMPLETE
 */
<a href="Event%28name%3D%26quot%3Bcomplete%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="complete", type="spark.events.VideoEvent")</a>

/**
 *  Dispatched the first time the FLV file's metadata is reached.
 *  The event object has an <code>info</code> property that contains the 
 *  info object received by the <code>NetStream.onMetaData</code> event callback.
 * 
 *  @eventType spark.events.VideoEvent.METADATA_RECEIVED
 */
<a href="Event%28name%3D%26quot%3BmetadataReceived%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="metadataReceived", type="spark.events.VideoEvent")</a>

/**
 *  Dispatched every 0.25 second while the 
 *  video is playing.  This event is not dispatched when it is paused 
 *  or stopped, unless a seek occurs.
 *
 *  @eventType spark.events.VideoEvent.PLAYHEAD_UPDATE
 */
<a href="Event%28name%3D%26quot%3BplayheadUpdate%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="playheadUpdate", type="spark.events.VideoEvent")</a>

/**
 *  Indicates progress made in number of bytes downloaded. Dispatched starting 
 *  when the load begins and ending when all bytes are loaded or there is a network error. 
 *  Dispatched every 0.25 seconds starting when load is called and ending
 *  when all bytes are loaded or if there is a network error. Use this event to check 
 *  bytes loaded or number of bytes in the buffer. 
 *
 *  <p>Dispatched only for a progressive HTTP download. Indicates progress in number of 
 *  downloaded bytes. The event object has the <code>bytesLoaded</code> and <code>bytesTotal</code>
 *  properties</p>
 * 
 *  @eventType flash.events.ProgressEvent.PROGRESS
 */
<a href="Event%28name%3D%26quot%3Bprogress%26quot%3B%2C%20type%3D%26quot%3Bflash.events.ProgressEvent%26quot%3B%29">Event(name="progress", type="flash.events.ProgressEvent")</a>

/**
 *  Dispatched when the video is loaded and ready to display.
 *
 *  <p>This event is dispatched the first time the VideoPlayer
 *  enters a responsive state after a new FLV is loaded
 *  with the <code>play()</code> or <code>load()</code> method.
 *  It is dispatched once for each FLV loaded.</p>
 *
 *  @eventType spark.events.VideoEvent.READY
 */
<a href="Event%28name%3D%26quot%3Bready%26quot%3B%2C%20type%3D%26quot%3Bspark.events.VideoEvent%26quot%3B%29">Event(name="ready", type="spark.events.VideoEvent")</a>

//--------------------------------------
//  SkinStates
//--------------------------------------

/**
 *  Connection Error State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3BconnectionError%26quot%3B%29">SkinState("connectionError")</a>

/**
 *  Disabled State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3Bdisabled%26quot%3B%29">SkinState("disabled")</a>

/**
 *  Disconnected State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3Bdisconnected%26quot%3B%29">SkinState("disconnected")</a>

/**
 *  Connection Error State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenConnectionError%26quot%3B%29">SkinState("fullScreenConnectionError")</a>

/**
 *  Disabled State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenDisabled%26quot%3B%29">SkinState("fullScreenDisabled")</a>

/**
 *  Disconnected State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenDisconnected%26quot%3B%29">SkinState("fullScreenDisconnected")</a>

/**
 *  Loading State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenLoading%26quot%3B%29">SkinState("fullScreenLoading")</a>

/**
 *  Playing State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenPlaying%26quot%3B%29">SkinState("fullScreenPlaying")</a>

/**
 *  Stopped State of the VideoPlayer when 
 *  in full screen mode.
 */
<a href="SkinState%28%26quot%3BfullScreenStopped%26quot%3B%29">SkinState("fullScreenStopped")</a>

/**
 *  Loading State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3Bloading%26quot%3B%29">SkinState("loading")</a>

/**
 *  Playing State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3Bplaying%26quot%3B%29">SkinState("playing")</a>

/**
 *  Stopped State of the VideoPlayer
 */
<a href="SkinState%28%26quot%3Bstopped%26quot%3B%29">SkinState("stopped")</a>

//--------------------------------------
//  Other metadata
//--------------------------------------

<a href="DefaultProperty%28%26quot%3Bsource%26quot%3B%29">DefaultProperty("source")</a>

<a href="IconFile%28%26quot%3BVideoPlayer.png%26quot%3B%29">IconFile("VideoPlayer.png")</a>

public class VideoPlayer extends FxComponent
{
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     */
    public function VideoPlayer();

    //--------------------------------------------------------------------------
    //
    //  Skin Parts
    //
    //--------------------------------------------------------------------------

    <a href="SkinPart%28required%3D%26quot%3Btrue%26quot%3B%29">SkinPart(required="true")</a>

    /**
     *  A required skin part that defines the VideoElement.
     */
    public var videoElement:VideoElement;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part to display the current playheadTime.
     */
    public var playheadTimeLabel:TextGraphicElement;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for a fullScreen button.
     */
    public var fullScreenButton:ButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the mute button.  When the
     *  video is muted, the selected property will be set to
     *  true.  When the video is not muted,
     *  the selected property will be set tofalse.
     */
    public var muteButton:ToggleButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the pause button
     */
    public var pauseButton:ButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the play button
     */
    public var playButton:ButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for all of the player controls.  We 
     *  need this skin part to know what to hide when in full screen 
     *  mode and there's been no user-interaction.
     */
    public var playerControls:DisplayObject;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for a play/pause button.  When the
     *  video is playing, the selected property will be set to
     *  <code>true</code>.  When the video is paused or stopped,
     *  the selected property will be set tofalse.
     */
    public var playPauseButton:ToggleButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the scrub bar (the
     *  timeline).
     */
    public var scrubBar:VideoPlayerScrubBar;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the stop button
     */
    public var stopButton:ButtonBase;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part to display the totalTime.
     */
    public var totalTimeLabel:TextGraphicElement;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the volume control.
     */
    public var volumeBar:VideoPlayerVolumeBar;

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  autoPlay
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(category="General", defaultValue="true")</a>

    /**
     *  @copy spark.primitives.VideoElement#autoPlay
     */
    public function get autoPlay():Boolean;
    public function set autoPlay(value:Boolean):void;

    //----------------------------------
    //  autoRewind
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(category="General", defaultValue="true")</a>

    /**
     *  @copy spark.primitives.VideoElement#autoRewind
     */
    public function get autoRewind():Boolean;
    public function set autoRewind(value:Boolean):void;

    //----------------------------------
    //  maintainAspectRatio
    //----------------------------------

    <a href="Inspectable%28Category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Btrue%26quot%3B%29">Inspectable(Category="General", defaultValue="true")</a>

    /**
     *  @copy spark.primitives.VideoElement#maintainAspectRatio
     */
    public function get maintainAspectRatio():Boolean;
    public function set maintainAspectRatio(value:Boolean):void;

    //----------------------------------
    //  muted
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bfalse%26quot%3B%29">Inspectable(category="General", defaultValue="false")</a>

    /**
     *  @copy spark.primitives.VideoElement#muted
     */
    public function get muted():Boolean;
    public function set muted(value:Boolean):void;

    //----------------------------------
    //  playheadTime
    //----------------------------------

    <a href="Bindable%28%26quot%3BplayheadUpdate%26quot%3B%29">Bindable("playheadUpdate")</a>
    <a href="Inspectable%28Category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3B0%26quot%3B%29">Inspectable(Category="General", defaultValue="0")</a>

    /**
     *  @copy spark.primitives.VideoElement#playheadTime
     */
    public function get playheadTime():Number;

    //----------------------------------
    //  playing
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>

    /**
     *  @copy spark.primitives.VideoElement#playing
     */
    public function get playing():Boolean;

    //----------------------------------
    //  source
    //----------------------------------

    <a href="Bindable%28%26quot%3BsourceChanged%26quot%3B%29">Bindable("sourceChanged")</a>
    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bnull%26quot%3B%29">Inspectable(category="General", defaultValue="null")</a>

    /**
     *  @copy spark.primitives.VideoElement#source
     */
    public function get source():Object;
    public function set source(value:Object):void;

    //----------------------------------
    //  totalTime
    //----------------------------------

    <a href="Bindable%28%26quot%3Bcomplete%26quot%3B%29">Bindable("complete")</a>
    <a href="Bindable%28%26quot%3BmetadataReceived%26quot%3B%29">Bindable("metadataReceived")</a>
    <a href="Inspectable%28defaultValue%3D%26quot%3BNaN%26quot%3B%29">Inspectable(defaultValue="NaN")</a>

    /**
     *  @copy spark.primitives.VideoElement#totalTime
     */
    public function get totalTime():Number;

    //----------------------------------
    //  volume
    //----------------------------------

    <a href="Bindable%28%26quot%3BvolumeChanged%26quot%3B%29">Bindable("volumeChanged")</a>
    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3B0.75%26quot%3B%29">Inspectable(category="General", defaultValue="0.75")</a>

    /**
     *  @copy spark.primitives.VideoElement#volume
     */
    public function get volume():Number;
    public function set volume(value:Number):void;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @copy spark.primitives.VideoElement#pause()
     *  
     *  @throws TypeError if the skin hasn't been loaded up yet
     *                    and there's no videoElement
     */
    public function pause():void;

    /**
     *  @copy spark.primitives.VideoElement#play()
     *  
     *  @throws TypeError if the skin hasn't been loaded up yet
     *                    and there's no videoElement
     */
    public function play(startTime:Number=NaN, duration:Number=NaN):void;

    /**
     *  @copy spark.primitives.VideoElement#seek()
     *  
     *  @throws TypeError if the skin hasn't been loaded up yet
     *                    and there's no videoElement
     */
    public function seek(time:Number):void;

    /**
     *  @copy spark.primitives.VideoElement#stop()
     *  
     *  @throws TypeError if the skin hasn't been loaded up yet
     *                    and there's no videoElement
     */
    public function stop():void;
}
}

\
\

package spark.components.supportClasses
{

<a href="DefaultProperty%28%26quot%3BstreamItems%26quot%3B%29">DefaultProperty("streamItems")</a>

/**
 *  The StreamingVideoSource class represents a streaming video source and can be 
 *  used for streaming pre-recorded video or live streaming video.  In addition, 
 *  it can support a single stream or multiple streams associated with different 
 *  bitrates.  The <code>VideoPlayer</code> and <code>VideoElement</code>
 *  classes can take a StreamingVideoSource instance as its <code>source</code>
 *  property.
 *
 *  @see spark.controls.VideoPlayer 
 *  @see spark.primitives.VideoElement
 */
public class StreamingVideoSource extends Object
{
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function StreamingVideoSource();

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  live
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bfalse%26quot%3B%29">Inspectable(category="General", defaultValue="false")</a>

    /**
     *  A Boolean value that is <code>true</code> if the video stream is live. 
     * 
     *  <p>Set the <code>live</code> property to <code>false</code> when sending 
     *  a prerecorded video stream to the video player and to <code>true</code> 
     *  when sending real-time data such as a live broadcast.</p>
     * 
     *  @default false
     */
    public function get live():Boolean;
    public function set live(value:Boolean):void;

    //----------------------------------
    //  serverURI
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>

    /**
     *  The uri pointing to the location of the server
     */ 
    public function get serverURI():String;
    public function set serverURI(value:String):void;

    //----------------------------------
    //  streamItems
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>

    /**
     *  The metadata info object with properties describing the FLB file.
     */ 
    public function get streamItems():Vector.<StreamItem>;
    public function set streamItems(value:Vector.<StreamItem>):void;

}
}

\
\

package spark.components.supportClasses
{

/**
 *  The StreamItem class represents a stream on the server plus a 
 *  bitRate for that stream.
 *
 *  @see spark.controls.VideoPlayer 
 *  @see spark.primitives.VideoElement
 */
public class StreamItem extends Object
{
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function StreamItem();

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3B0%26quot%3B%29">Inspectable(category="General", defaultValue="0")</a>

    //----------------------------------
    //  bitRate
    //----------------------------------

    /**
     *  The bitRate for this particular stream.
     * 
     *  @default 0
     */ 
    public function get bitRate():Number;
    public function set bitRate(value:Number):void;

    //----------------------------------
    //  streamName
    //----------------------------------

    <a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%29">Inspectable(category="General")</a>

    /**
     *  The stream name on the server
     */ 
    public function get streamName():String;
    public function set streamName(value:String):void;

}
}

\
\

package spark.components
{

/**
 *  The VideoScrubBar class is a video scrubbar/timeline that can show the
 *  current playHead, the amount previously played, and the buffered video.  
 */  
public class VideoPlayerScrubBar extends HSlider
{

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor. 
     */
    public function VideoPlayerScrubBar();

    //--------------------------------------------------------------------------
    //
    //  Skin Parts
    //
    //--------------------------------------------------------------------------

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the area on the track 
     *  representing the video that's been played.
     */
    public var playedArea:IVisualElement;

    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>

    /**
     *  An optional skin part for the area on the track 
     *  representing the buffered video.
     */
    public var bufferedArea:IVisualElement;

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    //--------------------------------------------------------------------------
    //
    // Properties
    //
    //--------------------------------------------------------------------------

    //--------------------------------- 
    // bufferedValue
    //---------------------------------

    /**
     *  The value of the video that's been buferred in.  This property 
     *  should be greater than value and less than maximum.
     */
    public function get bufferedValue():Number;

    /**
     *  @private
     */
    public function set bufferedValue(value:Number):void;

    //--------------------------------------------------------------------------
    //
    // Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Sets the size of the buffered area
     *
     *  @param bufferedAreaSize The new size of the buffered area
     */
    protected function sizeBufferedArea(bufferedAreaSize:Number):void;

    /**
     *  Sets the size of the played area
     *
     *  @param playedAreaSize The new size of the played area
     */
    protected function sizePlayedArea(playedAreaSize:Number):void;
}
}

\
\

// TODO: should be in spark.components.supportClasses?

package spark.components
{

/**
 *  Dispatched when the dropDown is dismissed for any reason such when 
 *  the user:
 *  <ul>
 *      <li>selects an item in the dropDown</li>
 *      <li>mouses outside outside of the dropDown</li>
 *  </ul>
 *
 *  @eventType spark.events.DropDownEvent.CLOSE
 */
<a href="Event%28name%3D%26quot%3Bclose%26quot%3B%2C%20type%3D%26quot%3Bspark.events.DropDownEvent%26quot%3B%29">Event(name="close", type="spark.events.DropDownEvent")</a>

/**
 *  Dispatched when the mouses over the dropDown button
 *  to display the dropDown.  It is also dispatched if the user
 *  uses the keyboard and types Ctrl-Down to open the dropDown.
 *
 *  @eventType spark.events.DropDownEvent.OPEN
 */
<a href="Event%28name%3D%26quot%3Bopen%26quot%3B%2C%20type%3D%26quot%3Bspark.events.DropDownEvent%26quot%3B%29">Event(name="open", type="spark.events.DropDownEvent")</a>


// TODO: WORK ON EVENT BELOW.. new class??

/**
 *  Dispatched when the user presses the mute button control.
 *
 *  @eventType flash.events.MouseEvent
 */
<a href="Event%28name%3D%26quot%3BmuteButtonClick%26quot%3B%2C%20type%3D%26quot%3Bflash.events.MouseEvent%26quot%3B%29">Event(name="muteButtonClick", type="flash.events.MouseEvent")</a>

/**
 *  Open State of the DropDown component
 */
<a href="SkinState%28%26quot%3Bopen%26quot%3B%29">SkinState("open")</a>

/**
 *  The VideoPlayerVolumeBar is a drop-down slider to control 
 *  the volume of the video player.  By default it pops up when the
 *  muteButton is rolled over (with a delay of 200 milliseconds).  The  
 *  muteButton functions as a mute/unmute button when clicked.
 */
public class VideoPlayerVolumeBar extends VSlider
{
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    // Should this be public?
    /**
     *  @private
     *  The default value for the rollover open delay of this component.
     * 
     *  <p>This is how long to wait while hovered over the button before the 
     *  pop up opens.</p>
     *
     *  @default 200
     */
    private static const ROLL_OVER_OPEN_DELAY:Number = 200;

    //--------------------------------------------------------------------------
    //
    //  Skin Parts
    //
    //--------------------------------------------------------------------------

    /**
     *  A skin part that defines the mute/unmute button.  
     */
    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>
    public var muteButton:VideoPlayerVolumeBarMuteButton;


    /**
     *  A skin part that defines the dropDown area. When the volume slider 
     *  dropdown is open, clicking anywhere outside of the dropDown skin 
     *  part will close the  video slider dropdown. 
     */
    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a>
    public var dropDown:DisplayObject;

    /**
     *  Constructor. 
     */
    public function VideoPlayerVolumeBar();

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  dropDownController
    //----------------------------------

    /**
     *  Instance of the helper class that handles all of the mouse, keyboard 
     *  and focus user interactions.
     */
    protected function get dropDownController():DropDownController;
    protected function set dropDownController(value:DropDownController):void;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Opens the dropDown. 
     */ 
    public function openDropDown():void;

     /**
     *  Closes the dropDown. 
     *   
     *  @param commit Flag indicating if the component should commit the selected
     *  data from the dropDown. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function closeDropDown(commit:Boolean):void;

    //--------------------------------------------------------------------------
    //
    //  Event handling
    //
    //--------------------------------------------------------------------------

    /**
     *  Event handler for the <code>dropDownController</code> 
     *  <code>DropdownEvent.OPEN</code> event. Updates the skin's state and 
     *  ensures that the selectedItem is visible. 
     */
    protected function dropDownController_openHandler(event:DropdownEvent):void;

    /**
     *  Event handler for the <code>dropDownController</code> 
     *  <code>DropdownEvent.CLOSE</code> event. Updates the skin's state.
     */
    protected function dropDownController_closeHandler(event:DropdownEvent):void;

}
}

\
\

package spark.components
{

/**
 *  The VideoPlayerVolumeBarMuteButton is a mute button 
 *  to be used inside the VideoPlayerVolumeBar.  The VideoPlayer
 *  hooks it up so that when the button is clicked it'll 
 *  mute/unmute the volume.  This button has a value property 
 *  so that the visuals of the button can change based on the 
 *  volume.
 */
public class VideoPlayerVolumeBarMuteButton extends Button
{

    /**
     *  Constructor. 
     */
    public function VideoPlayerVolumeBarMuteButton()

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    // TODO: should be called volume instead of value?

    //----------------------------------
    //  value
    //----------------------------------

    <a href="Bindable%28event%3D%26quot%3BvalueCommit%26quot%3B%29">Bindable(event="valueCommit")</a>

    /**
     *  The volume of the video player.
     */
    public function get value():Number;
    public function set value(value:Number):void;

}
}

\
\

package spark.events 
{

/**
 *  The VideoEvent class represents the event object passed to the event listener for 
 *  events dispatched by the VideoPlayer control, and defines the values of 
 *  the <code>VideoPlayer.state</code> property.
 *
 *  @see spark.controls.VideoPlayer
 */
public class VideoEvent extends Event 
{
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    /**
     *  The <code>VideoEvent.CLOSE</code> constant defines the value of the 
     *  <code>type</code> property of the event object for a <code>close</code> event.
     * 
     *  <p>The properties of the event object have the following values:</p>
     *  <table class="innertable">
     *     <tr><th>Property</th><th>Value</th></tr>
     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the 
     *       event listener that handles the event. For example, if you use 
     *       <code>myButton.addEventListener()</code> to register an event listener, 
     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
     *     <tr><td><code>metadataInfo</code></td><td>If the event was triggerred from 
     *       new metadata, an object describing the FLV file.</td></tr>
     *     <tr><td><code>playheadTime</code></td><td>The location of the playhead 
     *       when the event occurs.</td></tr>
     *     <tr><td><code>target</code></td><td>The Object that dispatched the event; 
     *       it is not always the Object listening for the event. 
     *       Use the <code>currentTarget</code> property to always access the 
     *       Object listening for the event.</td></tr>
     *  </table>
     *
     *  @eventType close
     */
    public static const CLOSE:String = "close";

    /**
     *  The <code>VideoEvent.COMPLETE</code> constant defines the value of the 
     *  <code>type</code> property of the event object for a <code>complete</code> event.
     * 
     *  <p>The properties of the event object have the following values:</p>
     *  <table class="innertable">
     *     <tr><th>Property</th><th>Value</th></tr>
     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the 
     *       event listener that handles the event. For example, if you use 
     *       <code>myButton.addEventListener()</code> to register an event listener, 
     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
     *     <tr><td><code>metadataInfo</code></td><td>If the event was triggerred from 
     *       new metadata, an object describing the FLV file.</td></tr>
     *     <tr><td><code>playheadTime</code></td><td>The location of the playhead 
     *       when the event occurs.</td></tr>
     *     <tr><td><code>target</code></td><td>The Object that dispatched the event; 
     *       it is not always the Object listening for the event. 
     *       Use the <code>currentTarget</code> property to always access the 
     *       Object listening for the event.</td></tr>
     *  </table>
     *
     *  @eventType complete
     */    
    public static const COMPLETE:String = "complete";

    /**
     * The VideoEvent.METADATA_RECEIVED constant defines the value of the 
     * <code>type</code> property for a <code>metadataReceived</code> event.
     *
     * <p>This event has the following properties:</p>
     * <table class="innertable" width="100%">
     *     <tr><th>Property</th><th>Value</th></tr>
     *     <tr><td><code>bubbles</code></td><td><code>false</code></td></tr>
     *     <tr><td><code>cancelable</code></td><td><code>false</code>; 
     *        there is no default behavior to cancel.</td></tr>
     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the 
     *       event listener that handles the event. For example, if you use 
     *       <code>myButton.addEventListener()</code> to register an event listener, 
     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
     *     <tr><td><code>metadataInfo</code></td><td>If the event was triggerred from 
     *       new metadata, an object describing the FLV file.</td></tr>
     *     <tr><td><code>playheadTime</code></td><td>The location of the playhead 
     *       when the event occurs.</td></tr>
     *     <tr><td><code>target</code></td><td>The Object that dispatched the event; 
     *       it is not always the Object listening for the event. 
     *       Use the <code>currentTarget</code> property to always access the 
     *       Object listening for the event.</td></tr>  
     * </table>
     * 
     *  @eventType metadataReceived
     */
    public static const METADATA_RECEIVED:String = "metadataReceived";

    /**
     *  The <code>VideoEvent.PLAYHEAD_UPDATE</code> constant defines the value of the 
     *  <code>type</code> property of the event object for a <code>playheadUpdate</code> event.
     * 
     *  <p>The properties of the event object have the following values:</p>
     *  <table class="innertable">
     *     <tr><th>Property</th><th>Value</th></tr>
     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the 
     *       event listener that handles the event. For example, if you use 
     *       <code>myButton.addEventListener()</code> to register an event listener, 
     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
     *     <tr><td><code>metadataInfo</code></td><td>If the event was triggerred from 
     *       new metadata, an object describing the FLV file.</td></tr>
     *     <tr><td><code>playheadTime</code></td><td>The location of the playhead 
     *       when the event occurs.</td></tr>
     *     <tr><td><code>target</code></td><td>The Object that dispatched the event; 
     *       it is not always the Object listening for the event. 
     *       Use the <code>currentTarget</code> property to always access the 
     *       Object listening for the event.</td></tr>
     *  </table>
     *
     *  @eventType playheadUpdate
     */    
    public static const PLAYHEAD_UPDATE:String = "playheadUpdate";

    /**
     *  The <code>VideoEvent.READY</code> constant defines the value of the 
     *  <code>type</code> property of the event object for a <code>ready</code> event.
     * 
     *  <p>The properties of the event object have the following values:</p>
     *  <table class="innertable">
     *     <tr><th>Property</th><th>Value</th></tr>
     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the 
     *       event listener that handles the event. For example, if you use 
     *       <code>myButton.addEventListener()</code> to register an event listener, 
     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
     *     <tr><td><code>metadataInfo</code></td><td>If the event was triggerred from 
     *       new metadata, an object describing the FLV file.</td></tr>
     *     <tr><td><code>playheadTime</code></td><td>The location of the playhead 
     *       when the event occurs.</td></tr>
     *     <tr><td><code>target</code></td><td>The Object that dispatched the event; 
     *       it is not always the Object listening for the event. 
     *       Use the <code>currentTarget</code> property to always access the 
     *       Object listening for the event.</td></tr>
     *  </table>
     *
     *  @eventType ready
     */         
    public static const READY:String = "ready";

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     *
     *  @param type The event type; indicates the action that caused the event.
     *
     *  @param bubbles Specifies whether the event can bubble up the display list hierarchy.
     *
     *  @param cancelable Specifies whether the behavior associated with 
     *  the event can be prevented.
     *
     *  @param playeheadTime The location of the playhead when the event occurs.    
     */
    public function VideoEvent(type:String, bubbles:Boolean = false,
                               cancelable:Boolean = false,
                               playheadTime:Number = NaN,
                               metadataInfo:Object = null);

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  metadataInfo:Object
    //----------------------------------

    /**
     *  The object with properties describing the FLV file.
     */    
    public var metadataInfo:Object;

    //----------------------------------
    //  playheadTime
    //----------------------------------

    /**
     *  The location of the playhead of the VideoPlayer control 
     *  when the event occurs.
     */   
    public var playheadTime:Number;

}

}

\
\

B Features


None

Examples and Usage


Example 1: Progressive Download

<s:VideoPlayer source="assets/phone.flv" autoPlay="false"/>

Example 2: Multi-bit rate streaming, chromeless

<s:VideoElement>
    <s:source>
        <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/">
            <s:streamItems>
                <s:StreamItem streamName="MP4:sample1_500kbps.f4v" bitRate="500" />
                <s:StreamItem streamName="MP4:sample1_700kbps.f4v" bitRate="700" />
                <s:StreamItem streamName="MP4:sample1_1000kbps.f4v" bitRate="1000" />
            </s:streamItems>
        </s:StreamingVideoSource>
    </s:source>
</s:VideoElement>

Example 3: Streaming Video

<s:VideoElement>
    <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/">
        <s:StreamItem streamName="myStream" />
    </s:StreamingVideoSource>
</s:VideoElement>

Example 4: Live Streaming Video

<s:VideoElement>
    <s:StreamingVideoSource serverURI="rtmp://localhost/myStream/" live="true">
        <s:StreamItem streamName="myStream" />
    </s:StreamingVideoSource>
</s:VideoElement>

Example 5: Sample skin for skinnable VideoPlayer:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
    <s:states>
        <s:State name="connectionError" stateGroups="connectionErrorStates, normalStates" />
        <s:State name="disabled" stateGroups="disabledStates, normalStates"/>
        <s:State name="disconnected" stateGroups="disconnectedStates, normalStates"/>
        <s:State name="loading" stateGroups="loadingStates, normalStates"/>
        <s:State name="playing" stateGroups="playingStates, normalStates"/>
        <s:State name="stopped" stateGroups="stoppedStates, normalStates"/>
        <s:State name="fullScreenConnectionError" stateGroups="connectionErrorStates, fullScreenStates"/>
        <s:State name="fullScreenDisabled" stateGroups="disabledStates, fullScreenStates"/>
        <s:State name="fullScreenDisconnected" stateGroups="disconnectedStates, fullScreenStates"/>
        <s:State name="fullScreenLoading" stateGroups="loadingStates, fullScreenStates"/>
        <s:State name="fullScreenPlaying" stateGroups="playingStates, fullScreenStates"/>
        <s:State name="fullScreenStopped" stateGroups="stoppedStates, fullScreenStates"/>
    </s:states>
    <s:layout> 
        <s:VerticalLayout horizontalAlign="center"/> 
    </s:layout>
    <s:VideoElement id="videoElement">
        <s:filters>
            <ShaderFilter includeIn="seeking, buffering" shader="@Embed(source='smudger.pbj')"/>
        </s:filters>
    </s:VideoElement>
    <s:VideoPlayerScrubBar id="scrubBar" />
    <s:Group id="playerControls">
        <s:layout>
            <s:HorizontalLayout />
        </s:layout>
        <s::Button id="stopButton" label="Stop" />
        <s:Button id="playButton" label="Play" />
        <s:Button id="pauseButton" label="Pause" />
        <s:ToggleButton id="muteButton" skinClass="MuteButtonSkin" />
        <s:VideoPlayerVolumeBar id="volumeBar" height="21" />
        <s:Button id="fullScreenButton" label="Fullscreen" />
    </s:Group>
</s:Skin>

Additional Implementation Details


The VideoElement is a GraphicElement whose displayObject is the VideoPlayer. This was a light-weight, easy implementation to get the videoPlayer into the Flex layout system. All the important video player properties/methods/events are proxied on to the video graphic element.

Prototype Work


A prototype of VideoElement and VideoPlayer was created to see how viable this strategy was, especially to figure out how easy it is to create the skin.

Compiler Work


No compiler work

Web Tier Compiler Impact


None

Flex Feature Dependencies


Dependence on the Video team and FLVPlayback + the new Strobe component.

Backwards Compatibility


Syntax changes

-

Behavior

-

Warnings/Deprecation

The old mx.controls.VideoPlayer component can still be used, but the new spark.controls.VideoPlayer should effectively replace it.

Accessibility


No special accessibility work is being done.

Performance


Wrapping VideoPlayer in a VideoElement in a VideoPlayer component could be a little expensive, especially as properties might be duplicated, method calls go through multiple levels of indirection, and event listeners are added on every object for every event. However, there shouldn't be many video elements in a given application, so we feel this shouldn't be much of an issue. Plus, we'll try to optimize the event listener issue as much as possible by not adding a listener on the sub-object unless someone adds a listener to the main object first.

Globalization


None

Localization


None

Framework Features

None

Issues and Recommendations


  • The scrub bar is often shaded to show what's been buffered in. How can we get our Range component to support that? The current plan is to add a new class that subclasses HSlider, VideoPlayerScrubBar. The VideoPlayer scrubBar part is typed as a VideoPlayerScrubBar,.
  • fullScreen support and states explosion: To support fullScreen, we had to double the number of skin states. Multi-dimensional states would be nice instead
  • To support the VideoPlayerVolumeBar which is a popup with Mute button in our case, we needed to be able to push the volume down into the mute button because the mute button/volume icon looks differently depending on what the volume is. Also, the VideoPlayer needs to be able to know when the mute button inside the VideoPlayerVolumeBar is clicked. Because of this, we typed the volumeBar to VideoPlayerVolumeBar and created another new class, VideoPlayerVolumeBarMuteButton. This does limit what other skin implementors can use (it must be a VideoPlayerVolumeBar), but makes the contract really clear.

Documentation


QA


Need to make sure autoPlay, play(), and source all interact well with each other. Also, need to make sure all the properties/methods/events are proxied up correctly, especially around data-binding. For instance, checking to make sure the volume property can be bound correctly when the volume property is set through ActionScript or by a user-action with the volumeBar.


Related

Wiki: Flex 4

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.