Transferred from the Adobe Open Source Subversion Repository

Revision: 8
Author:   open-source-svn-admin@adobe.com
Date:     2007-12-15T01:49:31.087529Z

Log Message:
-----------
The beginning of the open-source Flex SDK.

Populated the flex/sdk/trunk directory with an open-source subset of files from //depot/flex/sdk/...@191097 in the Faramir Perforce server. This changelist is a bit past the 3.0rc0 build.

The Ant scripts will not successfully build yet. They need to be adapted to the open-source subset.

Modified Paths:
--------------
2532 paths were modified

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1269172 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/mx/src/mx/controls/videoClasses/VideoPlayer.as
+++ frameworks/projects/mx/src/mx/controls/videoClasses/VideoPlayer.as
@@ -0,0 +1,2930 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  ADOBE SYSTEMS INCORPORATED
+//  Copyright 2005-2007 Adobe Systems Incorporated
+//  All Rights Reserved.
+//
+//  NOTICE: Adobe permits you to use, modify, and distribute this file
+//  in accordance with the terms of the license agreement accompanying it.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+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 NetConnection is closed,
+ *  whether by being timed out or by calling the close() method.
+ *  This event is only dispatched with RTMP streams, never HTTP.
+ *
+ *  @eventType mx.events.VideoEvent.CLOSE
+ *  @helpid 3482
+ *  @tiptext close event
+ */
+[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 stop() or
+ *  pause() are called.
+ *
+ *
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.

+ *
+ *  @eventType mx.events.VideoEvent.COMPLETE
+ *  @helpid 3482
+ *  @tiptext complete event
+ */
+[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
+ */
+[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
+ */
+[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
+ */
+[Event(name="playheadUpdate", type="mx.events.VideoEvent")]
+
+/**
+ *  Dispatched every 0.25 seconds while the video is downloading.
+ *
+ *
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 load is called and ends
+ *  when all bytes are loaded or if there is a network error.

+ *
+ *  @eventType flash.events.ProgressEvent.PROGRESS
+ *  @helpid 3485
+ *  @tiptext progress event
+ */
+[Event(name="progress", type="flash.events.ProgressEvent")]
+
+/**
+ *  Dispatched when the video is loaded and ready to display.
+ *
+ *
This event is dispatched the first time the VideoPlayer
+ *  enters a responsive state after a new FLV is loaded
+ *  with the play() or load() method.
+ *  It is dispatched once for each FLV loaded.

+ *
+ *  @eventType mx.events.VideoEvent.READY
+ */
+[Event(name="ready", type="mx.events.VideoEvent")]
+
+/**
+ *  Dispatched when the video autorewinds.
+ *
+ *  @eventType mx.events.VideoEvent.REWIND
+ */
+[Event(name="rewind", type="mx.events.VideoEvent")]
+
+/**
+ *  Dispatched when the playback state changes.
+ *
+ *
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
+ *  play(), pause(), stop()
+ *  and seek() will queue the requests to be executed
+ *  when the player enters a responsive state.

+ *
+ *  @eventType mx.events.VideoEvent.STATE_CHANGE
+ */
+[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.
+ *
+ *
VideoPlayer extends Video.

+ *
+ *  @tiptext    VideoPlayer: FLV player
+ *  @helpid ???
+ */
+public class VideoPlayer extends Video
+{
+    include "../../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 #state
+     *  @see #autoRewind
+     *  @see #stateResponsive
+     */
+    public static const REWINDING:String = "rewinding";
+
+    /**
+     *
State constant.  State entered after seek()
+     *  is called.

+     *
+     *
This is a unresponsive state.

+     *
+	 *  @see #state
+     *  @see #stateResponsive
+     *  @see #seek()
+     */
+    public static const SEEKING:String = "seeking";
+
+    /**
+     *
State constant.  State entered during autoresize.

+     *
+     *
This is a unresponsive state.

+     *
+	 *  @see #state
+	 *  @see #stateResponsive
+     */
+    public static const 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
+     */
+    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
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *
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".

+     */
+    public static var DEFAULT_INCMANAGER:Class = NCManager;
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *
Constructor.

+     *  @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
+     *
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.

+     */
+    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
+     */
+    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
+     */
+    override public function set scaleY(ys:Number):void
+    {
+        setScale(this.scaleX, ys);
+    }
+
+    //----------------------------------
+    //  width
+    //----------------------------------
+
+    /**
+     *
Width of video instance.  Not same as Video.width, that is videoWidth.

+     *
+     *  @see #setSize()
+     *  @see #videoWidth
+     *  @helpid 0
+     */
+    override public function set width(value:Number):void
+    {
+        setSize(value, height);
+    }
+
+    //----------------------------------
+    //  height
+    //----------------------------------
+
+    /**
+     *
Height of video.  Not same as Video.height, that is videoHeight.

+     *
+     *  @see #setSize()
+     *  @see #videoHeight
+     *  @helpid 0
+     */
+    override public function set height(value:Number):void
+    {
+        setSize(width, value);
+    }
+
+
+    /**
+     *
Source width of loaded FLV file.  Read only.  Returns
+     *  undefined if no information available yet.

+     *
+     *  @see #width
+     */
+    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;
+    }
+
+    /**
+     *
Source height of loaded FLV file.  Read only.  Returns
+     *  undefined if no information available yet.

+     *
+     *  @see #height
+     */
+    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;
+
+    /**
+     *
Use this instead of _visible because we
+     * sometimes do internal visibility management when doing an
+     * autoresize.

+     */
+    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;
+
+    /**
+     *
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.

+     *
+     *  @helpid 0
+     */
+    public function get autoRewind():Boolean
+    {
+        return _autoRewind;
+    }
+
+    /**
+     *  @private
+     */
+    public function set autoRewind(flag:Boolean):void
+    {
+        _autoRewind = flag;
+    }
+
+    //----------------------------------
+    //  playheadTime
+    //----------------------------------
+
+    /**
+     *
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.
+	 *
+     *  @tiptext Current position of the playhead in seconds
+     *  @helpid 3463
+     *  @see #seek()
+     */
+    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;
+
+    /**
+     *
url of currently loaded (or loading) stream. Will be url
+     *  last sent to play() or load(), null
+     *  if no stream is loaded.

+     *
+     *  @tiptext Holds the relative path and filename of the media to be streamed
+     *  @helpid 3457
+     */
+    public function get url():String
+    {
+        return _url;
+    }
+
+    //----------------------------------
+    //  volume
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _volume:Number;
+
+    /**
+     *
Volume control in range from 0 to 1.

+     *
+     *  @return The most recent volume setting
+	 *
+     *  @tiptext The volume setting in value range from 0 to 1.
+     *  @helpid 3468
+     *  @see #soundTransform
+     */
+    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;
+
+    /**
+     *
Provides direct access to the
+     *  flash.media.SoundTransform 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
+     */
+    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.
+	 */
+    public function get isRTMP():Boolean
+    {
+        if (_ncMgr == null)
+			return true;
+        return _ncMgr.isRTMP();
+    }
+
+    //----------------------------------
+    //  isLive
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _isLive:Boolean;
+
+    /**
+     *
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;
+    }
+
+    //----------------------------------
+    //  state
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _state:String;
+
+    /**
+     * Get state.  Read only.  Set with load,
+     * play(), stop(),
+     * pause() and seek().
+     */
+    public function get state():String
+    {
+        return _state;
+    }
+
+    //----------------------------------
+    //  stateResponsive
+    //----------------------------------
+
+    /**
+     *  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 #MAX_RESPONSIVE_STATE
+     *  @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;
+		}
+    }
+
+    //----------------------------------
+    //  bytesLoaded
+    //----------------------------------
+
+    /**
+     *
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.

+     *
+     *  @tiptext Number of bytes already loaded
+     *  @helpid 3455
+     */
+    public function get bytesLoaded():int
+    {
+        if (ns == null || _ncMgr.isRTMP())
+            return -1;
+        return ns.bytesLoaded;
+    }
+
+    //----------------------------------
+    //  bytesTotal
+    //----------------------------------
+
+    /**
+     *
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.

+     *
+     *  @tiptext Number of bytes to be loaded
+     *  @helpid 3456
+     */
+    public function get bytesTotal():int
+    {
+        if (ns == null || _ncMgr.isRTMP())
+            return -1;
+        return ns.bytesTotal;
+    }
+
+    //----------------------------------
+    //  totalTime
+    //----------------------------------
+
+    /**
+     *
property totalTime.  read only.  -1 means that property
+     *  was not pass into play() or
+     * load() 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
+     */
+    public function get totalTime():Number
+    {
+        return streamLength;
+    }
+
+    //----------------------------------
+    //  bufferTime
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _bufferTime:Number;
+
+    /**
+     *
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;
+    }
+
+    /**
+     *  @private
+     */
+    public function set bufferTime(aTime:Number):void
+    {
+        _bufferTime = aTime;
+        if (ns != null)
+            ns.bufferTime = _bufferTime;
+    }
+
+    //----------------------------------
+    //  idleTimeout
+    //----------------------------------
+
+    /**
+     *
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():uint
+    {
+        return idleTimeoutTimer.delay;
+    }
+
+    /**
+     *  @private
+     */
+    public function set idleTimeout(aTime:uint):void
+    {
+        idleTimeoutTimer.delay = aTime;
+    }
+
+    //----------------------------------
+    //  playheadUpdateInterval
+    //----------------------------------
+
+    /**
+     *
Property playheadUpdateInterval, which is amount of time
+     * in milliseconds between each "playheadUpdate" event.

+     *
+     *
If set when stream is playing, will restart timer.

+     */
+    public function get playheadUpdateInterval():uint
+    {
+        return updateTimeTimer.delay;
+    }
+
+    /**
+     *  @private
+     */
+    public function set playheadUpdateInterval(aTime:uint):void
+    {
+        updateTimeTimer.delay = aTime;
+    }
+
+    //----------------------------------
+    //  progressInterval
+    //----------------------------------
+
+    /**
+     *
Property progressInterval, which is amount of time
+     * in milliseconds between each "progress" event.

+     *
+     *
If set when stream is playing, will restart timer.

+     */
+    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;
+
+    /**
+     *
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.

+     */
+    public function get ncMgr():INCManager
+    {
+        if (_ncMgr == null)
+            createINCManager();
+        return _ncMgr;
+    }
+
+    //----------------------------------
+    //  metadata
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _metadata:Object;
+
+    /**
+     *
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():Object
+    {
+        return _metadata;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+
+
+    /**
+     *
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 == width && h == height)
+			return;
+        super.width = w;
+        super.height = h;
+    }
+
+    /**
+     *
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):void
+    {
+        if (xs == super.scaleX && ys == super.scaleY)
+			return;
+        super.scaleX = xs;
+        super.scaleY = ys;
+    }
+
+    /**
+     *
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.

+     *
+     *  @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 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 -1
+     *  to automatically detect length from metadata, server
+     *  or xml.  If INCManager.streamLength is not -1 when
+     *  ncConnected is called, then
+     *  that value will trump this one in any case.  Default is -1.
+     *
+     *  @see #connected
+     *  @see #stateResponsive
+     *  @see #load()
+     */
+    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
+    }
+
+    /**
+     *
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 -1 to
+     *  automatically detect length from metadata, server or xml.
+     *  If INCManager.streamLength is not -1 when
+     *  ncConnected is called, then that value will
+     *  trump this one in any case.  Default is -1.
+     *  @see #connected
+     *  @see #play()
+     */
+    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);
+    }
+
+    /**
+     *
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
+    {
+        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);
+    }
+
+    /**
+     *
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.

+     *
+     *  @see #connected
+     *  @see #stateResponsive
+     *  @see #autoRewind
+     *  @see #play()
+     */
+    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);
+        }
+    }
+
+    /**
+     *
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.

+     *
+     *  @param time seconds
+     *  @throws VideoError if time is < 0
+     *  @see #connected
+     *  @see #stateResponsive
+     */
+    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;
+        }
+    }
+
+    /**
+     *
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
+    {
+        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
+     *
Called by Timer updateTimeTimer to send
+     *  "playheadUpdate" events.  Events only sent when playhead is
+     *  moving, sent every .25 seconds (see
+     *  _updateTimeInterval).
+     */
+    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
+     *
Called by Timer _updateProgressTimer to send
+     *  "progress" events.  Event dispatch starts when
+     *  _load 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
+     *
NetStream.onStatus callback for rtmp.  Handles
+     *  automatic resizing, autorewind and buffering messaging.

