| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.controls.videoClasses |
| { |
| |
| import flash.events.Event; |
| import flash.events.NetStatusEvent; |
| import flash.events.TimerEvent; |
| import flash.net.NetConnection; |
| import flash.net.ObjectEncoding; |
| import flash.net.Responder; |
| import flash.utils.Timer; |
| import mx.managers.ISystemManager; |
| import mx.managers.SystemManager; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| |
| [ExcludeClass] |
| |
| [ResourceBundle("controls")] |
| |
| /** |
| * @private |
| * Creates <code>NetConnection</code> for <code>VideoPlayer</code>, a |
| * helper class for that user facing class. |
| */ |
| public class NCManager implements INCManager |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Default connection timeout in milliseconds. |
| * |
| * @see #timeout |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const DEFAULT_TIMEOUT:Number = 60000; |
| |
| /** |
| * @private |
| */ |
| private static const DEFAULT_NC_TIMEOUT:uint = 1500; // .5 seconds |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * List of connections tried by connectRTMP, in order tried. |
| */ |
| private static var RTMP_CONN:Array = |
| [ |
| { protocol: "rtmp:/", port: "1935" }, |
| { protocol: "rtmp:/", port:"443" }, |
| { protocol: "rtmpt:/", port:"80" }, |
| { protocol: "rtmps:/", port:"443" } |
| ]; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Constructor. |
| */ |
| public function NCManager() |
| { |
| super(); |
| |
| initNCInfo(); |
| initOtherInfo(); |
| |
| // intervals |
| owner = null; |
| _netConnection = null; |
| ncConnected = false; |
| |
| // actually calls setter |
| timeout = DEFAULT_TIMEOUT; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * <p>fallbackServerName is exposed in two ways:</p> |
| * |
| * <p>User can supply second <meta base> in smil and that base |
| * attr will be taken as the fallbackServerName (note that only |
| * the server name will be taken from this and not the application |
| * name or anything else).</p> |
| * |
| * <p>The second way is the user can directly set this by |
| * accessing the ncMgr property in FLVPlayback or VideoPlayer and |
| * set fallbackServerName property directly.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var fallbackServerName:String; |
| |
| /** |
| * @private |
| * Reference to the VideoPlayer instance associated with this INCManager. |
| */ |
| private var owner:VideoPlayer; |
| |
| /** |
| * @private |
| * The path to the content. |
| */ |
| private var contentPath:String; |
| |
| /** |
| * @private |
| */ |
| private var protocol:String; |
| |
| /** |
| * @private |
| * The host name portion of the URL. |
| */ |
| private var serverName:String; |
| |
| /** |
| * @private |
| * The port number portion of the URL. |
| */ |
| private var portNumber:String; |
| |
| /** |
| * @private |
| */ |
| private var wrappedURL:String; |
| |
| /** |
| * @private |
| */ |
| private var appName:String; |
| |
| /** |
| * @private |
| * Multiple streams for multiple bandwidths. |
| */ |
| private var streams:Array; |
| |
| /** |
| * @private |
| * Whether the protocol is RTMP. |
| * -1: undefined |
| * 0: no |
| * 1: yes |
| */ |
| private var _isRTMP:int = -1; |
| |
| /** |
| * @private |
| * Timer for connection timeout. |
| */ |
| private var connectionTimer:Timer; |
| |
| /** |
| * @private |
| */ |
| private var autoSenseBW:Boolean; |
| |
| /** |
| * @private |
| * Bandwidth detection stuff. |
| */ |
| public var payload:uint; |
| |
| /** |
| * @private |
| * Value of the NetConnection instance's uri property. This is saved upon |
| * connection and reused for reconnecting. |
| */ |
| private var ncURI:String; |
| |
| /** |
| * @private |
| */ |
| private var ncConnected:Boolean; |
| |
| /** |
| * @private |
| * Info on multiple connections we try. |
| */ |
| public var tryNC:Array; |
| |
| /** |
| * @private |
| */ |
| private var tryNCTimer:Timer; |
| |
| |
| /** |
| * @private |
| * Counter that tracks the next type to try in RTMP_CONN. |
| */ |
| private var connTypeCounter:uint; |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Public Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // timeout |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for timeout property. |
| */ |
| private var _timeout:uint; |
| |
| /** |
| * @see INCManager#timeout |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get timeout():uint |
| { |
| return _timeout; |
| } |
| |
| public function set timeout(t:uint):void |
| { |
| _timeout = t; |
| connectionTimer = new Timer(_timeout, 1); |
| connectionTimer.addEventListener(TimerEvent.TIMER, onFCSConnectTimeOut); |
| } |
| |
| //---------------------------------- |
| // bitrate |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for bitrate property. |
| */ |
| private var _bitrate:Number; |
| |
| /** |
| * For RTMP streams, returns value calculated from autodetection, |
| * not value set via bitrate. |
| * |
| * @see INCManager#bitrate |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get bitrate():Number |
| { |
| return _bitrate; |
| } |
| |
| /** |
| * This value is only used with progressive download (HTTP), with |
| * RTMP streaming uses autodetection. |
| * |
| * @see INCManager#bitrate |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function set bitrate(b:Number):void |
| { |
| if (_isRTMP == 0) |
| _bitrate = b; |
| } |
| |
| //---------------------------------- |
| // videoPlayer |
| //---------------------------------- |
| |
| /** |
| * @see INCManager#videoPlayer |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get videoPlayer():VideoPlayer |
| { |
| return owner; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set videoPlayer(value:VideoPlayer):void |
| { |
| owner = value; |
| } |
| |
| //---------------------------------- |
| // netConnection |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _netConnection:NetConnection; |
| |
| /** |
| * @see INCManagernetConnection |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get netConnection():NetConnection |
| { |
| return _netConnection; |
| } |
| |
| //---------------------------------- |
| // streamName |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for streamName property. |
| */ |
| private var _streamName:String; |
| |
| /** |
| * @see INCManager#streamName |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get streamName():String |
| { |
| return _streamName; |
| } |
| |
| //---------------------------------- |
| // streamLength |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for streamLength property. |
| */ |
| private var _streamLength:Number; |
| |
| /** |
| * @see INCManager#streamLength |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get streamLength():Number |
| { |
| return _streamLength; |
| } |
| |
| //---------------------------------- |
| // streamWidth |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for streamWidth property. |
| */ |
| private var _streamWidth:Number; |
| |
| /** |
| * @see INCManager#streamWidth |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get streamWidth():Number |
| { |
| return _streamWidth; |
| } |
| |
| //---------------------------------- |
| // streamHeight |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for streamHeight property. |
| */ |
| private var _streamHeight:Number; |
| |
| /** |
| * @see INCManager#streamHeight |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get streamHeight():Number |
| { |
| return _streamHeight; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Public methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @see INCManager#isRTMP |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function isRTMP():Boolean |
| { |
| return _isRTMP != 0; |
| } |
| |
| /** |
| * @see INCManager#connectToURL() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function connectToURL(url:String):Boolean |
| { |
| initOtherInfo(); |
| |
| contentPath = url; |
| if (!contentPath || contentPath == "") |
| throw new VideoError(VideoError.INVALID_CONTENT_PATH); |
| |
| // parse URL to determine what to do with it |
| var parseResults:Object = parseURL(contentPath); |
| |
| if (!parseResults.streamName || parseResults.streamName == "") |
| throw new VideoError(VideoError.INVALID_CONTENT_PATH, url); |
| |
| // connect to either rtmp or http or download and parse smil |
| if (parseResults.isRTMP) |
| { |
| var canReuseRTMP:Boolean = canReuseOldConnection(parseResults); |
| _isRTMP = 1; |
| protocol = parseResults.protocol; |
| _streamName = parseResults.streamName; |
| serverName = parseResults.serverName; |
| wrappedURL = parseResults.wrappedURL; |
| portNumber = parseResults.portNumber; |
| appName = parseResults.appName; |
| if ( !appName || appName == "" || |
| !_streamName || _streamName == "" ) |
| throw new VideoError(VideoError.INVALID_CONTENT_PATH, url); |
| autoSenseBW = (_streamName.indexOf(",") >= 0); |
| return (canReuseRTMP || connectRTMP()); |
| } |
| else |
| { |
| var canReuseHTTP:Boolean = canReuseOldConnection(parseResults); |
| _isRTMP = 0; |
| _streamName = parseResults.streamName; |
| return (canReuseHTTP || connectHTTP()); |
| } |
| } |
| |
| /** |
| * @see INCManager#connectAgain() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function connectAgain():Boolean |
| { |
| var slashIndex:Number = appName.indexOf("/"); |
| if (slashIndex < 0) |
| { |
| // return the appName and streamName back to original form |
| // so we can start this process all over again with the |
| // fallback server if necessary |
| slashIndex = _streamName.indexOf("/"); |
| if (slashIndex >= 0) |
| { |
| appName += "/"; |
| appName += _streamName.slice(0, slashIndex); |
| _streamName = _streamName.slice(slashIndex + 1); |
| } |
| return false; |
| } |
| |
| var newStreamName:String = appName.slice(slashIndex + 1); |
| newStreamName += "/"; |
| newStreamName += _streamName; |
| _streamName = newStreamName; |
| appName = appName.slice(0, slashIndex); |
| close(); |
| payload = 0; |
| connTypeCounter = 0; |
| cleanConns(); |
| connectRTMP(); |
| return true; |
| } |
| |
| /** |
| * INCManager#reconnect |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function reconnect():void |
| { |
| if (!_isRTMP) |
| { |
| var message:String = resourceManager.getString( |
| "controls", "invalidCall"); |
| throw new Error(message); |
| } |
| |
| _netConnection.addEventListener(NetStatusEvent.NET_STATUS, reconnectOnStatus); |
| _netConnection.client = new NCManagerReconnectClient(this); |
| _netConnection.connect(ncURI, false); |
| } |
| |
| /** |
| * @see INCManager#close |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function close():void |
| { |
| if (_netConnection) |
| { |
| _netConnection.close(); |
| ncConnected = false; |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Private Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Initialization. |
| */ |
| private function initNCInfo():void |
| { |
| _isRTMP = -1; |
| serverName = null; |
| wrappedURL = null; |
| portNumber = null; |
| appName = null; |
| } |
| |
| private function initOtherInfo():void |
| { |
| contentPath = null; |
| _streamName = null; |
| _streamLength = -1; |
| _streamWidth = -1; |
| _streamHeight = -1; |
| streams = null; |
| autoSenseBW = false; |
| |
| payload = 0; |
| connTypeCounter = 0; |
| cleanConns(); |
| } |
| |
| /** |
| * matches bitrate with stream |
| * |
| * @private |
| */ |
| private function bitrateMatch():void |
| { |
| var whichStream:Number; |
| var checkBitrate:Number = _bitrate; |
| |
| if (isNaN(checkBitrate)) |
| checkBitrate = 0; |
| |
| for (var j:uint = 0; j < streams.length; j++) |
| if (isNaN(streams[j].bitrate) || checkBitrate >= streams[j].bitrate) |
| { |
| whichStream = j; |
| break; |
| } |
| |
| if (isNaN(whichStream)) |
| throw new VideoError(VideoError.NO_BITRATE_MATCH); |
| |
| if (_streamName) |
| _streamName += streams[whichStream].src; |
| else |
| _streamName = streams[whichStream].src; |
| |
| _streamLength = streams[whichStream].dur; |
| } |
| |
| /** |
| * <p>Parses URL to determine if it is http or rtmp. If it is rtmp, |
| * breaks it into pieces to extract server URL and port, application |
| * name and stream name. If .flv is at the end of an rtmp URL, it |
| * will be stripped off.</p> |
| * |
| * @private |
| */ |
| private function parseURL(url:String):Object |
| { |
| var parseResults:Object = {}; |
| |
| // get protocol |
| var startIndex:uint = 0; |
| var endIndex:int = url.indexOf(":/", startIndex); |
| if (endIndex >= 0) |
| { |
| endIndex += 2; |
| parseResults.protocol = url.slice(startIndex, endIndex); |
| parseResults.isRelative = false; |
| } |
| else |
| parseResults.isRelative = true; |
| |
| if ( parseResults.protocol != null && |
| ( parseResults.protocol == "rtmp:/" || |
| parseResults.protocol == "rtmpt:/" || |
| parseResults.protocol == "rtmps:/" || |
| parseResults.protocol == "rtmpe:/" || |
| parseResults.protocol == "rmpte:/" ) ) |
| { |
| parseResults.isRTMP = true; |
| |
| startIndex = endIndex; |
| |
| if (url.charAt(startIndex) == '/') |
| { |
| startIndex++; |
| // get server (and maybe port) |
| var colonIndex:Number = url.indexOf(":", startIndex); |
| var slashIndex:Number = url.indexOf("/", startIndex); |
| if (slashIndex < 0) |
| { |
| if (colonIndex < 0) |
| parseResults.serverName = url.slice(startIndex); |
| else |
| { |
| endIndex = colonIndex; |
| parseResults.portNumber = url.slice(startIndex, endIndex); |
| startIndex = endIndex + 1; |
| parseResults.serverName = url.slice(startIndex); |
| } |
| return parseResults; |
| } |
| if (colonIndex >= 0 && colonIndex < slashIndex) |
| { |
| endIndex = colonIndex; |
| parseResults.serverName = url.slice(startIndex, endIndex); |
| startIndex = endIndex + 1; |
| endIndex = slashIndex; |
| parseResults.portNumber = url.slice(startIndex, endIndex); |
| } |
| else |
| { |
| endIndex = slashIndex; |
| parseResults.serverName = url.slice(startIndex, endIndex); |
| } |
| startIndex = endIndex + 1; |
| } |
| |
| // handle wrapped RTMP servers bit recursively, if it is there |
| if (url.charAt(startIndex) == '?') |
| { |
| var subURL:String = url.slice(startIndex + 1); |
| var subParseResults:Object = parseURL(subURL); |
| if (!subParseResults.protocol || !subParseResults.isRTMP) |
| throw new VideoError(VideoError.INVALID_CONTENT_PATH, url); |
| parseResults.wrappedURL = "?"; |
| parseResults.wrappedURL += subParseResults.protocol; |
| if (subParseResults.serverName != null) |
| { |
| parseResults.wrappedURL += "/"; |
| parseResults.wrappedURL += subParseResults.server; |
| } |
| if (subParseResults.wrappedURL != null) |
| { |
| parseResults.wrappedURL += "/?"; |
| parseResults.wrappedURL += subParseResults.wrappedURL; |
| } |
| parseResults.appName = subParseResults.appName; |
| parseResults.streamName = subParseResults.streamName; |
| return parseResults; |
| } |
| |
| // get application name |
| endIndex = url.indexOf("/", startIndex); |
| if (endIndex < 0) |
| { |
| parseResults.appName = url.slice(startIndex); |
| return parseResults; |
| } |
| parseResults.appName = url.slice(startIndex, endIndex); |
| startIndex = endIndex + 1; |
| |
| // check for instance name to be added to application name |
| endIndex = url.indexOf("/", startIndex); |
| if (endIndex < 0) |
| { |
| parseResults.streamName = url.slice(startIndex); |
| // strip off .flv and .flv2 if included |
| if (parseResults.streamName.slice(-5).toLowerCase() == ".flv2") |
| parseResults.streamName = parseResults.streamName.slice(0, -5); |
| else if (parseResults.streamName.slice(-4).toLowerCase() == ".flv") |
| parseResults.streamName = parseResults.streamName.slice(0, -4); |
| return parseResults; |
| } |
| parseResults.appName += "/"; |
| parseResults.appName += url.slice(startIndex, endIndex); |
| startIndex = endIndex + 1; |
| |
| // get flv name |
| parseResults.streamName = url.slice(startIndex); |
| // strip off .flv and .flv2 if included |
| if (parseResults.streamName.slice(-5).toLowerCase() == ".flv2") |
| parseResults.streamName = parseResults.streamName.slice(0, -5); |
| else if (parseResults.streamName.slice(-4).toLowerCase() == ".flv") |
| parseResults.streamName = parseResults.streamName.slice(0, -4); |
| |
| } |
| else |
| { |
| // is http, just return the full url received as streamName |
| parseResults.isRTMP = false; |
| parseResults.streamName = url; |
| } |
| |
| return parseResults; |
| } |
| |
| /** |
| * @private |
| * <p>Handles creating <code>NetConnection</code> instance for |
| * progressive download of FLV via http.</p> |
| */ |
| private function connectHTTP():Boolean |
| { |
| _netConnection = new NetConnection(); |
| _netConnection.objectEncoding = ObjectEncoding.AMF0; |
| _netConnection.connect(null); |
| ncConnected = true; |
| return true; |
| } |
| |
| /** |
| * @private |
| * <p>Top level function for creating <code>NetConnection</code> |
| * instance for streaming playback of FLV via rtmp. Actually |
| * tries to create several different connections using different |
| * protocols and ports in a pipeline, so multiple connection |
| * attempts may be occurring simultaneously, and will use the |
| * first one that connects successfully.</p> |
| */ |
| private function connectRTMP():Boolean |
| { |
| // setup timeout |
| connectionTimer.reset(); |
| connectionTimer.start(); |
| |
| tryNC = []; |
| |
| tryNCTimer = new Timer(DEFAULT_NC_TIMEOUT); |
| tryNCTimer.addEventListener(TimerEvent.TIMER, nextConnect); |
| |
| for (var i:uint = 0; i < RTMP_CONN.length; i++) |
| { |
| tryNC[i] = new NetConnection(); |
| tryNC[i].objectEncoding = ObjectEncoding.AMF0; |
| tryNC[i].client = new NCManagerConnectClient(tryNC[i], this, i); |
| tryNC[i].addEventListener(NetStatusEvent.NET_STATUS, connectOnStatus); |
| } |
| |
| nextConnect(null); |
| return false; |
| } |
| |
| /** |
| * @private |
| * <p>Does work of trying to open rtmp connections. Called either |
| * by <code>connectRTMP</code> or <code>Timer</code> set up in |
| * that method.</p> |
| * |
| * <p>For creating rtmp connections.</p> |
| * |
| * @see #connectRTMP() |
| */ |
| private function nextConnect(event:Event):void |
| { |
| tryNCTimer.reset(); |
| |
| var tempProtocol:String; |
| var port:String; |
| if (connTypeCounter == 0) |
| { |
| tempProtocol = protocol; |
| if (portNumber) |
| port = portNumber; |
| else |
| for (var i:Number = 0; i < RTMP_CONN.length; i++) |
| if (protocol == RTMP_CONN[i].protocol) |
| { |
| port = RTMP_CONN[i].port; |
| break; |
| } |
| } |
| else |
| { |
| tempProtocol = RTMP_CONN[connTypeCounter].protocol; |
| port = RTMP_CONN[connTypeCounter].port; |
| } |
| var connectionURL:String = tempProtocol + ((!serverName) ? "" : |
| "/" + serverName + ":" + port + "/") + ((!wrappedURL) ? "" : |
| wrappedURL + "/") + appName; |
| |
| tryNC[connTypeCounter].client.pending = true; |
| tryNC[connTypeCounter].connect(connectionURL, autoSenseBW); |
| |
| if (connTypeCounter < RTMP_CONN.length - 1) |
| { |
| connTypeCounter++; |
| tryNCTimer.start(); |
| } |
| } |
| |
| /** |
| * @private |
| * <p>Stops all timers, closes all unneeded connections, and other |
| * cleanup related to the <code>connectRTMP</code> strategy of |
| * pipelining connection attempts to different protocols and |
| * ports.</p> |
| * |
| * <p>For creating rtmp connections.</p> |
| * |
| * @see #connectRTMP() |
| */ |
| private function cleanConns():void |
| { |
| if (tryNCTimer != null) |
| { |
| tryNCTimer.reset(); |
| tryNCTimer = null; |
| } |
| |
| if (tryNC) |
| { |
| for (var i:uint = 0; i < tryNC.length; i++) |
| { |
| if (tryNC[i]) |
| { |
| tryNC[i].removeEventListener(NetStatusEvent.NET_STATUS, connectOnStatus); |
| if (tryNC[i].client.pending) |
| tryNC[i].addEventListener(NetStatusEvent.NET_STATUS, disconnectOnStatus); |
| else |
| tryNC[i].close(); |
| } |
| tryNC[i] = null; |
| } |
| tryNC = null; |
| } |
| } |
| |
| /** |
| * @private |
| * <p>Starts another pipelined connection attempt with |
| * <code>connectRTMP</code> with the fallback server.</p> |
| * |
| * <p>For creating rtmp connections.</p> |
| * |
| * @see #connectRTMP() |
| */ |
| private function tryFallBack():void |
| { |
| if (serverName == fallbackServerName || !fallbackServerName) |
| { |
| //it's not connected |
| _netConnection = null; |
| ncConnected = false; |
| owner.ncConnected(); |
| } |
| else |
| { |
| connTypeCounter = 0; |
| cleanConns(); |
| serverName = fallbackServerName; |
| connectRTMP(); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Callbacks and event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * dispatches reconnect event, called by |
| * <code>NetConnection.onBWDone</code> |
| */ |
| public function onReconnected():void |
| { |
| _netConnection.removeEventListener(NetStatusEvent.NET_STATUS, connectOnStatus); |
| ncConnected = true; |
| owner.ncReconnected(); |
| } |
| |
| /** |
| * @private |
| * <p>Starts another pipelined connection attempt with |
| * <code>connectRTMP</code> with the fallback server.</p> |
| * |
| * <p>For creating rtmp connections.</p> |
| * |
| * @see #connectRTMP() |
| */ |
| public function onConnected(p_nc:NetConnection, p_bw:Number):void |
| { |
| // avoid timeout |
| connectionTimer.reset(); |
| |
| // ditch these now unneeded functions and listeners |
| // Don't need to assign the client to null |
| // since it will anyways get reassigned on |
| // reconnection. |
| // p_nc.client = null; |
| p_nc.removeEventListener(NetStatusEvent.NET_STATUS, connectOnStatus); |
| |
| // store pointers to the successful connection and uri |
| _netConnection = p_nc; |
| ncURI = _netConnection.uri; |
| ncConnected = true; |
| |
| if (autoSenseBW) |
| { |
| _bitrate = p_bw * 1024; |
| |
| if (streams) |
| bitrateMatch(); |
| else |
| { |
| var sSplit:Array = _streamName.split(","); |
| |
| // remove leading and trailing whitespace from string |
| for (var i:uint = 0; i < sSplit.length; i+=2) |
| { |
| var sName:String = sSplit[i].replace(/^\s*(\S.*\S)\s*$/, "$1"); |
| if (i + 1 < sSplit.length) |
| { |
| // If we have less bw than the next threshold or if |
| // there isn't another threshold (last string) |
| if (p_bw <= Number(sSplit[i+1])) |
| { |
| _streamName = sName; |
| break; |
| } |
| } |
| else |
| { |
| _streamName = sName; |
| break; |
| } |
| } // for |
| // strip off .flv and .flv2 if included |
| if (_streamName.slice(-5).toLowerCase() == ".flv2") |
| _streamName = _streamName.slice(0, -5); |
| else if (_streamName.slice(-4).toLowerCase() == ".flv") |
| _streamName = _streamName.slice(0, -4); |
| } |
| } |
| |
| |
| // if we need to get the stream length from the server, do it here |
| // Donot call getStreamLength when main.asc is not present |
| // since getStreamLength is defined in main.asc |
| if ((!owner.isLive && _streamLength == -1) && owner.autoBandWidthDetection) |
| { |
| var res:Responder = new Responder(getStreamLengthResult, |
| getStreamLengthStatus); |
| _netConnection.call("getStreamLength", res, _streamName); |
| } |
| else |
| owner.ncConnected(); |
| } |
| |
| /** |
| * @private |
| * netStatus event listener when connecting |
| */ |
| public function connectOnStatus(event:NetStatusEvent):void |
| { |
| var stuff:Object; |
| var target:NetConnection = NetConnection(event.target); |
| |
| target.client.pending = false; |
| |
| if (event.info.code == "NetConnection.Connect.Success") |
| { |
| _netConnection = tryNC[target.client.connIndex]; |
| // Force call to onConnected when main.asc is not present |
| // which would call this function through onBWDone |
| if (!owner.autoBandWidthDetection) |
| onConnected(_netConnection, 0); |
| tryNC[target.client.connIndex] = null; |
| cleanConns(); |
| } |
| else if (((event.info.code == "NetConnection.Connect.Failed") |
| || (event.info.code == "NetConnection.Connect.Rejected")) |
| && (target.client.connIndex == (RTMP_CONN.length - 1))) |
| // Try rearranging the app URL, then the fallbackServer |
| if (!connectAgain()) |
| tryFallBack(); |
| } |
| |
| /** |
| * @private |
| * netStatus event listener when reconnecting |
| */ |
| public function reconnectOnStatus(event:NetStatusEvent):void |
| { |
| if ((event.info.code == "NetConnection.Connect.Failed") |
| || (event.info.code == "NetConnection.Connect.Rejected")) |
| { |
| // Try the fallbackServer |
| _netConnection = null; |
| ncConnected = false; |
| owner.ncReconnected(); |
| } |
| } |
| |
| /** |
| * @private |
| * netStatus event listener for disconnecting extra |
| * NetConnections that were opened in parallel |
| */ |
| public function disconnectOnStatus(event:NetStatusEvent):void |
| { |
| if (event.info.code == "NetConnection.Connect.Success") |
| { |
| event.target.removeEventListener(NetStatusEvent.NET_STATUS, |
| disconnectOnStatus); |
| NetConnection(event.target).close(); |
| } |
| } |
| |
| /** |
| * @private |
| * Responder function to receive streamLength result from |
| * server after making rpc |
| */ |
| private function getStreamLengthResult(length:Number):void |
| { |
| if (length > 0) |
| _streamLength = length; |
| owner.ncConnected(); |
| } |
| |
| /** |
| * @private |
| * Responder function to receive status messages for rpc to |
| * get streamLength |
| */ |
| private function getStreamLengthStatus(item:Object):void |
| { |
| } |
| |
| /** |
| * @private |
| * <p>Called by timer to timeout all connection attempts.</p> |
| * |
| * <p>For creating rtmp connections.</p> |
| * |
| * @see #connectRTMP() |
| */ |
| private function onFCSConnectTimeOut(event:Event):void |
| { |
| cleanConns(); |
| _netConnection = null; |
| ncConnected = false; |
| if (!connectAgain()) |
| owner.ncConnected(); |
| } |
| |
| /** |
| * @private |
| * <p>Compares connection info with previous NetConnection, |
| * will reuse existing connection if possible. |
| */ |
| private function canReuseOldConnection(parseResults:Object):Boolean |
| { |
| // no reuse if no prior connection |
| if (_netConnection == null || !ncConnected) |
| return false; |
| |
| // http connection |
| if (!parseResults.isRTMP) |
| { |
| // can reuse if prev connection was http |
| if (!_isRTMP) |
| return true; |
| // cannot reuse if was rtmp--close |
| owner.close(); |
| _netConnection = null; |
| ncConnected = false; |
| initNCInfo(); |
| return false; |
| } |
| |
| // rtmp connection |
| if (_isRTMP) |
| { |
| if (parseResults.serverName == serverName && parseResults.appName == appName && |
| parseResults.protocol == protocol && parseResults.portNumber == portNumber && |
| parseResults.wrappedURL == wrappedURL) |
| return true; |
| // cannot reuse this rtmp--close |
| owner.close(); |
| _netConnection = null; |
| ncConnected = false; |
| } |
| |
| initNCInfo(); |
| return false; |
| } |
| |
| } // class mx.controls.videoClasses.NCManager |
| } |