//**************************************************************************** //Copyright (C) 2004-2005 Macromedia, Inc. All Rights Reserved. //The following is Sample Code and is subject to all restrictions on //such code as contained in the End User License Agreement accompanying //this product. //**************************************************************************** import mx.events.EventDispatcher; import mx.video.*; /** *
Event dispatched when NetConnection
is closed,
* whether by being timed out or by calling close()
API.
* Only dispatched with RTMP streams, never HTTP. Event Object has
* properties state and playheadTime.
Event dispatched when playing completes by reaching the end of
* the FLV. Is not dispatched if the APIs stop()
or
* pause()
are called. Event Object has properties state and
* playheadTime.
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.
* */ [Event("complete")] /** *Event dispatched when a cue point is reached. Event Object has an
* info property that contains the info object received by the
* NetStream.onCuePoint
callback for FLV cue points or
* the object passed into the AS cue point APIs for AS cue points.
Event dispatched the first time the FLV metadata is reached.
* Event Object has an info property that contains the info object
* received by the NetStream.onMetaData
callback.
While FLV is playing, this event is dispatched every .25 * seconds. Not dispatched when we are paused or stopped, unless a * seek occurs. Event Object has properties state and playheadTime.
* */ [Event("playheadUpdate")] /** *Indicates progress made in number of bytes downloaded. User can
* use this to check bytes loaded or number of bytes in the buffer.
* Fires every .25 seconds, starting when load is called and ending
* when all bytes are loaded or if there is a network error. Event Object is
* of type mx.events.ProgressEvent
.
Event dispatched when FLV is loaded and ready to display. Event * object has properties state and playheadTime.
* *Fired the first time we enter a responsive state after we * load a new flv with play() or load() API. Only fires once * for each FLV loaded.
* */ [Event("ready")] /** *Event dispatched when video is autoresized due to * maintainAspectRatio or autoSize properties set to true. Event * Object has properties x, y, width and height.
* */ [Event("resize")] /** *Event dispatched when video autorewinds. Event Object has properties * state and playheadTime.
* */ [Event("rewind")] /** *Event dispatched when playback state changes. Event Object has * properties state and playheadTime.
* *This event can be used to track when playback enters/leaves
* unresponsive states (for example in the middle of connecting,
* resizing or rewinding) during which times APIs play()
,
* pause()
, stop()
and seek()
* will queue the requests to be executed when the player enters
* a responsive state.
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.
* *VideoPlayer extends MovieClip and wraps a Video object. It also * "extends" EventDispatcher using mixins.
* * @author copyright 2004-2005 Macromedia, Inc. */ class mx.video.VideoPlayer extends MovieClip { #include "ComponentVersion.as" // public state constants /** *State constant. This is the state when the VideoPlayer is
* constructed and when the stream is closed by a call to
* close()
or timed out on idle.
This is a responsive state.
* * @see #state * @see #stateResponsive * @see #connected * @see #idleTimeout * @see #close() */ public static var DISCONNECTED:String = "disconnected"; /** *State constant. FLV is loaded and play is stopped. This state
* is entered when stop()
is called and when the
* playhead reaches the end of the stream.
This is a responsive state.
* * @see #state * @see #stateResponsive * @see #stop() */ public static var STOPPED:String = "stopped"; /** *State constant. FLV is loaded and is playing.
* This state is entered when play()
* is called.
This is a responsive state.
* * @see #state * @see #stateResponsive * @see #play() */ public static var PLAYING:String = "playing"; /** *State constant. FLV is loaded, but play is paused.
* This state is entered when pause()
is
* called or when load()
is called.
This is a responsive state.
* * @see #state * @see #stateResponsive * @see #pause() * @see #load() */ public static var PAUSED:String = "paused"; /** *State constant. State entered immediately after
* play()
or load()
is called.
This is a responsive state.
* * @see #state * @see #stateResponsive */ public static var BUFFERING:String = "buffering"; /** *State constant. State entered immediately after
* play()
or load()
is called.
This is a unresponsive state.
* * @see #state * @see #stateResponsive * @see #load() * @see #play() */ public static var LOADING:String = "loading"; /** *State constant. Stream attempted to load was unable to load * for some reason. Could be no connection to server, stream not * found, etc.
* *This is a unresponsive state.
* * @see #state * @see #stateResponsive */ public static var CONNECTION_ERROR:String = "connectionError"; /** *State constant. State entered during a autorewind triggered
* by a stop. After rewind is complete, the state will be
* STOPPED
.
This is a unresponsive state.
* * @see #autoRewind * @see #state * @see #stateResponsive */ public static var REWINDING:String = "rewinding"; /** *State constant. State entered after seek()
* is called.
This is a unresponsive state.
* * @see #state * @see #stateResponsive * @see #seek() */ public static var SEEKING:String = "seeking"; /** *State constant. State entered during autoresize.
* *This is a unresponsive state.
* * @see #autoSize * @see #maintainAspectRatio * @see #state * @see #stateResponsive */ public static var RESIZING:String = "resizing"; /** *State constant. State during execution of queued command. * There will never get a "stateChange" event notification with * this state; it is internal only.
* *This is a unresponsive state.
* * @see #state * @see #stateResponsive */ static var EXEC_QUEUED_CMD:String = "execQueuedCmd"; // buffer states private static var BUFFER_EMPTY:String = "bufferEmpty"; private static var BUFFER_FULL:String = "bufferFull"; private static var BUFFER_FULL_SAW_PLAY_STOP:String = "bufferFullSawPlayStop"; // state private var _state:String; private var _cachedState:String; private var _bufferState:String; private var _cachedPlayheadTime:Number; private var _metadata:Object; private var _startingPlay:Boolean; private var _invalidSeekTime:Boolean; private var _invalidSeekRecovery:Boolean; private var _readyDispatched:Boolean; private var _autoResizeDone:Boolean; private var _lastUpdateTime:Number; private var _sawSeekNotify:Boolean; // Video object private var _video:Video; // INCManager private var _ncMgr:INCManager; public var ncMgrClassName:String; /** *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.video.NCManager".
*/ public static var DEFAULT_INCMANAGER:String = "mx.video.NCManager"; // info about NetStream private var _ns:NetStream; private var _currentPos:Number; private var _atEnd:Boolean; private var _streamLength:Number; // store properties private var _autoSize:Boolean; private var _aspectRatio:Boolean; /** *If true, then video plays immediately, if false waits for
* play
to be called. Set to true if stream is
* loaded with call to play()
, false if loaded
* by call to load()
.
Even if _autoPlay
is set to false, we will start
* loading the video after initialize()
is called.
* In the case of FCS, this means creating the stream and loading
* the first frame to display (and loading more if
* autoSize
or aspectRatio
is true). In
* the case of HTTP download, we will start downloading the stream
* and show the first frame.
Constructor.
* * @see INCManager * @see NCManager */ public function VideoPlayer() { // add EventDispatcher mixins EventDispatcher.initialize(this); // init state variables _state = DISCONNECTED; _cachedState = _state; _bufferState = BUFFER_EMPTY; _cachedPlayheadTime = 0; _metadata = null; _startingPlay = false; _invalidSeekTime = false; _invalidSeekRecovery = false; _currentPos = 0; _atEnd = false; _cmdQueue = new Array(); _readyDispatched = false; _autoResizeDone = false; _lastUpdateTime = -1; _sawSeekNotify = false; // put off creation of INCManager until last minute to // give time to customize DEFAULT_INCMANAGER // setup intervals _updateTimeIntervalID = 0; _updateTimeInterval = DEFAULT_UPDATE_TIME_INTERVAL; _updateProgressIntervalID = 0; _updateProgressInterval = DEFAULT_UPDATE_PROGRESS_INTERVAL; _idleTimeoutIntervalID = 0; _idleTimeoutInterval = DEFAULT_IDLE_TIMEOUT_INTERVAL; _autoResizeIntervalID = 0; _rtmpDoStopAtEndIntervalID = 0; _rtmpDoSeekIntervalID = 0; _httpDoSeekIntervalID = 0; _httpDoSeekCount = 0; _finishAutoResizeIntervalID = 0; _delayedBufferingIntervalID = 0; _delayedBufferingInterval = HTTP_DELAYED_BUFFERING_INTERVAL; // init get/set properties if (_isLive == undefined) _isLive = false; if (_autoSize == undefined) _autoSize = false; if (_aspectRatio == undefined) _aspectRatio = true; if (_autoPlay == undefined) _autoPlay = true; if (_autoRewind == undefined) _autoRewind = true; if (_bufferTime == undefined) _bufferTime = 0.1; if (_volume == undefined) _volume = 100; _sound = new Sound(this); _sound.setVolume(_volume); __visible = true; _hiddenForResize = false; _hiddenForResizeMetadataDelay = 0; _contentPath = ""; //ifdef DEBUG //_debugSingleton = this; //endif } /** *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.
* *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.
* * @param width * @param height * @see width * @see height */ public function setSize(w:Number, h:Number):Void { if ( (w == _video._width && h == _video._height) || _autoSize ) return; _video._width = w; _video._height = h; if (_aspectRatio) { startAutoResize(); } } /** *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.
* *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.
* * @param scaleX * @param scaleY * @see scaleX * @see scaleY */ public function setScale(xs:Number, ys:Number) { if ( (xs == _video._xscale && ys == _video._yscale) || _autoSize ) return; _video._xscale = xs; _video._yscale = ys; if (_aspectRatio) { startAutoResize(); } } /** *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.
* *If player is in an unresponsive state, queues the request.
* *Throws an exception if called with no args and no stream
* is connected. Use "stateChange" event and
* connected
property to determine when it is
* safe to call this method.
null
.
* @param isLive Pass in true if streaming a live feed from FCS.
* Defaults to false.
* @param totalTime Pass in length of FLV. Pass in 0 or null or
* undefined to automatically detect length from metadata, server
* or xml. If INCManager.streamLength
is not 0 or
* null or undefined when ncConnected
is called, then
* that value will trump this one in any case. Default is
* undefined.
* @see #connected
* @see #stateResponsive
* @see #load()
*/
public function play(url:String, isLive:Boolean, totalTime:Number):Void {
//ifdef DEBUG
//debugTrace("play(" + url + ")");
//endif
// if new url passed, ask the INCManager to reconnect for us
if (url != null && url != undefined) {
if (_state == EXEC_QUEUED_CMD) {
_state = _cachedState;
} else if (!stateResponsive) {
queueCmd(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 == undefined ||
_ncMgr.getNetConnection() == null ||
_ncMgr.getNetConnection() == undefined ) {
throw new VideoError(VideoError.NO_CONNECTION);
} else {
//ifdef DEBUG
//debugTrace("RECONNECTING!!!");
//endif
flushQueuedCmds();
queueCmd(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(PLAY);
return;
} else {
execQueuedCmds();
}
// recreate stream if necessary (this will never happen with
// http download, just rtmp)
if (_ns == null || _ns == undefined) {
_createStream();
_video.attachVideo(_ns);
this.attachAudio(_ns);
}
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
}
/**
* 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 PAUSED
.
Takes same arguments as play()
, but unlike that
* method it is never acceptable to call load()
with
* no url. If you do, an Error
will be thrown.
If player is in an unresponsive state, queues the request.
* * @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 0 or null or * undefined to automatically detect length from metadata, server * or xml. IfINCManager.streamLength
is not 0 or
* null or undefined when ncConnected
is called, then
* that value will trump this one in any case. Default is
* undefined.
* @see #connected
* @see #play()
*/
public function load(url:String, isLive:Boolean, totalTime:Number):Void {
if (url == null || url == undefined) {
throw new Error("null url sent to VideoPlayer.load");
}
//ifdef DEBUG
//debugTrace("load(" + url + ")");
//endif
if (_state == EXEC_QUEUED_CMD) {
_state = _cachedState;
} else if (!stateResponsive) {
queueCmd(LOAD, url, isLive, totalTime);
return;
} else {
execQueuedCmds();
}
_autoPlay = false;
_load(url, isLive, totalTime);
}
/*
* does loading work for play and load
*/
private function _load(url:String, isLive:Boolean, totalTime:Number):Void {
//ifdef DEBUG
//debugTrace("_load(" + url + ", " + isLive + ", " + totalTime + ")");
//endif
_prevVideoWidth = this.videoWidth;
if (_prevVideoWidth == undefined) {
_prevVideoWidth = _video.width;
if (_prevVideoWidth == undefined) _prevVideoWidth = 0;
}
_prevVideoHeight = this.videoHeight;
if (_prevVideoHeight == undefined) {
_prevVideoHeight = _video.height;
if (_prevVideoHeight == undefined) _prevVideoHeight = 0;
}
// reset state
_autoResizeDone = false;
_cachedPlayheadTime = 0;
_bufferState = BUFFER_EMPTY;
_metadata = null;
_startingPlay = false;
_invalidSeekTime = false;
_invalidSeekRecovery = false;
_isLive = (isLive == undefined) ? false : isLive;
_contentPath = url;
_currentPos = 0;
_streamLength = totalTime;
_atEnd = false;
_videoWidth = undefined;
_videoHeight = undefined;
_readyDispatched = false;
_lastUpdateTime = -1;
_sawSeekNotify = false;
// must stop ALL intervals here
clearInterval(_updateTimeIntervalID);
_updateTimeIntervalID = 0;
clearInterval(_updateProgressIntervalID);
_updateProgressIntervalID = 0;
clearInterval(_idleTimeoutIntervalID);
_idleTimeoutIntervalID = 0;
clearInterval(_autoResizeIntervalID);
_autoResizeIntervalID = 0;
clearInterval(_rtmpDoStopAtEndIntervalID);
_rtmpDoStopAtEndIntervalID = 0;
clearInterval(_rtmpDoSeekIntervalID);
_rtmpDoSeekIntervalID = 0;
clearInterval(_httpDoSeekIntervalID);
_httpDoSeekIntervalID = 0;
clearInterval(_finishAutoResizeIntervalID);
_finishAutoResizeIntervalID = 0;
clearInterval(_delayedBufferingIntervalID);
_delayedBufferingIntervalID = 0;
// close netstream
closeNS(false);
// if returns false, wait for a "connected" message and
// then do these things
if (_ncMgr == null || _ncMgr == undefined) {
createINCManager();
}
var instantConnect:Boolean = _ncMgr.connectToURL(_contentPath);
setState(LOADING);
_cachedState = LOADING;
if (instantConnect) {
_createStream();
_setUpStream();
}
if (!_ncMgr.isRTMP()) {
clearInterval(_updateProgressIntervalID);
_updateProgressIntervalID = setInterval(this, "doUpdateProgress", _updateProgressInterval);
}
}
/**
* Pauses video playback. If video is paused or stopped, has
* no effect. To start playback again, call play()
.
* Takes no parameters
If player is in an unresponsive state, queues the request.
* *Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* connected
property to determine when it is
* safe to call this method.
If state is already stopped, pause is does nothing and state * remains stopped.
* * @see #connected * @see #stateResponsive * @see #play() */ public function pause():Void { //ifdef DEBUG //debugTrace("pause()"); //endif if (!isXnOK()) { if ( _state == CONNECTION_ERROR || _ncMgr == null || _ncMgr == undefined || _ncMgr.getNetConnection() == null || _ncMgr.getNetConnection() == undefined ) { throw new VideoError(VideoError.NO_CONNECTION); } else { return; } } else if (_state == EXEC_QUEUED_CMD) { _state = _cachedState; } else if (!stateResponsive) { queueCmd(PAUSE); return; } else { execQueuedCmds(); } if (_state == PAUSED || _state == STOPPED || _ns == null || _ns == undefined) return; _pause(true); setState(PAUSED); } /** *Stops video playback. If autoRewind
is set to
* true
, rewinds to first frame. If video is already
* stopped, has no effect. To start playback again, call
* play()
. Takes no parameters
If player is in an unresponsive state, queues the request.
* *Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* connected
property to determine when it is
* safe to call this method.
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.
* *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.
* *If player is in an unresponsive state, queues the request.
* *Throws an exception if called when no stream is
* connected. Use "stateChange" event and
* connected
property to determine when it is
* safe to call this method.
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.
* * @see idleTimeout */ public function close():Void { //ifdef DEBUG //debugTrace("close()"); //endif closeNS(true); // never makes sense to close an http NetConnection, it doesn't really maintain // any kind of network connection! if (_ncMgr != null && _ncMgr != undefined && _ncMgr.isRTMP()) { _ncMgr.close(); } setState(DISCONNECTED); dispatchEvent({type:"close", state:_state, playheadTime:playheadTime}); } // // public getters, setters // public function get x():Number { return this._x; } public function set x(xpos:Number) { this._x = xpos; } public function get y():Number { return this._y; } public function set y(ypos:Number) { this._y = ypos; } /** * 100 is standard scale * * @see #setScale() */ function get scaleX():Number { return _video._xscale; } function set scaleX(xs:Number):Void { setScale(xs, this.scaleY); } /** * 100 is standard scale * * @see #setScale() */ function get scaleY():Number { return _video._yscale; } function set scaleY(ys:Number):Void { setScale(this.scaleX, ys); } /** *Width of video instance. Not same as Video.width, that is videoWidth.
* * @see #setSize() * @see #videoWidth */ public function get width():Number { return _video._width; } public function set width(w:Number):Void { setSize(w, _video._height); } /** *Height of video. Not same as Video.height, that is videoHeight.
* * @see #setSize() * @see #videoHeight */ public function get height():Number { return _video._height; } public function set height(h:Number):Void { setSize(_video._width, h); } /** *Source width of loaded FLV file. Read only. Returns * undefined if no information available yet.
* * @see #width */ public function get videoWidth() { if (_readyDispatched) { _videoWidth = _video.width; } return _videoWidth; } /** *Source height of loaded FLV file. Read only. Returns * undefined if no information available yet.
* * @see #height */ public function get videoHeight() { if (_readyDispatched) { _videoHeight = _video.height; } return _videoHeight; } /** *Use this instead of _visible
because we
* sometimes do internal visibility management when doing an
* autoresize.
Determines whether the instance is automatically resized to * the source dimensions. If this is set from false to true after * an FLV has been loaded, an automatic resize will start * immediately.
* */ public function get autoSize():Boolean { return _autoSize; } public function set autoSize(flag:Boolean):Void { if (_autoSize != flag) { _autoSize = flag; if (_autoSize) { startAutoResize(); } } } /** *Determines whether video aspect ratio is maintained. If
* this is set from false to true and autoSize
*
* @see #autoSize
*/
public function get maintainAspectRatio():Boolean
{
return _aspectRatio;
}
public function set maintainAspectRatio(flag:Boolean):Void
{
if (_aspectRatio != flag) {
_aspectRatio = flag;
if (_aspectRatio && !_autoSize) {
startAutoResize();
}
}
}
/**
*
Determines whether the FLV is rewound to the first frame
* when play stops, either by calling stop()
or by
* reaching the end of the stream. Meaningless for live streams.
The current playhead time in seconds. Setting does a seek * and has all the restrictions of a seek.
* *The event "playheadUpdate" is dispatched when the playhead * time changes, including every .25 seconds while the FLV is * playing.
* * @return The playhead position, measured in seconds since the start. Will return a fractional value. * @see #seek() */ public function get playheadTime():Number { var nowTime:Number = (_ns == null || _ns == undefined) ? _currentPos : _ns.time; if (_metadata.audiodelay != undefined) { nowTime -= _metadata.audiodelay; if (nowTime < 0) nowTime = 0; } return nowTime; } public function set playheadTime(position:Number):Void { seek(position); } /** *url of currently loaded (or loading) stream. Will be url
* last sent to play()
or load()
, null
* if no stream is loaded.
Volume control in range from 0 to 100.
* * @return The most recent volume setting * @see #transform */ public function get volume():Number { return _volume; } public function set volume(aVol:Number):Void { _volume = aVol; if (!_hiddenForResize) { _sound.setVolume(_volume); } } /** *Provides direct access to the
* Sound.setTransform()
and
* Sound.getTransform()
APIs. 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.
True if stream is live, read only. isLive only makes sense when * streaming from FVSS or FCS, value is ignored when doing http * download.
*/ public function get isLive():Boolean { return _isLive; } /** * Get state. Read only. Set withload
,
* play()
, stop()
,
* pause()
and seek()
.
*/
public function get state():String {
return _state;
}
/**
* Read only. Gets whether state is responsive. If state is
* unresponsive, calls to APIs play()
,
* load()
, stop()
,
* pause()
and seek()
will queue the
* requests for later, when the state changes to a responsive
* one.
*
* @see #connected
* @see #DISCONNECTED
* @see #STOPPED
* @see #PLAYING
* @see #PAUSED
* @see #LOADING
* @see #RESIZING
* @see #CONNECTION_ERROR
* @see #REWINDING
*/
public function get stateResponsive():Boolean {
switch (_state) {
case DISCONNECTED:
case STOPPED:
case PLAYING:
case PAUSED:
case BUFFERING:
return true;
default:
return false;
}
}
/**
* 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.
* */ public function get bytesLoaded():Number { if (_ns == null || _ns == undefined || _ncMgr.isRTMP()) return -1; return _ns.bytesLoaded; } /** *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.
* */ public function get bytesTotal():Number { if (_ns == null || _ns == undefined || _ncMgr.isRTMP()) return -1; return _ns.bytesTotal; } /** *property totalTime. read only. 0 or null or undefined
* means that property was not passed into play()
or
* load()
and was unable to detect automatically, or
* have not yet.
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
*/ public function get bufferTime():Number { return _bufferTime; } public function set bufferTime(aTime:Number):Void { _bufferTime = aTime; if (_ns != null && _ns != undefined) { _ns.setBufferTime(_bufferTime); } } /** *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.
* *If set when stream already idle, restarts idle timeout with * new value.
*/ public function get idleTimeout():Number { return _idleTimeoutInterval; } public function set idleTimeout(aTime:Number):Void { _idleTimeoutInterval = aTime; if (_idleTimeoutIntervalID > 0) { clearInterval(_idleTimeoutIntervalID); _idleTimeoutIntervalID = setInterval(this, "doIdleTimeout", _idleTimeoutInterval); } } /** *Property playheadUpdateInterval, which is amount of time * in milliseconds between each "playheadUpdate" event.
* *If set when stream is playing, will restart interval.
*/ public function get playheadUpdateInterval():Number { return _updateTimeInterval; } public function set playheadUpdateInterval(aTime:Number):Void { _updateTimeInterval = aTime; if (_updateTimeIntervalID > 0) { clearInterval(_updateTimeIntervalID); _updateTimeIntervalID = setInterval(this, "doUpdateTime", _updateTimeInterval); } } /** *Property progressInterval, which is amount of time * in milliseconds between each "progress" event.
* *If set when stream is playing, will restart interval.
*/ public function get progressInterval():Number { return _updateProgressInterval; } public function set progressInterval(aTime:Number):Void { _updateProgressInterval = aTime; if (_updateProgressIntervalID > 0) { clearInterval(_updateProgressIntervalID); _updateProgressIntervalID = setInterval(this, "doUpdateProgress", _updateProgressInterval); } } /** *Access to instance of the class implementing
* INCManager
. Read only.
One use case for this is that a custom
* INCManager
implementation may require custom
* initialization.
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.
* * @see #load() * @see #play() */ public function get metadata() { return _metadata; }; // // public callbacks, not really APIs // /** *Called on interval determined by
* playheadUpdateInterval
to send "playheadUpdate"
* events. Events only sent when playhead is moving, sent every
* .25 seconds by default.
Called at interval determined by
* progressInterval
to send "progress" events.
* Object dispatch starts when _load
is called, ends
* when all bytes downloaded or a network error of some kind
* occurs, dispatched every .25 seconds by default.
NetStream.onStatus
callback for rtmp. Handles
* automatic resizing, autorewind and buffering messaging.
NetStream.onStatus
callback for http. Handles
* autorewind.
Called by INCManager after when connection complete or
* failed after call to INCManager.connectToURL
.
* If connection failed, set INCManager.nc = null
* before calling.
Called by INCManager after when reconnection complete or
* failed after call to INCManager.reconnect
. If
* connection failed, set INCManager.nc = null
* before calling.
INCManager
.
* We put this off until we need to do it to give time for the
* user to customize the DEFAULT_INCMANAGER
* static variable.
*
* @private
*/
private function createINCManager():Void {
if (ncMgrClassName == null || ncMgrClassName == undefined) {
ncMgrClassName = DEFAULT_INCMANAGER;
}
var ncMgrConstructor:Function = eval( (ncMgrClassName) );
_ncMgr = new ncMgrConstructor;
_ncMgr.setVideoPlayer(this);
}
/**
* ONLY CALL THIS WITH RTMP STREAMING
* *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.
* * @private */ private function rtmpDoStopAtEnd(force:Boolean):Void { //ifdef DEBUG //debugTrace("rtmpDoStopAtEnd()"); //endif // check if we really want to stop if this was triggered on an // interval. If we are running this on an interval (see // rtmpOnStatus) we do a stop when the playhead hasn't moved // since last time we checked, we check every .25 seconds. if (_rtmpDoStopAtEndIntervalID > 0) { switch (_state) { case DISCONNECTED: case CONNECTION_ERROR: clearInterval(_rtmpDoStopAtEndIntervalID); _rtmpDoStopAtEndIntervalID = 0; return; } if (force || _cachedPlayheadTime == playheadTime) { clearInterval(_rtmpDoStopAtEndIntervalID); _rtmpDoStopAtEndIntervalID = 0; } else { _cachedPlayheadTime = playheadTime; return; } } _bufferState = BUFFER_EMPTY; _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(); if (_state != STOPPED) return; dispatchEvent({type:"complete", state:_state, playheadTime:playheadTime}); if (_state != STOPPED) return; if (_autoRewind && !_isLive && playheadTime != 0) { _atEnd = false; _currentPos = 0; _play(0, 0); setState(REWINDING); } else { closeNS(); } } /** *ONLY CALL THIS WITH RTMP STREAMING
* *Wait until time goes back to zero to leave rewinding state.
* * @private */ private function rtmpDoSeek():Void { //ifdef DEBUG //debugTrace("rtmpDoSeek()"); //endif if (_state != REWINDING && _state != SEEKING) { clearInterval(_rtmpDoSeekIntervalID); _rtmpDoSeekIntervalID = 0; _sawSeekNotify = false; } else if (playheadTime != _cachedPlayheadTime) { clearInterval(_rtmpDoSeekIntervalID); _rtmpDoSeekIntervalID = 0; _sawSeekNotify = false; setStateFromCachedState(); doUpdateTime(); } } /** *ONLY CALL THIS WITH HTTP PROGRESSIVE DOWNLOAD
* *Call this when playing stops by hitting the end.
* * @private */ private function httpDoStopAtEnd():Void { //ifdef DEBUG //debugTrace("httpDoStopAtEnd()"); //endif _atEnd = true; if ( _streamLength == undefined || _streamLength == null || _streamLength <= 0 ) { _streamLength = _ns.time; } _pause(true); setState(STOPPED); if (_state != STOPPED) return; doUpdateTime(); if (_state != STOPPED) return; dispatchEvent({type:"complete", state:_state, playheadTime:playheadTime}); if (_state != STOPPED) return; if (_autoRewind) { _atEnd = false; _pause(true); _seek(0); setState(REWINDING); } } /** *ONLY CALL THIS WITH HTTP PROGRESSIVE DOWNLOAD
* *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.
* * @private */ private function httpDoSeek():Void { //ifdef DEBUG //debugTrace("httpDoSeek()"); //debugTrace("playheadTime = " + playheadTime); //debugTrace("_cachedPlayheadTime = " + _cachedPlayheadTime); //endif 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; clearInterval(_httpDoSeekIntervalID); _httpDoSeekIntervalID = 0; // only do the rest if were seeking or rewinding to start with if (!seekState) return; setStateFromCachedState(); if (_invalidSeekTime) { _invalidSeekTime = false; _invalidSeekRecovery = true; seek(playheadTime); } else { doUpdateTime(); } } /** *Wrapper for NetStream.close()
. Never call
* NetStream.close()
directly, always call this
* method because it does some other housekeeping.
We do a brief timer before entering BUFFERING state to avoid * quick switches from BUFFERING to PLAYING and back.
* * @private */ private function doDelayedBuffering():Void { //ifdef DEBUG //debugTrace("doDelayedBuffering()"); //endif 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 clearInterval(_delayedBufferingIntervalID); _delayedBufferingIntervalID = 0; setState(BUFFERING); break; default: // any other state, bail and kill timer clearInterval(_delayedBufferingIntervalID); _delayedBufferingIntervalID = 0; break; } } /** * Wrapper forNetStream.pause()
. Never call
* NetStream.pause()
directly, always call this
* method because it does some other housekeeping.
*
* @private
*/
private function _pause(doPause:Boolean):Void {
//ifdef DEBUG
//debugTrace("_pause(" + doPause + ")");
//endif
_ns.pause(doPause);
}
/**
* Wrapper for NetStream.play()
. Never call
* NetStream.play()
directly, always call this
* method because it does some other housekeeping.
*
* @private
*/
private function _play():Void {
//ifdef DEBUG
//var debugString:String = "_play("
//if (arguments.length > 0) {
// debugString += arguments[0];
// if (arguments.length > 1) {
// debugString += ", " + arguments[1];
// }
//}
//debugString += ")";
//debugTrace(debugString);
//debugTrace("_ncMgr.getStreamName() = " + _ncMgr.getStreamName());
//endif
_startingPlay = true;
switch (arguments.length) {
case 0:
_ns.play(_ncMgr.getStreamName(), (_isLive) ? -1 : 0, -1);
break;
case 1:
_ns.play(_ncMgr.getStreamName(), (_isLive) ? -1 : arguments[0], -1);
break;
case 2:
_ns.play(_ncMgr.getStreamName(), (_isLive) ? -1 : arguments[0], arguments[1]);
break;
default:
throw new Error("bad args to _play");
}
}
/**
* Wrapper for NetStream.seek()
. Never call
* NetStream.seek()
directly, always call
* this method because it does some other housekeeping.
*
* @private
*/
private function _seek(time:Number):Void {
//ifdef DEBUG
//debugTrace("_seek(" + time + ")");
//endif
if (_metadata.audiodelay != undefined && time + _metadata.audiodelay < _streamLength) {
time += _metadata.audiodelay;
}
_ns.seek(time);
_invalidSeekTime = false;
_bufferState = BUFFER_EMPTY;
_sawSeekNotify = false;
}
/**
* Gets whether connected to a stream. If not, then calls to APIs
* play() with no args
, stop()
,
* pause()
and seek()
will throw
* exceptions.
*
* @see #stateResponsive
* @private
*/
private function isXnOK():Boolean {
if (_state == LOADING) return true;
if (_state == CONNECTION_ERROR) return false;
if (_state != DISCONNECTED) {
if ( _ncMgr == null || _ncMgr == undefined ||
_ncMgr.getNetConnection() == null ||
_ncMgr.getNetConnection() == undefined ||
!_ncMgr.getNetConnection().isConnected ) {
setState(DISCONNECTED);
return false;
}
return true;
}
return false;
}
/**
* Kicks off autoresize process
*
* @private
*/
private function startAutoResize() {
switch (_state) {
case DISCONNECTED:
case CONNECTION_ERROR:
// autoresize will happen later automatically
return;
default:
_autoResizeDone = false;
if (stateResponsive && _videoWidth != undefined && _videoHeight != undefined) {
// do it now!
doAutoResize();
} else {
// do it on an interval, it won't happen until we are
// back in a responsive state
clearInterval(_autoResizeIntervalID);
_autoResizeIntervalID = setInterval(this, "doAutoResize", AUTO_RESIZE_INTERVAL);
break;
}
}
}
/**
* Does the actual work of resetting the width and height.
* *Called on an interval which is stopped when width and height
* of the Video
object are not zero. Finishing the
* resize is done in another method which is either called on a
* interval set up here for live streams or on a
* NetStream.Play.Stop event in rtmpOnStatus
after
* stream is rewound if it is not a live stream. Still need to
* get a http solution.
Makes video visible, turns on sound and starts * playing if live or autoplay.
*/ private function finishAutoResize():Void { //ifdef DEBUG //debugTrace("finishAutoResize()"); //endif clearInterval(_finishAutoResizeIntervalID); _finishAutoResizeIntervalID = 0; if (stateResponsive) return; _visible = __visible; _sound.setVolume(_volume); _hiddenForResize = false; //ifdef DEBUG //debugTrace("_autoPlay = " + _autoPlay); //endif dispatchEvent({type:"resize", x:_x, y:_y, width:_width, height:_height}); 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); } } /** *Creates NetStream
and does some basic
* initialization.
Does initialization after first connecting to the server
* and creating the stream. Will get the stream duration from
* the INCManager
if it has it for us.
Starts resize if necessary, otherwise starts playing if * necessary, otherwise loads first frame of video. In http case, * starts progressive download in any case.
* * @private */ private function _setUpStream():Void { //ifdef DEBUG //debugTrace("_setUpStream()"); //endif _video.attachVideo(_ns); this.attachAudio(_ns); // 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.getStreamLength()) && _ncMgr.getStreamLength() >= 0 ) { _streamLength = _ncMgr.getStreamLength(); } if ( !isNaN(_ncMgr.getStreamWidth()) && _ncMgr.getStreamWidth() >= 0 ) { _videoWidth = _ncMgr.getStreamWidth(); } else { _videoWidth = undefined; } if ( !isNaN(_ncMgr.getStreamHeight()) && _ncMgr.getStreamHeight() >= 0 ) { _videoHeight = _ncMgr.getStreamHeight(); } else { _videoHeight = undefined; } // resize immediately if height and width set above if ((_autoSize || _aspectRatio) && _videoWidth != undefined && _videoHeight != undefined) { _prevVideoWidth = undefined; _prevVideoHeight = undefined; doAutoResize(); } // just start if static, start resize otherwise if ((!_autoSize && !_aspectRatio) || (_videoWidth != undefined && _videoHeight != undefined)) { 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(0, 0); } else { _play(); _pause(true); _seek(0); } } } else { _hiddenForResize = true; _hiddenForResizeMetadataDelay = 0; __visible = _visible; _visible = false; _volume = _sound.getVolume(); _sound.setVolume(0); _play(0); if (_currentPos > 0) { _seek(_currentPos); _currentPos = 0; } } clearInterval(_autoResizeIntervalID); _autoResizeIntervalID = setInterval(this, "doAutoResize", AUTO_RESIZE_INTERVAL); } /** *ONLY CALL THIS WITH RTMP STREAMING
* *Only used for rtmp connections. When we pause or stop,
* setup an interval to call this after a delay (see property
* idleTimeout
). 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.