+     */
+    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
+     *
NetStream.onStatus callback for http.  Handles
+     *  autorewind.

+     */
+    public function httpOnStatus(event:NetStatusEvent):void
+    {
+        switch (event.info.code)
+        {
+        case "NetStream.Play.Stop":
+            delayedBufferingTimer.reset();
+            if (invalidSeekTime)
+            {
+                invalidSeekTime = false;
+                invalidSeekRecovery = true;
+                setState(cachedState);
+                seek(playheadTime);
+            }
+            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
+            	invalidSeekTime = true;
+            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
+     *
Called by INCManager after when connection complete or
+     *  failed after call to INCManager.connectToURL.
+     *  If connection failed, set INCManager.netConnection = null
+     *  before calling.

+     *
+     *  @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
+     *
Called by INCManager after when reconnection complete or
+     *  failed after call to INCManager.reconnect.  If
+     *  connection failed, set INCManager.netConnection = null
+     *  before calling.

+     *
+     *  @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
+     *  creates our implementatino of the 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 function createINCManager():void
+    {
+        var ncMgrClass:Class = (ncMgrClassName == null) ? DEFAULT_INCMANAGER : ncMgrClassName;
+        _ncMgr = new ncMgrClass();
+        _ncMgr.videoPlayer = this;
+    }
+
+    /**
+     *  @private
+     *
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 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
+	 *
ONLY CALL THIS WITH RTMP STREAMING

+	 *
+	 *
Wait until time goes back to zero to leave rewinding state.

+	 */
+	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
+     *
ONLY CALL THIS WITH HTTP PROGRESSIVE DOWNLOAD

+     *
+     *
Call this when playing stops by hitting the end.

+     */
+    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
+     *
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 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;
+
+		setStateFromCachedState();
+		if (invalidSeekTime)
+		{
+			invalidSeekTime = false;
+			invalidSeekRecovery = true;
+			seek(playheadTime);
+		}
+		else
+            doUpdateTime(null);
+    }
+
+    /**
+     *  @private
+     *
Wrapper for NetStream.close().  Never call
+     *  NetStream.close() directly, always call this
+     *  method because it does some other housekeeping.

+     */
+    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
+     *
We do a brief timer before entering BUFFERING state to avoid
+     *  quick switches from BUFFERING to PLAYING and back.

+     */
+    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 NetStream.pause() and NetStream.resume().
+	 *  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 NetStream.play().  Never call
+     *  NetStream.play() 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 NetStream.seek().  Never call
+     *  NetStream.seek() 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
+     *  play() with no args, stop(),
+     *  pause() and seek() 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
+     *
Does the actual work of resetting the width and height.

+     *
+     *
Called on a timer 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
+     *  timer 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.

+     */
+    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);
+    }
+
+    /**
+     *
Makes video visible, turns on sound and starts
+     * playing if live or autoplay.

+     */
+    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
+     *
Creates NetStream and does some basic
+     *  initialization.

