blob: fc2621a30821856f52e3241e9f3f5d2cce902133 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.controls.videoClasses
{
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Timer;
import mx.core.mx_internal;
import mx.events.MetadataEvent;
import mx.events.VideoEvent;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the <code>NetConnection</code> is closed,
* whether by being timed out or by calling the <code>close()</code> method.
* This event is only dispatched with RTMP streams, never HTTP.
*
* @eventType mx.events.VideoEvent.CLOSE
* @helpid 3482
* @tiptext close event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="close", type="mx.events.VideoEvent")]
/**
* Dispatched when playing completes by reaching the end of the FLV.
* This event not dispatched if the method <code>stop()</code> or
* <code>pause()</code> are called.
*
* <p>When using progressive download and not setting totalTime
* explicitly and downloading an FLV with no metadata duration,
* the totalTime will be set to an approximate total value, now
* that we have played the whole file we can make a guess.
* That value is set by the time this event is dispatched.</p>
*
* @eventType mx.events.VideoEvent.COMPLETE
* @helpid 3482
* @tiptext complete event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="complete", type="mx.events.VideoEvent")]
/**
* Dispatched when a cue point is reached.
*
* @eventType mx.events.MetadataEvent.CUE_POINT
* @helpid 3483
* @tiptext cuePoint event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="cuePoint", type="mx.events.MetadataEvent")]
/**
* Dispatched the first time the FLV metadata is reached.
*
* @eventType mx.events.MetadataEvent.METADATA_RECEIVED
* @tiptext metadata event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="metadataReceived", type="mx.events.MetadataEvent")]
/**
* 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 mx.events.VideoEvent.PLAYHEAD_UPDATE
* @helpid 3480
* @tiptext change event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="playheadUpdate", type="mx.events.VideoEvent")]
/**
* Dispatched every 0.25 seconds while the video is downloading.
*
* <p>Indicates progress made in number of bytes downloaded.
* You can use this event to check the number of bytes loaded
* or the number of bytes in the buffer.
* This event starts when <code>load</code> is called and ends
* when all bytes are loaded or if there is a network error.</p>
*
* @eventType flash.events.ProgressEvent.PROGRESS
* @helpid 3485
* @tiptext progress event
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="progress", type="flash.events.ProgressEvent")]
/**
* 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 mx.events.VideoEvent.READY
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="ready", type="mx.events.VideoEvent")]
/**
* Dispatched when the video autorewinds.
*
* @eventType mx.events.VideoEvent.REWIND
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="rewind", type="mx.events.VideoEvent")]
/**
* Dispatched when the playback state changes.
*
* <p>This event can be used to track when playback enters and leaves
* unresponsive states (for example in the middle of connecting,
* resizing or rewinding) during which times the method
* <code>play()</code>, <code>pause()</code>, <code>stop()</code>
* and <code>seek()</code> will queue the requests to be executed
* when the player enters a responsive state.</p>
*
* @eventType mx.events.VideoEvent.STATE_CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="stateChange", type="mx.events.VideoEvent")]
//--------------------------------------
// Other metadata
//--------------------------------------
[ExcludeClass]
[ResourceBundle("controls")]
/**
* @private
* VideoPlayer is an easy to use wrapper for Video, NetConnection,
* NetStream, etc. that makes playing FLV easy. It supports streaming
* from Flash Communication Server (FCS) and http download of FLVs.
*
* <p>VideoPlayer extends Video.</p>
*
* @tiptext VideoPlayer: FLV player
* @helpid ???
*/
public class VideoPlayer extends Video
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
//----------------------------------
// public state constants
//----------------------------------
/**
* <p>State constant. This is the state when the VideoPlayer is
* constructed and when the stream is closed by a call to
* <code>close()</code> or timed out on idle.</p>
*
* <p>This is a responsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #connected
* @see #idleTimeout
* @see #close()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const DISCONNECTED:String = "disconnected";
/**
* <p>State constant. FLV is loaded and play is stopped. This state
* is entered when <code>stop()</code> is called and when the
* playhead reaches the end of the stream.</p>
*
* <p>This is a responsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #stop()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const STOPPED:String = "stopped";
/**
* <p>State constant. FLV is loaded and is playing.
* This state is entered when <code>play()</code>
* is called.</p>
*
* <p>This is a responsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const PLAYING:String = "playing";
/**
* <p>State constant. FLV is loaded, but play is paused.
* This state is entered when <code>pause()</code> is
* called or when <code>load()</code> is called.</p>
*
* <p>This is a responsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #pause()
* @see #load()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const PAUSED:String = "paused";
/**
* <p>State constant. State entered immediately after
* <code>play()</code> or <code>load()</code> is called.</p>
*
* <p>This is a responsive state.</p>
*
* @see #state
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const BUFFERING:String = "buffering";
/**
* <p>State constant. State entered immediately after
* <code>play()</code> or <code>load()</code> is called.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #load()
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const LOADING:String = "loading";
/**
* <p>State constant. Stream attempted to load was unable to load
* for some reason. Could be no connection to server, stream not
* found, etc.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const CONNECTION_ERROR:String = "connectionError";
/**
* <p>State constant. State entered during a autorewind triggered
* by a stop. After rewind is complete, the state will be
* <code>STOPPED</code>.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #autoRewind
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const REWINDING:String = "rewinding";
/**
* <p>State constant. State entered after <code>seek()</code>
* is called.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #stateResponsive
* @see #seek()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const SEEKING:String = "seeking";
/**
* <p>State constant. State entered during autoresize.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const RESIZING:String = "resizing";
/**
* <p>State constant. State during execution of queued command.
* There will never get a "stateChange" event notification with
* this state; it is internal only.</p>
*
* <p>This is a unresponsive state.</p>
*
* @see #state
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static const EXEC_QUEUED_CMD:String = "execQueuedCmd";
//----------------------------------
// buffer states
//----------------------------------
/**
* @private
*/
private static const BUFFER_EMPTY:String = "bufferEmpty";
/**
* @private
*/
private static const BUFFER_FULL:String = "bufferFull";
/**
* @private
* use this full plus state to work around bug where sometimes
* empty full messages coming in quick succession come in wrong
* order
*/
private static const BUFFER_FLUSH:String = "bufferFlush";
//----------------------------------
// default times for intervals
//----------------------------------
public static const DEFAULT_UPDATE_TIME_INTERVAL:Number = 250; // .25 seconds
public static const DEFAULT_UPDATE_PROGRESS_INTERVAL:Number = 250; // .25 seconds
public static const DEFAULT_IDLE_TIMEOUT_INTERVAL:Number = 300000; // five minutes
public static const AUTO_RESIZE_INTERVAL:Number = 100; // .1 seconds
public static const AUTO_RESIZE_PLAYHEAD_TIMEOUT:Number = .5; // .5 seconds
public static const AUTO_RESIZE_METADATA_DELAY_MAX:Number = 5; // .5 seconds
public static const FINISH_AUTO_RESIZE_INTERVAL:Number = 250; // .25 seconds
public static const RTMP_DO_STOP_AT_END_INTERVAL:Number = 500; // .5 seconds
public static const RTMP_DO_SEEK_INTERVAL:Number = 100; // .1 seconds
public static const HTTP_DO_SEEK_INTERVAL:Number = 250; // .25 seconds
public static const HTTP_DO_SEEK_MAX_COUNT:Number = 4; // 4 times * .25 seconds = 1 second
public static const CLOSE_NS_INTERVAL:Number = .25; // .25 secconds
public static const HTTP_DELAYED_BUFFERING_INTERVAL:Number = 100; // .1 seconds
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* <p>Set this property to the name of your custom class to
* make all VideoPlayer objects created use that class as the
* default INCManager implementation. The default value is
* "mx.controls.videoClasses.NCManager".</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public static var DEFAULT_INCMANAGER:Class = NCManager;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* <p>Constructor.</p>
* @private
* Constructor.
* @helpid 0
* @see INCManager
* @see NCManager
*/
public function VideoPlayer(width:uint, height:uint, ncMgrClassName:Class = null)
{
super(width, height);
// init state variables
_state = DISCONNECTED;
cachedState = _state;
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
cachedPlayheadTime = 0;
_metadata = null;
startingPlay = false;
invalidSeekTime = false;
invalidSeekRecovery = false;
currentPos = 0;
atEnd = false;
cmdQueue = [];
readyDispatched = false;
lastUpdateTime = -1;
sawSeekNotify = false;
// put off creation of INCManager until last minute to
// give time to customize DEFAULT_INCMANAGER
ncMgrClassName = (ncMgrClassName == null) ? DEFAULT_INCMANAGER : ncMgrClassName;
// setup timers
updateTimeTimer = new Timer(DEFAULT_UPDATE_TIME_INTERVAL);
updateTimeTimer.addEventListener(TimerEvent.TIMER, doUpdateTime);
updateProgressTimer = new Timer(DEFAULT_UPDATE_PROGRESS_INTERVAL);
updateProgressTimer.addEventListener(TimerEvent.TIMER, doUpdateProgress);
idleTimeoutTimer = new Timer(DEFAULT_IDLE_TIMEOUT_INTERVAL, 1);
idleTimeoutTimer.addEventListener(TimerEvent.TIMER, doIdleTimeout);
autoResizeTimer = new Timer(AUTO_RESIZE_INTERVAL);
autoResizeTimer.addEventListener(TimerEvent.TIMER, doAutoResize);
rtmpDoStopAtEndTimer = new Timer(RTMP_DO_STOP_AT_END_INTERVAL);
rtmpDoStopAtEndTimer.addEventListener(TimerEvent.TIMER, rtmpDoStopAtEnd);
rtmpDoSeekTimer = new Timer(RTMP_DO_SEEK_INTERVAL);
rtmpDoSeekTimer.addEventListener(TimerEvent.TIMER, doSeek);
httpDoSeekTimer = new Timer(HTTP_DO_SEEK_INTERVAL);
httpDoSeekTimer.addEventListener(TimerEvent.TIMER, doSeek);
finishAutoResizeTimer = new Timer(FINISH_AUTO_RESIZE_INTERVAL, 1);
finishAutoResizeTimer.addEventListener(TimerEvent.TIMER, finishAutoResize);
delayedBufferingTimer = new Timer(HTTP_DELAYED_BUFFERING_INTERVAL);
delayedBufferingTimer.addEventListener(TimerEvent.TIMER, doDelayedBuffering);
// setup intervals
httpDoSeekCount = 0;
// init get/set properties
_isLive = false;
autoPlay = true;
_autoRewind = true;
_bufferTime = 0.1;
_volume = 100;
_soundTransform = new SoundTransform(_volume);
_visible = true;
_url = "";
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* See VideoDisplay's autoBandWidthDetection
*/
public var autoBandWidthDetection:Boolean = false;
/**
* @private
*/
private var cachedState:String;
/**
* @private
*/
private var bufferState:String;
/**
* @private
*/
private var sawPlayStop:Boolean;
/**
* @private
*/
private var cachedPlayheadTime:Number;
/**
* @private
*/
private var startingPlay:Boolean;
/**
* @private
*/
private var invalidSeekRecovery:Boolean;
/**
* @private
*/
private var invalidSeekTime:Boolean;
/**
* @private
*/
private var readyDispatched:Boolean;
/**
* @private
*/
private var lastUpdateTime:Number;
/**
* @private
*/
private var sawSeekNotify:Boolean;
/**
* @private
*/
public var ncMgrClassName:Class;
//----------------------------------
// info about NetStream
//----------------------------------
/**
* @private
*/
private var ns:VideoPlayerNetStream;
/**
* @private
*/
private var currentPos:Number;
/**
* @private
*/
private var atEnd:Boolean;
/**
* @private
*/
private var streamLength:Number;
/**
* @private
* <p>If true, then video plays immediately, if false waits for
* <code>play</code> to be called. Set to true if stream is
* loaded with call to <code>play()</code>, false if loaded
* by call to <code>load()</code>.</p>
*
* <p>Even if <code>autoPlay</code> is set to false, we will start
* loading the video after <code>initialize()</code> is called.
* In the case of FCS, this means creating the stream and loading
* the first frame to display (and loading more if
* <code>autoSize</code> or <code>aspectRatio</code> is true). In
* the case of HTTP download, we will start downloading the stream
* and show the first frame.</p>
*/
private var autoPlay:Boolean;
/**
* @private
* The bytes loaded at the prior sample.
* Used to determine whether to dispatch a progress event.
*/
private var _priorBytesLoaded:int = -1;
/**
* @private
* Internally used for sizing
*/
private var internalVideoWidth:Number = -1;
private var internalVideoHeight:Number = -1;
private var prevVideoWidth:Number = -1;
private var prevVideoHeight:Number = -1;
/**
* @private
* Timers
*/
private var updateTimeTimer:Timer;
private var updateProgressTimer:Timer;
private var idleTimeoutTimer:Timer;
private var autoResizeTimer:Timer;
private var rtmpDoStopAtEndTimer:Timer;
private var rtmpDoSeekTimer:Timer;
private var httpDoSeekTimer:Timer;
private var finishAutoResizeTimer:Timer;
private var delayedBufferingTimer:Timer;
/**
* @private
* Count for httpDoSeekTimer
*/
private var httpDoSeekCount:Number;
/**
* @private
* queues up Objects describing queued commands to be run later
* QueuedCommand defined at the end of this file
*/
private var cmdQueue:Array;
/**
* @private
* Used for accessing localized Error messages.
*/
private var resourceManager:IResourceManager =
ResourceManager.getInstance();
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// scaleX
//----------------------------------
/**
* 100 is standard scale
*
* @see #setScale()
* @tiptext Specifies the horizontal scale factor
* @helpid 3974
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set scaleX(xs:Number):void
{
setScale(xs, this.scaleY);
}
//----------------------------------
// scaleY
//----------------------------------
/**
* 100 is standard scale
*
* @see #setScale()
* @tiptext Specifies the vertical scale factor
* @helpid 3975
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set scaleY(ys:Number):void
{
setScale(this.scaleX, ys);
}
//----------------------------------
// width
//----------------------------------
/**
* <p>Width of video instance. Not same as Video.width, that is videoWidth.</p>
*
* @see #setSize()
* @see #videoWidth
* @helpid 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set width(value:Number):void
{
setSize(value, height);
}
//----------------------------------
// height
//----------------------------------
/**
* <p>Height of video. Not same as Video.height, that is videoHeight.</p>
*
* @see #setSize()
* @see #videoHeight
* @helpid 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set height(value:Number):void
{
setSize(width, value);
}
/**
* <p>Source width of loaded FLV file. Read only. Returns
* undefined if no information available yet.</p>
*
* @see #width
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get videoWidth():int
{
// _videoWidth and _videoHeight come from the NCManager, which would normally mean they
// came from the SMIL and they get top priority if they are non-negative
if (internalVideoWidth > 0) return internalVideoWidth;
// Next priority is the metadata height and width. If the metadata height and width are the same,
// then it might be buggy metadata from an older version of the sorenson encoder, so we ignore it
// and use the super.videoWidth and super.videoHeight instead ONLY if ready has been dispatched.
// this is because we never consider the super.videoWidth and super.videoHeight to be ready
// until ready is dispatched--it could still be 0 or still match the last video loaded
if (metadata != null && !isNaN(metadata.width) && !isNaN(metadata.height))
{
if (metadata.width == metadata.height && readyDispatched)
return super.videoWidth;
else
return int(metadata.width);
}
// last priority is the super.videoWidth and the super.videoHeight, which is
// only used if ready has been dispatched, otherwise return -1
if (readyDispatched) return super.videoWidth;
return -1;
}
/**
* <p>Source height of loaded FLV file. Read only. Returns
* undefined if no information available yet.</p>
*
* @see #height
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get videoHeight():int
{
// _videoWidth and _videoHeight come from the NCManager, which would normally mean they
// came from the SMIL and they get top priority if they are non-negative
if (internalVideoHeight > 0) return internalVideoHeight;
// Next priority is the metadata height and width. If the metadata height and width are the same,
// then it might be buggy metadata from an older version of the sorenson encoder, so we ignore it
// and use the super.videoWidth and super.videoHeight instead ONLY if ready has been dispatched.
// this is because we never consider the super.videoWidth and super.videoHeight to be ready
// until ready is dispatched--it could still be 0 or still match the last video loaded
if (metadata != null && !isNaN(metadata.width) && !isNaN(metadata.height))
{
if (metadata.width == metadata.height && readyDispatched)
return super.videoHeight;
else
return int(metadata.height);
}
// last priority is the super.videoWidth and the super.videoHeight, which is
// only used if ready has been dispatched, otherwise return -1
if (readyDispatched) return super.videoHeight;
return -1;
}
//----------------------------------
// visible
//----------------------------------
/**
* @private
*/
private var _visible:Boolean;
/**
* <p>Use this instead of <code>_visible</code> because we
* sometimes do internal visibility management when doing an
* autoresize.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get visible():Boolean
{
_visible = super.visible;
return _visible;
}
/**
* @private
*/
override public function set visible(v:Boolean):void
{
_visible = v;
super.visible = _visible;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// autoRewind
//----------------------------------
/**
* @private
*/
private var _autoRewind:Boolean;
/**
* <p>Determines whether the FLV is rewound to the first frame
* when play stops, either by calling <code>stop()</code> or by
* reaching the end of the stream. Meaningless for live streams.</p>
*
* @helpid 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get autoRewind():Boolean
{
return _autoRewind;
}
/**
* @private
*/
public function set autoRewind(flag:Boolean):void
{
_autoRewind = flag;
}
//----------------------------------
// playheadTime
//----------------------------------
/**
* <p>The current playhead time in seconds. Setting does a seek
* and has all the restrictions of a seek.</p>
*
* <p>The event "playheadUpdate" is dispatched when the playhead
* time changes, including every .25 seconds while the FLV is
* playing.</p>
*
* @return The playhead position, measured in seconds since the start. Will return a fractional value.
*
* @tiptext Current position of the playhead in seconds
* @helpid 3463
* @see #seek()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get playheadTime():Number
{
var nowTime:Number = (ns == null) ? currentPos : ns.time; // or _ncMgr.isHttp ? ns.time : ns.time + currentPos;
if (_metadata && _metadata.audiodelay)
{
nowTime -= _metadata.audiodelay;
if (nowTime < 0) nowTime = 0;
}
return nowTime;
}
/**
* @private
*/
public function set playheadTime(position:Number):void
{
seek(position);
}
//----------------------------------
// url
//----------------------------------
/**
* @private
*/
private var _url:String;
/**
* <p>url of currently loaded (or loading) stream. Will be url
* last sent to <code>play()</code> or <code>load()</code>, <code>null</code>
* if no stream is loaded.</p>
*
* @tiptext Holds the relative path and filename of the media to be streamed
* @helpid 3457
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get url():String
{
return _url;
}
//----------------------------------
// volume
//----------------------------------
/**
* @private
*/
private var _volume:Number;
/**
* <p>Volume control in range from 0 to 1.</p>
*
* @return The most recent volume setting
*
* @tiptext The volume setting in value range from 0 to 1.
* @helpid 3468
* @see #soundTransform
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get volume():Number
{
return _volume;
}
/**
* @private
*/
public function set volume(aVol:Number):void
{
if ((aVol>= 0) && (aVol <= 1))
_volume = aVol;
else if (aVol < 0)
_volume = 0;
else
_volume = 1;
_soundTransform.volume = _volume;
if (ns != null)
ns.soundTransform = _soundTransform;
}
//----------------------------------
// soundTransform
//----------------------------------
/**
* @private
*/
private var _soundTransform:SoundTransform;
/**
* <p>Provides direct access to the
* <code>flash.media.SoundTransform</code> object to expose
* more sound control. Must set property for changes to take
* effect, get property just to get a copy of the current
* settings to tweak.
*
* @see #volume
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get soundTransform():SoundTransform
{
return _soundTransform;
}
/**
* @private
*/
public function set soundTransform(s:SoundTransform):void
{
_soundTransform = s;
_volume = _soundTransform.volume;
ns.soundTransform = _soundTransform;
}
//----------------------------------
// isRTMP
//----------------------------------
/**
* True if stream is RTMP download (streaming from Flash
* Communication Server), read only.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get isRTMP():Boolean
{
if (_ncMgr == null)
return true;
return _ncMgr.isRTMP();
}
//----------------------------------
// isLive
//----------------------------------
/**
* @private
*/
private var _isLive:Boolean;
/**
* <p>True if stream is live, read only. isLive only makes sense when
* streaming from FVSS or FCS, value is ignored when doing http
* download.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get isLive():Boolean
{
return _isLive;
}
//----------------------------------
// state
//----------------------------------
/**
* @private
*/
private var _state:String;
/**
* Get state. Read only. Set with <code>load</code>,
* <code>play()</code>, <code>stop()</code>,
* <code>pause()</code> and <code>seek()</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get state():String
{
return _state;
}
//----------------------------------
// stateResponsive
//----------------------------------
/**
* Read only. Gets whether state is responsive. If state is
* unresponsive, calls to APIs <code>play()</code>,
* <code>load()</code>, <code>stop()</code>,
* <code>pause()</code> and <code>seek()</code> will queue the
* requests for later, when the state changes to a responsive
* one.
*
* @see #connected
* @see #MAX_RESPONSIVE_STATE
* @see #DISCONNECTED
* @see #STOPPED
* @see #PLAYING
* @see #PAUSED
* @see #LOADING
* @see #RESIZING
* @see #CONNECTION_ERROR
* @see #REWINDING
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get stateResponsive():Boolean
{
switch (_state)
{
case DISCONNECTED:
case STOPPED:
case PLAYING:
case PAUSED:
case BUFFERING:
return true;
default:
return false;
}
}
//----------------------------------
// bytesLoaded
//----------------------------------
/**
* <p>property bytesLoaded, read only. Returns -1 when there
* is no stream, when the stream is FCS or if the information
* is not yet available. Return value only useful for HTTP
* download.</p>
*
* @tiptext Number of bytes already loaded
* @helpid 3455
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get bytesLoaded():int
{
if (ns == null || _ncMgr.isRTMP())
return -1;
return ns.bytesLoaded;
}
//----------------------------------
// bytesTotal
//----------------------------------
/**
* <p>property bytesTotal, read only. Returns -1 when there
* is no stream, when the stream is FCS or if the information
* is not yet available. Return value only useful for HTTP
* download.</p>
*
* @tiptext Number of bytes to be loaded
* @helpid 3456
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get bytesTotal():int
{
if (ns == null || _ncMgr.isRTMP())
return -1;
return ns.bytesTotal;
}
//----------------------------------
// totalTime
//----------------------------------
/**
* <p>property totalTime. read only. -1 means that property
* was not pass into <code>play()</code> or
* <code>load()</code> and was unable to detect automatically,
* or have not yet.
*
* @return The total running time of the FLV in seconds
* @tiptext The total length of the FLV in seconds
* @helpid 3467
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get totalTime():Number
{
return streamLength;
}
//----------------------------------
// bufferTime
//----------------------------------
/**
* @private
*/
private var _bufferTime:Number;
/**
* <p>Sets number of seconds to buffer in memory before playing
* back stream. For slow connections streaming over rtmp, it is
* important to increase this from the default. Default is
* 0.1</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get bufferTime():Number
{
return _bufferTime;
}
/**
* @private
*/
public function set bufferTime(aTime:Number):void
{
_bufferTime = aTime;
if (ns != null)
ns.bufferTime = _bufferTime;
}
//----------------------------------
// idleTimeout
//----------------------------------
/**
* <p>Property idleTimeout, which is amount of time in
* milliseconds before connection is idle (playing is paused
* or stopped) before connection to the FCS server is
* terminated. Has no effect to HTTP download of FLV.</p>
*
* <p>If set when stream already idle, restarts idle timeout with
* new value.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get idleTimeout():uint
{
return idleTimeoutTimer.delay;
}
/**
* @private
*/
public function set idleTimeout(aTime:uint):void
{
idleTimeoutTimer.delay = aTime;
}
//----------------------------------
// playheadUpdateInterval
//----------------------------------
/**
* <p>Property playheadUpdateInterval, which is amount of time
* in milliseconds between each "playheadUpdate" event.</p>
*
* <p>If set when stream is playing, will restart timer.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get playheadUpdateInterval():uint
{
return updateTimeTimer.delay;
}
/**
* @private
*/
public function set playheadUpdateInterval(aTime:uint):void
{
updateTimeTimer.delay = aTime;
}
//----------------------------------
// progressInterval
//----------------------------------
/**
* <p>Property progressInterval, which is amount of time
* in milliseconds between each "progress" event.</p>
*
* <p>If set when stream is playing, will restart timer.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get progressInterval():uint
{
return updateProgressTimer.delay;
}
/**
* @private
*/
public function set progressInterval(aTime:uint):void
{
updateProgressTimer.delay = aTime;
}
//----------------------------------
// ncMgr
//----------------------------------
/**
* @private
*/
private var _ncMgr:INCManager;
/**
* <p>Access to instance of the class implementing
* <code>INCManager</code>. Read only.</p>
*
* <p>One use case for this is that a custom
* <code>INCManager</code> implementation may require custom
* initialization.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get ncMgr():INCManager
{
if (_ncMgr == null)
createINCManager();
return _ncMgr;
}
//----------------------------------
// metadata
//----------------------------------
/**
* @private
*/
private var _metadata:Object;
/**
* <p>Read only. Object received by call to onMetaData callback.
* null if onMetaData callback has not been called since the last
* load or play call. Always null with FLVs with no onMetaData
* packet.</p>
*
* @see #load()
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get metadata():Object
{
return _metadata;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* <p>set width and height simultaneously. Since setting either
* one can trigger an autoresize, this can be better than invoking
* set width and set height individually.</p>
*
* <p>If autoSize is true then this has no effect, since the player
* sets its own dimensions. If maintainAspectRatio is true and
* autoSize is false, then changing width or height will trigger
* an autoresize.</p>
*
* @param width
* @param height
* @see width
* @see height
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function setSize(w:Number, h:Number):void
{
if (w == width && h == height)
return;
super.width = w;
super.height = h;
}
/**
* <p>set scaleX and scaleY simultaneously. Since setting either
* one can trigger an autoresize, this can be better than invoking
* set width and set height individually.</p>
*
* <p>If autoSize is true then this has no effect, since the player
* sets its own dimensions. If maintainAspectRatio is true and
* autoSize is false, then changing scaleX or scaleY will trigger an
* autoresize.</p>
*
* @param scaleX
* @param scaleY
* @see scaleX
* @see scaleY
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function setScale(xs:Number, ys:Number):void
{
if (xs == super.scaleX && ys == super.scaleY)
return;
super.scaleX = xs;
super.scaleY = ys;
}
/**
* <p>Causes the video to play. Can be called while the video is
* paused, stopped, or while the video is already playing. Call this
* method with no arguments to play an already loaded video or pass
* in a url to load a new stream.</p>
*
* <p>If player is in an unresponsive state, queues the request.</p>
*
* <p>Throws an exception if called with no args and no stream
* is connected. Use "stateChange" event and
* <code>connected</code> property to determine when it is
* safe to call this method.</p>
*
* @param url Pass in a url string if you want to load and play a
* new FLV. If you have already loaded an FLV and want to continue
* playing it, pass in <code>null</code>.
* @param isLive Pass in true if streaming a live feed from FCS.
* Defaults to false.
* @param totalTime Pass in length of FLV. Pass in -1
* to automatically detect length from metadata, server
* or xml. If <code>INCManager.streamLength</code> is not -1 when
* <code>ncConnected</code> is called, then
* that value will trump this one in any case. Default is -1.
*
* @see #connected
* @see #stateResponsive
* @see #load()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function play(url:String = null, isLive:Boolean = false, totalTime:Number = -1):void
{
// if new url passed, ask the INCManager to reconnect for us
if (url != null)
{
if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.PLAY, url, isLive, totalTime);
return;
}
else
execQueuedCmds();
autoPlay = true;
_load(url, isLive, totalTime);
// playing will start automatically once stream is setup, so return.
return;
}
if (!isXnOK())
{
if (_state == CONNECTION_ERROR ||
_ncMgr == null ||
_ncMgr.netConnection == null)
throw new VideoError(VideoError.NO_CONNECTION);
else
{
flushQueuedCmds();
queueCmd(VideoPlayerQueuedCommand.PLAY);
setState(LOADING);
cachedState = LOADING;
_ncMgr.reconnect();
// playing will start automatically once stream is setup, so return.
return;
}
}
else if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.PLAY, null, isLive);
return;
}
else
execQueuedCmds();
// recreate stream if necessary (this will never happen with
// http download, just rtmp)
if (ns == null)
{
createStream();
attachNetStream(ns);
//this.attachAudio(ns); revisit - Is this needed?
}
switch (_state)
{
case BUFFERING:
if (_ncMgr.isRTMP())
{
_play(0);
if (atEnd)
{
atEnd = false;
currentPos = 0;
setState(REWINDING);
}
else if (currentPos > 0)
{
_seek(currentPos);
currentPos = 0;
}
}
// no break
case PLAYING:
// already playing
return;
case STOPPED:
if (_ncMgr.isRTMP())
{
if (isLive)
{
_play(-1);
setState(BUFFERING);
}
else
{
_play(0);
if (atEnd)
{
atEnd = false;
currentPos = 0;
_state = BUFFERING;
setState(REWINDING);
}
else if (currentPos > 0)
{
_seek(currentPos);
currentPos = 0;
setState(BUFFERING);
}
else
setState(BUFFERING);
}
}
else
{
_pause(false);
if (atEnd)
{
atEnd = false;
_seek(0);
_state = BUFFERING;
setState(REWINDING);
}
else
{
if (bufferState == BUFFER_EMPTY)
setState(BUFFERING);
else
setState(PLAYING);
}
}
break;
case PAUSED:
_pause(false);
if (!_ncMgr.isRTMP())
if (bufferState == BUFFER_EMPTY)
setState(BUFFERING);
else
setState(PLAYING);
else
setState(BUFFERING);
break;
} // switch
}
/**
* <p>Similar to play, but causes the FLV to be loaded without
* playing. Autoresizing will occur if appropriate and the first
* frame of FLV will be shown (except for maybe not in the live case).
* After initial load and autoresize, state will be <code>PAUSED</code>.</p>
*
* <p>Takes same arguments as <code>play()</code>, but unlike that
* method it is never acceptable to call <code>load()</code> with
* no url. If you do, an <code>Error</code> will be thrown.</p>
*
* <p>If player is in an unresponsive state, queues the request.</p>
*
* @param url Pass in a url string for the FLV you want to load.
* @param isLive Pass in true if streaming a live feed from FCS.
* Defaults to false.
* @param totalTime Pass in length of FLV. Pass in -1 to
* automatically detect length from metadata, server or xml.
* If <code>INCManager.streamLength</code> is not -1 when
* <code>ncConnected</code> is called, then that value will
* trump this one in any case. Default is -1.
* @see #connected
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function load(url:String, isLive:Boolean = false, totalTime:Number = -1):void
{
if (url == null)
{
var message:String = resourceManager.getString(
"controls", "nullURL");
throw new ArgumentError(message);
}
if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.LOAD, url, isLive, totalTime);
return;
}
else
execQueuedCmds();
autoPlay = false;
_load(url, isLive, totalTime);
}
/**
* <p>Pauses video playback. If video is paused or stopped, has
* no effect. To start playback again, call <code>play()</code>.
* Takes no parameters</p>
*
* <p>If player is in an unresponsive state, queues the request.</p>
*
* <p>Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* <code>connected</code> property to determine when it is
* safe to call this method.</p>
*
* <p>If state is already stopped, pause is does nothing and state
* remains stopped.</p>
*
* @see #connected
* @see #stateResponsive
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function pause():void
{
if (!isXnOK())
if (_state == CONNECTION_ERROR ||
_ncMgr == null ||
_ncMgr.netConnection == null)
throw new VideoError(VideoError.NO_CONNECTION);
else
return;
else if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.PAUSE);
return;
}
else
execQueuedCmds();
if (_state == PAUSED || _state == STOPPED || ns == null)
return;
_pause(true);
setState(PAUSED);
}
/**
* <p>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>. Takes no parameters</p>
*
* <p>If player is in an unresponsive state, queues the request.</p>
*
* <p>Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* <code>connected</code> property to determine when it is
* safe to call this method.</p>
*
* @see #connected
* @see #stateResponsive
* @see #autoRewind
* @see #play()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function stop():void
{
if (!isXnOK())
if (_state == CONNECTION_ERROR ||
_ncMgr == null ||
_ncMgr.netConnection == null)
throw new VideoError(VideoError.NO_CONNECTION);
else
return;
else if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.STOP);
return;
}
else
execQueuedCmds();
if (_state == STOPPED || ns == null)
return;
if (_ncMgr.isRTMP())
{
if (_autoRewind && !_isLive)
{
currentPos = 0;
_play(0, 0);
_state = STOPPED;
setState(REWINDING);
}
else
{
closeNS();
setState(STOPPED);
}
}
else
{
_pause(true);
if (_autoRewind)
{
_seek(0);
_state = STOPPED;
setState(REWINDING);
}
else
setState(STOPPED);
}
}
/**
* <p>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>
*
* <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>
*
* <p>If player is in an unresponsive state, queues the request.</p>
*
* <p>Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* <code>connected</code> property to determine when it is
* safe to call this method.</p>
*
* @param time seconds
* @throws VideoError if time is < 0
* @see #connected
* @see #stateResponsive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function seek(time:Number):void
{
// we do not allow more seeks until we are out of an invalid seek time state
if (invalidSeekTime)
return;
if (isNaN(time) || time < 0)
throw new VideoError(VideoError.INVALID_SEEK);
if (!isXnOK())
if (_state == CONNECTION_ERROR ||
_ncMgr == null ||
_ncMgr.netConnection == null)
throw new VideoError(VideoError.NO_CONNECTION);
else
{
flushQueuedCmds();
queueCmd(VideoPlayerQueuedCommand.SEEK, null, false, time);
setState(LOADING);
cachedState = LOADING;
_ncMgr.reconnect();
// playing will start automatically once stream is setup, so return.
return;
}
else if (_state == EXEC_QUEUED_CMD)
_state = cachedState;
else if (!stateResponsive)
{
queueCmd(VideoPlayerQueuedCommand.SEEK, null, false, time);
return;
}
else
execQueuedCmds();
// recreate stream if necessary (this will never happen with
// http download, just rtmp)
if (ns == null)
{
createStream();
attachNetStream(ns);
//this.attachAudio(ns); revisit - Is this needed?
}
if (atEnd && time <= playheadTime)
atEnd = false;
switch (_state)
{
case PLAYING:
_state = BUFFERING;
// no break;
case BUFFERING:
case PAUSED:
_seek(time);
setState(SEEKING);
break;
case STOPPED:
if (_ncMgr.isRTMP())
{
_play(0);
_pause(true);
}
_seek(time);
_state = PAUSED;
setState(SEEKING);
break;
}
}
/**
* <p>Forces close of video stream and FCS connection. Triggers
* "close" event. Typically calling this directly is not necessary
* because the idle timeout functionality will take care of this.</p>
*
* @see idleTimeout
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function close():void
{
closeNS();
// never makes sense to close an http NetConnection, it doesn't really maintain
// any kind of network connection!
if (_ncMgr != null && _ncMgr.isRTMP())
_ncMgr.close();
setState(DISCONNECTED);
var videoEvent:VideoEvent = new VideoEvent(VideoEvent.CLOSE);
videoEvent.state = _state;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
}
//----------------------------------
// public callbacks, not really APIs
//----------------------------------
/**
* @private
* <p>Called by <code>Timer updateTimeTimer</code> to send
* "playheadUpdate" events. Events only sent when playhead is
* moving, sent every .25 seconds (see
* <code>_updateTimeInterval</code>).
*/
public function doUpdateTime(event:Event):void
{
var theTime:Number = playheadTime;
// stop timer if we are stopped or paused
switch (_state)
{
case STOPPED:
case PAUSED:
case DISCONNECTED:
case CONNECTION_ERROR:
if (event != null)
updateTimeTimer.reset();
break;
}
if (lastUpdateTime != theTime)
{
var videoEvent:VideoEvent =
new VideoEvent(VideoEvent.PLAYHEAD_UPDATE);
videoEvent.state = _state;
videoEvent.playheadTime = theTime;
dispatchEvent(videoEvent);
lastUpdateTime = theTime;
}
}
/**
* @private
* <p>Called by <code>Timer _updateProgressTimer</code> to send
* "progress" events. Event dispatch starts when
* <code>_load</code> is called, ends when all bytes downloaded or
* a network error of some kind occurs, dispatched every .25
* seconds.
*/
public function doUpdateProgress(event:Event):void
{
if (ns == null)
return;
if (ns.bytesTotal >= 0 && ns.bytesLoaded != _priorBytesLoaded)
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false,
ns.bytesLoaded, ns.bytesTotal));
if (_state == DISCONNECTED || _state == CONNECTION_ERROR ||
ns.bytesLoaded == ns.bytesTotal)
updateProgressTimer.reset();
_priorBytesLoaded = ns.bytesLoaded;
}
/**
* @private
* <p><code>NetStream.onStatus</code> callback for rtmp. Handles
* automatic resizing, autorewind and buffering messaging.</p>
*/
public function rtmpOnStatus(event:NetStatusEvent):void
{
if (_state == CONNECTION_ERROR)
// always do nothing
return;
switch (event.info.code)
{
case "NetStream.Play.Stop":
if (startingPlay)
return;
switch (_state)
{
case RESIZING:
break;
case LOADING:
case STOPPED:
case PAUSED:
// yes we are stopped, we already know this
break;
default:
sawPlayStop = true;
break;
} // switch (_state)
break;
case "NetStream.Buffer.Empty":
switch (bufferState)
{
case BUFFER_FULL:
if (sawPlayStop)
rtmpDoStopAtEnd(null);
else if (_state == PLAYING)
setState(BUFFERING);
break;
}
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
break;
case "NetStream.Buffer.Flush":
if (sawSeekNotify && _state == SEEKING)
{
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
setStateFromCachedState();
doUpdateTime(null);
}
if (sawPlayStop &&
(bufferState == BUFFER_EMPTY ||
(_bufferTime <= 0.1 && ns.bufferLength <= 0.1)))
{
// if we did a seek toward the end of the file so that
// there is less file left to show than we have
// buffer, than we will get a NetStream.Play.Stop when
// the buffer loads rest of the file, but never get
// a NetStream.Buffer.Full, since it won't fill, so
// we check if we are done on a timer
cachedPlayheadTime = playheadTime;
rtmpDoStopAtEndTimer.start();
}
switch (bufferState)
{
case BUFFER_EMPTY:
if ((_state == LOADING && cachedState == PLAYING)
|| _state == BUFFERING)
setState(PLAYING);
else if (cachedState == BUFFERING)
cachedState = PLAYING;
bufferState = BUFFER_FLUSH;
break;
default:
if (_state == BUFFERING)
setStateFromCachedState();
break;
} // switch (bufferState)
break;
case "NetStream.Buffer.Full":
if (sawSeekNotify && _state == SEEKING)
{
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
setStateFromCachedState();
doUpdateTime(null);
}
switch (bufferState)
{
case BUFFER_EMPTY:
bufferState = BUFFER_FULL;
if ((_state == LOADING && cachedState == PLAYING) || _state == BUFFERING)
setState(PLAYING);
else if (cachedState == BUFFERING)
cachedState = PLAYING;
if (rtmpDoStopAtEndTimer.running)
{
sawPlayStop = true;
rtmpDoStopAtEndTimer.reset();
}
break;
case BUFFER_FLUSH:
bufferState = BUFFER_FULL;
if (rtmpDoStopAtEndTimer.running)
{
sawPlayStop = true;
rtmpDoStopAtEndTimer.reset();
}
break;
} // switch (bufferState)
if (_state == BUFFERING)
setStateFromCachedState();
break;
case "NetStream.Pause.Notify":
// do nothing
break;
case "NetStream.Unpause.Notify":
if (_state == PAUSED)
{
_state = PLAYING;
setState(BUFFERING);
}
else
cachedState = PLAYING;
break;
case "NetStream.Play.Start":
rtmpDoStopAtEndTimer.reset();
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
if (startingPlay)
{
startingPlay = false;
cachedPlayheadTime = playheadTime;
}
else if (_state == PLAYING)
setState(BUFFERING);
break;
case "NetStream.Play.Reset":
rtmpDoStopAtEndTimer.reset();
if (_state == REWINDING)
{
rtmpDoSeekTimer.reset();
if (playheadTime == 0 || playheadTime < cachedPlayheadTime)
setStateFromCachedState();
else
{
cachedPlayheadTime = playheadTime;
rtmpDoSeekTimer.start();
}
}
break;
case "NetStream.Seek.Notify":
if (playheadTime != cachedPlayheadTime)
{
setStateFromCachedState();
doUpdateTime(null);
}
else
{
sawSeekNotify = true;
if (!rtmpDoSeekTimer.running)
rtmpDoSeekTimer.start();
}
break;
case "Netstream.Play.UnpublishNotify":
break;
case "Netstream.Play.PublishNotify":
break;
case "NetStream.Play.StreamNotFound":
if (!_ncMgr.connectAgain())
setState(CONNECTION_ERROR);
break;
case "NetStream.Play.Failed":
case "NetStream.Failed":
setState(CONNECTION_ERROR);
break;
} // switch (event.info.code)
}
/**
* @private
* <p><code>NetStream.onStatus</code> callback for http. Handles
* autorewind.</p>
*/
public function httpOnStatus(event:NetStatusEvent):void
{
switch (event.info.code)
{
case "NetStream.Play.Stop":
delayedBufferingTimer.reset();
if (invalidSeekTime)
{
recoverInvalidSeek();
}
else
switch (_state)
{
case PLAYING:
case BUFFERING:
case SEEKING:
httpDoStopAtEnd();
break;
}
break;
case "NetStream.Seek.InvalidTime":
if (invalidSeekRecovery)
{
invalidSeekTime = false;
invalidSeekRecovery = false;
setState(cachedState);
seek(0);
}
else
{
recoverInvalidSeek();
}
break;
case "NetStream.Buffer.Empty":
bufferState = BUFFER_EMPTY;
if (_state == PLAYING)
delayedBufferingTimer.start();
break;
case "NetStream.Buffer.Full":
case "NetStream.Buffer.Flush":
delayedBufferingTimer.reset();
bufferState = BUFFER_FULL;
if ((_state == LOADING && cachedState == PLAYING) || _state == BUFFERING)
setState(PLAYING);
else if (cachedState == BUFFERING)
cachedState = PLAYING;
break;
case "NetStream.Seek.Notify":
invalidSeekRecovery = false;
switch (_state)
{
case SEEKING:
case REWINDING:
if (!httpDoSeekTimer.running)
{
httpDoSeekCount = 0;
httpDoSeekTimer.start();
}
break;
} // switch (_state)
break;
case "NetStream.Play.StreamNotFound":
setState(CONNECTION_ERROR);
break;
} // switch (event.info.code)
}
/**
* @private
* <p>Called by INCManager after when connection complete or
* failed after call to <code>INCManager.connectToURL</code>.
* If connection failed, set <code>INCManager.netConnection = null</code>
* before calling.</p>
*
* @see #ncReconnected()
* @see INCManager#connectToURL
* @see NCManager#connectToURL
*/
public function ncConnected():void
{
if (_ncMgr == null ||
_ncMgr.netConnection == null)
setState(CONNECTION_ERROR);
else
{
createStream();
setUpStream();
}
}
/**
* @private
* <p>Called by INCManager after when reconnection complete or
* failed after call to <code>INCManager.reconnect</code>. If
* connection failed, set <code>INCManager.netConnection = null</code>
* before calling.</p>
*
* @see #ncConnected()
* @see INCManager#reconnect
* @see NCManager#reconnect
*/
public function ncReconnected():void
{
if (_ncMgr == null ||
_ncMgr.netConnection == null)
setState(CONNECTION_ERROR);
else
{
ns = null;
_state = STOPPED;
execQueuedCmds();
}
}
/**
* handles NetStream.onMetaData callback
*
* @private
*/
public function onMetaData(info:Object):void
{
if (_metadata != null)
return;
_metadata = info;
if (isNaN(streamLength) || streamLength <= 0)
streamLength = info.duration;
if (isNaN(internalVideoWidth) || internalVideoWidth <= 0)
internalVideoWidth = info.width;
if (isNaN(internalVideoHeight) || internalVideoHeight <= 0)
internalVideoHeight = info.height;
var metadataEvent:MetadataEvent =
new MetadataEvent(MetadataEvent.METADATA_RECEIVED);
metadataEvent.info = info;
dispatchEvent(metadataEvent);
}
/**
* handles NetStream.onCuePoint callback
*
* @private
*/
public function onCuePoint(info:Object):void
{
var metadataEvent:MetadataEvent =
new MetadataEvent(MetadataEvent.CUE_POINT);
metadataEvent.info = info;
dispatchEvent(metadataEvent);
}
//----------------------------------
// Private Methods
//----------------------------------
/**
* @private
* does loading work for play and load
*/
private function _load(url:String, isLive:Boolean, totalTime:Number):void
{
if (prevVideoWidth == -1)
(videoWidth >= 0) ? prevVideoWidth = videoWidth : prevVideoWidth = 0;
if (prevVideoHeight == -1)
(videoHeight >= 0) ? prevVideoHeight = videoHeight : prevVideoHeight = 0;
// reset state
cachedPlayheadTime = 0;
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
_metadata = null;
startingPlay = false;
invalidSeekRecovery = false;
invalidSeekTime = false;
_isLive = isLive;
_url = url;
currentPos = 0;
streamLength = totalTime;
atEnd = false;
internalVideoWidth = -1;
internalVideoHeight = -1;
readyDispatched = false;
lastUpdateTime = -1;
sawSeekNotify = false;
// must stop ALL intervals here
updateTimeTimer.reset();
updateProgressTimer.reset();
idleTimeoutTimer.reset();
autoResizeTimer.reset();
rtmpDoStopAtEndTimer.reset();
rtmpDoSeekTimer.reset();
httpDoSeekTimer.reset();
finishAutoResizeTimer.reset();
delayedBufferingTimer.reset();
// close netstream
closeNS(false);
// if returns false, wait for a "connected" message and
// then do these things
if (_ncMgr == null)
createINCManager();
var instantConnect:Boolean = _ncMgr.connectToURL(_url);
setState(LOADING);
cachedState = LOADING;
if (instantConnect)
{
createStream();
setUpStream();
}
if (!_ncMgr.isRTMP())
updateProgressTimer.start();
}
/**
* @private
* sets state, dispatches event, execs queued commands. Always try to call
* this AFTER you do your work, because the state might change again after
* you call this if you set it to a responsive state becasue of the call
* to exec queued commands. If you set this to a responsive state and
* then do more state based logic, check _state to make sure it did not
* change out from under you.
*/
private function setState(s:String):void
{
if (s == _state)
return;
cachedState = _state;
cachedPlayheadTime = playheadTime;
_state = s;
var newState:String = _state;
var videoEvent:VideoEvent = new VideoEvent(VideoEvent.STATE_CHANGE);
videoEvent.state = newState;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
if (!readyDispatched)
{
switch (newState)
{
case STOPPED:
case PLAYING:
case PAUSED:
case BUFFERING:
{
readyDispatched = true;
videoEvent = new VideoEvent(VideoEvent.READY);
videoEvent.state = newState;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
}
}
}
switch (cachedState)
{
case REWINDING:
{
videoEvent = new VideoEvent(VideoEvent.REWIND);
videoEvent.state = newState;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
if (_ncMgr.isRTMP() && newState == STOPPED)
closeNS();
break;
}
}
switch (newState)
{
case STOPPED:
case PAUSED:
{
if (_ncMgr.isRTMP() && !idleTimeoutTimer.running)
idleTimeoutTimer.start();
break;
}
case SEEKING:
case REWINDING:
{
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
// no break
}
case PLAYING:
case BUFFERING:
{
if (!updateTimeTimer.running)
updateTimeTimer.start();
// no break
}
case LOADING:
case RESIZING:
{
idleTimeoutTimer.reset();
break;
}
}
execQueuedCmds();
}
/**
* @private
* Sets state to _cachedState if the _cachedState is PLAYING,
* PAUSED or BUFFERING, otherwise sets state to STOPPED.
*/
private function setStateFromCachedState():void
{
switch (cachedState)
{
case PLAYING:
case PAUSED:
{
setState(cachedState);
break;
}
case BUFFERING:
{
if (bufferState == BUFFER_EMPTY)
setState(BUFFERING);
else
setState(cachedState);
break;
}
default:
{
setState(STOPPED);
break;
}
}
}
/**
* @private
* Helper used when an invalid seek occurs. We reset
* our player state and seek back to a valid playhead
* location.
*/
private function recoverInvalidSeek():void
{
setStateFromCachedState();
invalidSeekTime = false;
invalidSeekRecovery = true;
seek(playheadTime);
}
/**
* @private
* creates our implementatino of the <code>INCManager</code>.
* We put this off until we need to do it to give time for the
* user to customize the <code>DEFAULT_INCMANAGER</code>
* static variable.
*/
private function createINCManager():void
{
var ncMgrClass:Class = (ncMgrClassName == null) ? DEFAULT_INCMANAGER : ncMgrClassName;
_ncMgr = new ncMgrClass();
_ncMgr.videoPlayer = this;
}
/**
* @private
* <p>ONLY CALL THIS WITH RTMP STREAMING</p>
*
* <p>Has the logic for what to do when we decide we have come to
* a stop by coming to the end of an rtmp stream. There are a few
* different ways we decide this has happened, and we sometimes
* even set an interval that calls this function repeatedly to
* check if the time is still changing, which is why it has its
* own special function.</p>
*/
private function rtmpDoStopAtEnd(event:TimerEvent):void
{
// check if we really want to stop if this was triggered on an
// timer. If we are running this on an timer (see
// rtmpOnStatus) we do a stop when the playhead hasn't moved
// since last time we checked, we check every .25 seconds.
if (event != null)
{
switch (_state)
{
case DISCONNECTED:
case CONNECTION_ERROR:
rtmpDoStopAtEndTimer.reset();
return;
}
if (cachedPlayheadTime == playheadTime)
rtmpDoStopAtEndTimer.reset();
else
{
cachedPlayheadTime = playheadTime;
return;
}
}
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
atEnd = true;
// all this triggers callbacks, so need to keep checking if
// _state == STOPPED--if no longer, then we bail
setState(STOPPED);
if (_state != STOPPED)
return;
doUpdateTime(null);
if (_state != STOPPED)
return;
var videoEvent:VideoEvent = new VideoEvent(VideoEvent.COMPLETE);
videoEvent.state = _state;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
if (_state != STOPPED)
return;
if (_autoRewind && !_isLive && playheadTime != 0)
{
atEnd = false;
currentPos = 0;
_play(0, 0);
setState(REWINDING);
}
else
closeNS();
}
/**
* @private
* <p>ONLY CALL THIS WITH RTMP STREAMING</p>
*
* <p>Wait until time goes back to zero to leave rewinding state.</p>
*/
private function rtmpDoSeek():void
{
if (_state != REWINDING && _state != SEEKING)
{
rtmpDoSeekTimer.reset();
sawSeekNotify = false;
}
else if (playheadTime != cachedPlayheadTime)
{
rtmpDoSeekTimer.reset();
sawSeekNotify = false;
setStateFromCachedState();
doUpdateTime(null);
}
}
/**
* @private
* <p>ONLY CALL THIS WITH HTTP PROGRESSIVE DOWNLOAD</p>
*
* <p>Call this when playing stops by hitting the end.</p>
*/
private function httpDoStopAtEnd():void
{
atEnd = true;
if (isNaN(streamLength) || streamLength <= 0)
streamLength = ns.time;
_pause(true);
setState(STOPPED);
if (_state != STOPPED)
return;
doUpdateTime(null);
if (_state != STOPPED)
return;
var videoEvent:VideoEvent = new VideoEvent(VideoEvent.COMPLETE);
videoEvent.state = _state;
videoEvent.playheadTime = playheadTime;
dispatchEvent(videoEvent);
if (_state != STOPPED)
return;
if (_autoRewind)
{
atEnd = false;
_pause(true);
_seek(0);
setState(REWINDING);
}
}
/**
* @private
* <p>If we get an onStatus callback indicating a seek is over,
* but the playheadTime has not updated yet, then we wait on a
* timer before moving forward.</p>
*/
private function doSeek(event:Event):void
{
var seekState:Boolean = (_state == REWINDING || _state == SEEKING);
// if seeking or rewinding, then need to wait for playhead time to
// change or for timeout
if (seekState && httpDoSeekCount < HTTP_DO_SEEK_MAX_COUNT &&
(cachedPlayheadTime == playheadTime || invalidSeekTime))
{
httpDoSeekCount++;
return;
}
// reset
httpDoSeekCount = 0;
httpDoSeekTimer.reset();
// only do the rest if were seeking or rewinding to start with
if (!seekState)
return;
if (invalidSeekTime)
{
recoverInvalidSeek();
}
else
{
setStateFromCachedState();
doUpdateTime(null);
}
}
/**
* @private
* <p>Wrapper for <code>NetStream.close()</code>. Never call
* <code>NetStream.close()</code> directly, always call this
* method because it does some other housekeeping.</p>
*/
private function closeNS(updateCurrentPos:Boolean = true):void
{
if (ns != null)
{
if (updateCurrentPos)
{
updateTimeTimer.reset();
doUpdateTime(null);
currentPos = ns.time;
}
ns.removeEventListener(NetStatusEvent.NET_STATUS, httpOnStatus);
ns.removeEventListener(NetStatusEvent.NET_STATUS, rtmpOnStatus);
ns.close();
ns = null;
}
}
/**
* @private
* <p>We do a brief timer before entering BUFFERING state to avoid
* quick switches from BUFFERING to PLAYING and back.</p>
*/
private function doDelayedBuffering(event:Event):void
{
switch (_state)
{
case LOADING:
case RESIZING:
// if loading or resizing, still at beginning so keep whirring, might go into buffering state
break;
case PLAYING:
// still in that playing state, let's go to buffering
delayedBufferingTimer.reset();
setState(BUFFERING);
break;
default:
// any other state, bail and kill timer
delayedBufferingTimer.reset();
break;
}
}
/**
* @private
* Wrapper for <code>NetStream.pause()</code> and <code>NetStream.resume()</code>.
* Never call these NetStream methods directly; always call this
* method because it does some other housekeeping.
*/
private function _pause(doPause:Boolean):void
{
rtmpDoStopAtEndTimer.reset();
if (doPause)
ns.pause();
else
ns.resume();
}
/**
* @private
* Wrapper for <code>NetStream.play()</code>. Never call
* <code>NetStream.play()</code> directly, always call this
* method because it does some other housekeeping.
*/
private function _play(... rest):void
{
rtmpDoStopAtEndTimer.reset();
startingPlay = true;
switch (rest.length)
{
case 0:
ns.play(_ncMgr.streamName);
break;
case 1:
ns.play(_ncMgr.streamName, rest[0]);
break;
case 2:
ns.play(_ncMgr.streamName, rest[0], rest[1]);
break;
default:
{
var message:String = resourceManager.getString(
"controls", "badArgs");
throw new ArgumentError(message);
}
}
}
/**
* @private
* Wrapper for <code>NetStream.seek()</code>. Never call
* <code>NetStream.seek()</code> directly, always call
* this method because it does some other housekeeping.
*/
private function _seek(time:Number):void
{
rtmpDoStopAtEndTimer.reset();
// round the number to three decimal places...
var seekTime:Number = Math.round(time * 1000) / 1000;
if (_metadata && _metadata.audiodelay && time + _metadata.audiodelay < streamLength) // Revisit
time += _metadata.audiodelay;
ns.seek(time);
invalidSeekTime = false;
bufferState = BUFFER_EMPTY;
sawPlayStop = false;
sawSeekNotify = false;
}
/**
* @private
* Gets whether connected to a stream. If not, then calls to APIs
* <code>play() with no args</code>, <code>stop()</code>,
* <code>pause()</code> and <code>seek()</code> will throw
* exceptions.
*
* @see #stateResponsive
*/
private function isXnOK():Boolean
{
if (_state == LOADING) return true;
if (_state == CONNECTION_ERROR) return false;
if (_state != DISCONNECTED)
{
if (_ncMgr == null ||
_ncMgr.netConnection == null ||
!_ncMgr.netConnection.connected)
{
setState(DISCONNECTED);
return false;
}
return true;
}
return false;
}
/**
* @private
* <p>Does the actual work of resetting the width and height.</p>
*
* <p>Called on a timer which is stopped when width and height
* of the <code>Video</code> object are not zero. Finishing the
* resize is done in another method which is either called on a
* timer set up here for live streams or on a
* NetStream.Play.Stop event in <code>rtmpOnStatus</code> after
* stream is rewound if it is not a live stream. Still need to
* get a http solution.</p>
*/
private function doAutoResize(event:Event):void
{
if (event != null)
{
switch (_state)
{
case RESIZING:
case LOADING:
break;
case DISCONNECTED:
case CONNECTION_ERROR:
// autoresize will happen later automatically
autoResizeTimer.reset();
return;
default:
if (!stateResponsive)
// keep trying until we get into a responsive state
return;
}
if (videoWidth != prevVideoWidth || videoHeight != prevVideoHeight ||
bufferState == BUFFER_FULL || bufferState == BUFFER_FLUSH ||
((ns) ? ns.time > AUTO_RESIZE_PLAYHEAD_TIMEOUT : false))
{ // revisit - Was ns.time > AUTO_RESIZE_PLAYHEAD_TIMEOUT
// if have not received metadata yet, slight delay to avoid race condition in player
// but there may not be any metadata, so cannot wait forever
internalVideoWidth = videoWidth;
internalVideoHeight = videoHeight;
autoResizeTimer.reset();
}
else
// keep trying until our size is set
return;
}
// do not need to do autoresize, but DO need to signal readyness
setState(cachedState);
}
/**
* <p>Makes video visible, turns on sound and starts
* playing if live or autoplay.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private function finishAutoResize(event:Event):void
{
finishAutoResizeTimer.reset();
if (stateResponsive)
return;
super.visible = _visible;
_soundTransform.volume = _volume;
ns.soundTransform = _soundTransform;
if (autoPlay)
if (_ncMgr.isRTMP())
{
if (!_isLive)
{
currentPos = 0;
_play(0);
}
if (_state == RESIZING)
{
setState(LOADING);
cachedState = PLAYING;
}
}
else
{
_pause(false);
cachedState = PLAYING;
}
else
setState(STOPPED);
}
/**
* @private
* <p>Creates <code>NetStream</code> and does some basic
* initialization.</p>
*/
private function createStream():void
{
ns = new VideoPlayerNetStream(_ncMgr.netConnection, this);
ns.bufferTime = _bufferTime;
ns.soundTransform = _soundTransform;
ns.addEventListener(NetStatusEvent.NET_STATUS,
(_ncMgr.isRTMP()) ? rtmpOnStatus : httpOnStatus);
attachNetStream(ns);
}
/**
* @private
* <p>Does initialization after first connecting to the server
* and creating the stream. Will get the stream duration from
* the <code>INCManager</code> if it has it for us.</p>
*
* <p>Starts resize if necessary, otherwise starts playing if
* necessary, otherwise loads first frame of video. In http case,
* starts progressive download in any case.</p>
*/
private function setUpStream():void
{
// INCManager MIGHT have gotten the stream length, width and height for
// us. If its length is null, undefined or < 0, then it did not.
if (!isNaN(_ncMgr.streamLength) && _ncMgr.streamLength >= 0)
streamLength = _ncMgr.streamLength;
if (!isNaN(_ncMgr.streamWidth) && _ncMgr.streamWidth >= 0)
internalVideoWidth = _ncMgr.streamWidth;
else
internalVideoWidth = -1;
if (!isNaN(_ncMgr.streamHeight) && _ncMgr.streamHeight >= 0)
internalVideoHeight = _ncMgr.streamHeight;
else
internalVideoHeight = -1
// just start if static, start resize otherwise
if (autoPlay)
if (!_ncMgr.isRTMP())
{
cachedState = BUFFERING;
_play();
}
else if (_isLive)
{
cachedState = BUFFERING;
_play(-1);
}
else
{
cachedState = BUFFERING;
_play(0);
}
else
{
cachedState = STOPPED;
if (_ncMgr.isRTMP())
_play(-2, 0);
else
{
_play();
_pause(true);
_seek(0);
}
}
autoResizeTimer.reset();
autoResizeTimer.start();
}
/**
* @private
* <p>ONLY CALL THIS WITH RTMP STREAMING</p>
*
* <p>Only used for rtmp connections. When we pause or stop,
* setup a timer to call this after a delay (see property
* <code>idleTimeout</code>). We do this to spare the server from
* having a bunch of extra xns hanging around, although this needs
* to be balanced with the load that creating connections puts on
* the server, and keep in mind that FCS can be configured to
* terminate idle connections on its own, which is a better way to
* manage the issue.</p>
*/
private function doIdleTimeout(event:Event):void
{
idleTimeoutTimer.reset();
close();
}
/**
* @private
* Dumps all queued commands without executing them
*/
private function flushQueuedCmds():void
{
while (cmdQueue.length > 0)
cmdQueue.pop();
}
/**
* @private
* Executes as many queued commands as possible, obviously
* stopping when state becomes unresponsive.
*/
private function execQueuedCmds():void
{
while (cmdQueue.length > 0 && (stateResponsive || _state == CONNECTION_ERROR) &&
((cmdQueue[0].url != null) ||
(_state != DISCONNECTED && _state != CONNECTION_ERROR)))
{
var nextCmd:VideoPlayerQueuedCommand = cmdQueue.shift();
cachedState = _state;
_state = EXEC_QUEUED_CMD;
switch (nextCmd.type)
{
case VideoPlayerQueuedCommand.PLAY:
play(nextCmd.url, nextCmd.isLive, nextCmd.time);
break;
case VideoPlayerQueuedCommand.LOAD:
load(nextCmd.url, nextCmd.isLive, nextCmd.time);
break;
case VideoPlayerQueuedCommand.PAUSE:
pause();
break;
case VideoPlayerQueuedCommand.STOP:
stop();
break;
case VideoPlayerQueuedCommand.SEEK:
seek(nextCmd.time);
break;
} // switch
}
}
private function queueCmd(type:uint, url:String = null, isLive:Boolean = false, time:Number = 0):void
{
cmdQueue.push(new VideoPlayerQueuedCommand(type, url, isLive, time));
}
} // class mx.controls.videoClasses.VideoPlayer
} // package mx.controls.videoClasses
////////////////////////////////////////////////////////////////////////////////
//
// Helper class: VideoPlayerNetStream
//
////////////////////////////////////////////////////////////////////////////////
import flash.net.NetConnection;
import flash.net.NetStream;
import mx.controls.videoClasses.VideoPlayer;
/**
* @private
* This subclass of NetStream handles onMetaData() and onCuePoint()
* calls from the server and forwards them to the VideoPlayer.
*/
dynamic class VideoPlayerNetStream extends NetStream
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function VideoPlayerNetStream(connection:NetConnection,
videoPlayer:VideoPlayer)
{
super(connection);
this.videoPlayer = videoPlayer;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var videoPlayer:VideoPlayer;
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Called by the server.
*/
public function onMetaData(info:Object, ... rest):void
{
videoPlayer.onMetaData(info);
}
/**
* @private
* Called by the server.
*/
public function onCuePoint(info:Object, ... rest):void
{
videoPlayer.onCuePoint(info);
}
/**
* @private
*/
public function onPlayStatus(... rest):void
{
}
}