+     */
+    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
+     *
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 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
+     *
ONLY CALL THIS WITH RTMP STREAMING

+     *
+     *
Only used for rtmp connections.  When we pause or stop,
+     *  setup a timer 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.

+     */
+    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.
+	 */
+	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
+    {
+    }
+}


Transferred from the Adobe Open Source Subversion Repository

Revision: 5099
Author:   gauravj@adobe.com
Date:     2009-02-27T03:43:02.639229Z

Log Message:
-----------
Update action script files with asdoc version tags.

QE Notes: None.
Doc Notes: Please review and update as necessary.
tests: checkintests

Modified Paths:
--------------
147 paths were modified

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1273953 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/rpc/src/mx/messaging/messages/ISmallMessage.as
+++ frameworks/projects/rpc/src/mx/messaging/messages/ISmallMessage.as
@@ -15,6 +15,12 @@
 /**
  * A marker interface that is used to indicate that an IMessage has an
  * alternative smaller form for serialization.
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 9
+ *  @playerversion AIR 1.1
+ *  @productversion BlazeDS 4
+ *  @productversion LCDS 3
  */
 public interface ISmallMessage extends IMessage
 {
@@ -33,6 +39,12 @@
      * @return Returns An alternative representation of an
      * flex.messaging.messages.IMessage so that the serialized form
      * is smaller than the regular message.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion BlazeDS 4
+     *  @productversion LCDS 3
      */
     function getSmallMessage():IMessage;
 }


Transferred from the Adobe Open Source Subversion Repository

Revision: 12322
Author:   cframpto@adobe.com
Date:     2009-12-01T14:50:07.041950Z

Log Message:
-----------
Add support for custom context menus to TextArea, TextInput and RichEditableText.  TLF has a context menu but we now allow it to be modified, or a totally different one (or none) to be used instead.

When pasting text into a field with multiline equal to false, strip the newlines from the pasted text.

For composite operations, the RET changing event is now dispatched only at the beginning of the composite operation, rather than at the beginning of each individual operation in the composite operation.  It is not possible to a composite operation as it is for individual operations.

The RET change event is now dispatched at flow operation complete rather than flow operation end.  At this point the text flow is no longer in an intermediary state and it can be modified if desired in the user's event handler.

QE notes:
Doc notes:
Bugs: SDK-23950, SDK-23926, SDK-24263
Reviewer: Gordon
Tests run: checkintests
Is noteworthy for integration: no

Modified Paths:
--------------
<path kind="file" action="M">/flex/sdk/trunk/frameworks/projects/spark/src/spark/components/supportClasses/RichEditableTextContainerManager.as</path>
<path kind="file" action="M">/flex/sdk/trunk/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as</path>
<path kind="file" action="M">/flex/sdk/trunk/frameworks/projects/spark/src/spark/components/RichEditableText.as</path>

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1280200 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/spark/src/spark/components/RichEditableText.as
+++ frameworks/projects/spark/src/spark/components/RichEditableText.as
@@ -29,6 +29,7 @@
 import flash.text.engine.TextBlock;
 import flash.text.engine.TextElement;
 import flash.text.engine.TextLine;
+import flash.ui.ContextMenu;
 import flash.ui.Keyboard;

 import flashx.textLayout.compose.ISWFContext;
@@ -492,6 +493,13 @@
      */
     private static var staticTextFormat:TextFormat;

+    /**
+     *  @private
+     *  Regular expression which matches all newlines in the text.  Used
+     *  to strip newlines when pasting text when multiline is false.
+     */
+    private static const ALL_NEWLINES_REGEXP:RegExp = /\n/g;
+
     //--------------------------------------------------------------------------
     //
     //  Class properties
@@ -603,6 +611,10 @@
         _textContainerManager.addEventListener(
             FlowOperationEvent.FLOW_OPERATION_END,
             textContainerManager_flowOperationEndHandler);
+
+        _textContainerManager.addEventListener(
+            FlowOperationEvent.FLOW_OPERATION_COMPLETE,
+            textContainerManager_flowOperationCompleteHandler);

         _textContainerManager.addEventListener(
             StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,
@@ -997,6 +1009,57 @@
     public function get contentWidth():Number
     {
         return _contentWidth;
+    }
+
+    //----------------------------------
+    //  contextMenu
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Use to distinguish between null because it hasn't been set yet and
+     *  null because it was set to null to remove the default context menu.
+     */
+    mx_internal var contextMenuSet:Boolean;
+
+    /**
+     *  The context menu associated with this component.  If the context menu
+     *  has not been set this is the default context menu which will be used.
+     *  Set the context menu to null to remove the default menu.
+     *
+     *  @see flash.display.InteractiveObject
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function get contextMenu():ContextMenu
+    {
+        if (!contextMenuSet)
+        {
+            super.contextMenu = _textContainerManager.getContextMenu();
+            contextMenuSet = true;
+        }
+
+        return super.contextMenu;
+    }
+
+    /**
+     *  @private
+     */
+    override public function set contextMenu(value:ContextMenu):void
+    {
+        if (contextMenuSet && super.contextMenu == value)
+            return;
+
+        // If the context menu is being reset need to update the menu listeners.
+        if (contextMenuSet && super.contextMenu)
+            _textContainerManager.resetContextMenuListener(value);
+
+        super.contextMenu = value;
+
+        contextMenuSet = true;
     }

     //----------------------------------
@@ -1560,7 +1623,8 @@
      *
If true, the Enter key starts a new paragraph.
      *  If false, the Enter key doesn't affect the text
      *  but causes the RichEditableText to dispatch an "enter"
-     *  event.

+     *  event.  If you paste text into the RichEditableText with a multiline
+     *  value of true, newlines are stripped out of the text.

      *
      *  @default true
      *
@@ -3247,6 +3311,11 @@
      */
     mx_internal function isMeasureFixed():Boolean
     {
+        // This can be called from RET EditManager when hostFormat is null
+        // because a style changed and commitProperties hasn't run yet.
+        if (!_textContainerManager.hostFormat)
+            updateStylesIfChanged();
+
         if (_textContainerManager.hostFormat.blockProgression !=
             BlockProgression.TB)
         {
@@ -3593,12 +3662,25 @@
      */
     private function handlePasteOperation(op:PasteOperation):void
     {
-        if (!restrict && !maxChars && !displayAsPassword)
-            return;
-
         // If copied/cut from displayAsPassword field the pastedText
         // is '*' characters but this is correct.
         var pastedText:String = TextUtil.extractText(op.textScrap.textFlow);
+        // See if there is anything we need to do.
+        if (!restrict && !maxChars && !displayAsPassword)
+        {
+            if (multiline || pastedText.indexOf("\n") == -1)
+                return;
+        }
+
+        // Save this in case we modify the pasted text.  We need to know
+        // how much text to delete.
+        var textLength:int = pastedText.length;
+
+        // If multiline is false, strip newlines out of pasted text
+        // This will not strip newlines out of displayAsPassword fields
+        // since the text is the passwordChar and newline won't be found.
+        if (!multiline)
+            pastedText = pastedText.replace(ALL_NEWLINES_REGEXP, "");

         // We know it's an EditManager or we wouldn't have gotten here.
         var editManager:IEditManager =
@@ -3611,7 +3693,7 @@

         var selectionState:SelectionState = new SelectionState(
             op.textFlow, op.absoluteStart,
-            op.absoluteStart + pastedText.length);
+            op.absoluteStart + textLength);
         editManager.deleteText(selectionState);

         // Insert the same text, the same place where the paste was done.
@@ -4095,7 +4177,7 @@
     private function textContainerManager_flowOperationBeginHandler(
                         event:FlowOperationEvent):void
     {
-        //trace("flowOperationBegin", "generation", textFlow.generation);
+        //trace("flowOperationBegin", "level", event.level);

         var op:FlowOperation = event.operation;

@@ -4191,7 +4273,9 @@

         // Dispatch a 'changing' event from the RichEditableText
         // as notification that an editing operation is about to occur.
-        if (dispatchChangeAndChangingEvents)
+        // The level will be 0 for single operations, and at the start
+        // of a composite operation.
+        if (dispatchChangeAndChangingEvents && event.level == 0)
         {
             var newEvent:TextOperationEvent =
                 new TextOperationEvent(TextOperationEvent.CHANGING);
@@ -4214,16 +4298,30 @@
     private function textContainerManager_flowOperationEndHandler(
                         event:FlowOperationEvent):void
     {
-        //trace("flowOperationEnd", "generation", textFlow.generation);
+        //trace("flowOperationEnd", "level", event.level);

         // Paste is a special case.  Any mods have to be made to the text
         // which includes what was pasted.
         if (event.operation is PasteOperation)
             handlePasteOperation(PasteOperation(event.operation));
-
+    }
+
+    /**
+     *  @private
+     *  Called when the TextContainerManager dispatches an 'operationComplete'
+     *  event after an editing operation.
+     */
+    private function textContainerManager_flowOperationCompleteHandler(
+        event:FlowOperationEvent):void
+    {
+        //trace("flowOperationComplete", "level", event.level);
+
         // Dispatch a 'change' event from the RichEditableText
         // as notification that an editing operation has occurred.
-        if (dispatchChangeAndChangingEvents)
+        // The flow is now in a state that it can be manipulated.
+        // The level will be 0 for single operations, and at the end
+        // of a composite operation.
+        if (dispatchChangeAndChangingEvents && event.level == 0)
         {
             var newEvent:TextOperationEvent =
                 new TextOperationEvent(TextOperationEvent.CHANGE);


Transferred from the Adobe Open Source Subversion Repository

Revision: 19270
Author:   cframpto@adobe.com
Date:     2010-12-10T18:21:34.150045Z

Log Message:
-----------
Add mouseWheelChanging event to VScrollBar, HScrollBar and Scroller.  The event is dispatched before the mouseWheel event modifies the scroll position.  The event can be canceled to prevent the mouseWheel handler's default behavior or the event's delta can be modified and that value will be used by the mouseWheel handler when calculating the new scroll position.  In RichEditableText, when pasting, add null check before accessing the TextScrap.  There is a bug in one or more version of the player such that this can be null at times.

QE notes: Hans said we will do a PARB review by email for the new event
Doc notes:
Bugs: SDK-27106, SDK-26432
Reviewer: Hans (scroller), Darrell (RET)
Tests run: cyclone
Is noteworthy for integration: yes - new mouseWheelChanging event on VScrollBar, HScrollBar and Scroller

Modified Paths:
--------------
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/RichEditableText.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/Scroller.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/framework/src/mx/events/FlexMouseEvent.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/VScrollBar.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/HScrollBar.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/utils/MouseEventUtil.as</path>

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1280252 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/spark/src/spark/components/RichEditableText.as
+++ frameworks/projects/spark/src/spark/components/RichEditableText.as
@@ -4093,6 +4093,10 @@
             if (!hasConstraints && multiline)
                 return;

+            // Make sure the clipboard has something to paste.
+            if (op.textScrap == null || op.textScrap.textFlow == null)
+                return;
+
             // If copied/cut from displayAsPassword field the pastedText
             // is '*' characters but this is correct.
             var pastedText:String = staticPlainTextExporter.export(