1/*
2 * jPlayer Plugin for jQuery JavaScript Library
3 * http://www.jplayer.org
4 *
5 * Copyright (c) 2009 - 2014 Happyworm Ltd
6 * Licensed under the MIT license.
7 * http://opensource.org/licenses/MIT
8 *
9 * Author: Robert M. Hall
10 * Date: 7th August 2012
11 * Custom NetConnection Manager for more robust RTMP support
12 * Based in part on work by Will Law for the old Akamai NCManager.as
13 * and some of Will's new work in the OVP base classes (Open Video Player)
14 * as well as similar approaches by many other NetConnection managers
15 *
16 */
17
18 /*
19 TODO LIST 08/18/2011:
20 1. Wired up errors to dispatch events to Jplayer events to allow them to bubble up to JS
21 2. Rework event dispatch to handoff netconnection instead of a passed in reference
22 3. Allow a customizeable list of protocols and ports to be used instead of entire list
23 4. Allow a specific port/protocol (1 connect type) to be used first, and then optionally fallback on a custom list or the default list
24 5. Remove some traces and check a few other items below where I've made notes
25 */
26
27package happyworm.jPlayer {
28
29	import flash.events.*;
30	import flash.net.*;
31
32	import flash.utils.Timer;
33	import flash.utils.getTimer;
34	import flash.utils.clearInterval;
35	import flash.utils.setInterval;
36
37	public class ConnectManager extends Object {
38
39		private var protocols_arr:Array = new Array("rtmp","rtmpt","rtmpe","rtmpte","rtmps");
40		private var ports_arr:Array = new Array("",":1935",":80",":443");
41		private const protCount:Number = 5;
42		private const portCount:Number = 4;
43
44		private var _ncRef:Object;
45
46		private var _aNC:Array;
47
48		private var k_TIMEOUT:Number = 30000;
49		private var k_startConns:Number;
50		private var m_connList:Array = [];
51		private var m_serverName:String;
52		private var m_appName:String;
53		private var m_streamName:String;
54		private var m_connListCounter:Number;
55		private var m_flashComConnectTimeOut:Number;
56		private var m_validNetConnection:NetConnection;
57
58		private var connectSuccess:Boolean=false;
59
60		private var negotiating:Boolean=false;
61		private var idleTimeOut:Boolean=false;
62
63		public function ConnectManager() {
64			trace ("ConnectManager Initialized Version: 1.00 DT");
65			createPortsProtocolsArray();
66		}
67
68		private function createPortsProtocolsArray():void {
69		var outerLoop:Number=0;
70		var innerLoop:Number=0;
71			for (outerLoop=0; outerLoop<protocols_arr.length; outerLoop++) {
72
73				for (innerLoop=0; innerLoop<ports_arr.length; innerLoop++) {
74					m_connList.push( { protocol: protocols_arr[outerLoop], port: ports_arr[innerLoop] } );
75				}
76
77			}
78		}
79
80		public function negotiateConnect(ncRef:Object,p_serverName:String,p_appName:String):void
81		{
82			negotiating=true;
83			_ncRef=ncRef;
84			trace("*** SERVER NAME: "+p_serverName);
85			trace("*** APP NAME: "+p_serverName);
86			k_startConns = getTimer();
87			m_serverName = p_serverName;
88			m_appName = p_appName;
89
90			// Set a timeout function, just in case we never connect successfully
91			clearInterval(m_flashComConnectTimeOut);
92			m_flashComConnectTimeOut = setInterval(onFlashComConnectTimeOut,k_TIMEOUT,k_TIMEOUT);
93
94			// Createe a NetConnection for each of the protocols/ports listed in the m_connList list.
95			// Connection attempts occur at intervals of 1.5 seconds.
96			// The first connection to succeed will be used, all the others will be closed.
97			_aNC = new Array();
98			for (var i:uint = 0; i < m_connList.length; i++)
99			{
100				_aNC[i] = new NetConnection();
101				_aNC[i].addEventListener(NetStatusEvent.NET_STATUS,netStatus);
102				_aNC[i].addEventListener(SecurityErrorEvent.SECURITY_ERROR,netSecurityError);
103				_aNC[i].addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncError);
104				_aNC[i].client = new Object;
105				_aNC[i].client.owner = this;
106				_aNC[i].client.connIndex = i;
107				_aNC[i].client.id = i;
108				_aNC[i].client.pending = true;
109
110				/* Revisit this chunk - not needed at the moment as NC is handed off and this
111				// is handled elsewhere
112				// Need to put in some event dispatching as a more elegant solution and leave it here
113
114				_aNC[i].client.onBWDone = function (p_bw, deltaDown, deltaTime, latency) {
115					//this.owner.dispatchEvent ({type:"ncBandWidth", kbps:p_bw, latency:latency});
116				};
117
118				_aNC[i].client.onBWCheck = function (counter) {
119					return ++counter;
120				};
121
122				_aNC[i].client.onStatus = function (info) {
123					//
124				};
125				*/
126
127			}
128			m_connListCounter = 0;
129			nextConnect ();
130		}
131
132		private function nextConnect():void
133		{
134			trace("*** Connection: "+ m_connListCounter + ": "+m_connList[m_connListCounter].protocol + "://" + m_serverName + m_connList[m_connListCounter].port + "/" + m_appName);
135
136			try {
137				_aNC[m_connListCounter].connect(m_connList[m_connListCounter].protocol + "://" + m_serverName + m_connList[m_connListCounter].port + "/" + m_appName);
138
139			} catch (error:Error) {
140				// statements
141				trace("*** Caught an error condition: "+error);
142				m_connListCounter = m_connList.length+1;
143			}
144			// statements
145				clearInterval(_aNC["ncInt" + m_connListCounter]);
146
147			if ((m_connListCounter < m_connList.length - 1))
148			{
149				m_connListCounter++;
150				_aNC["ncInt" + m_connListCounter] = setInterval(nextConnect,1500);
151			}
152
153		}
154
155		// Cleans up all connections if none have succeeded by the timeout interval
156		private function onFlashComConnectTimeOut(timeout:Number):void
157		{
158			stopAll();
159		}
160
161		private function handleGoodConnect(_nc:NetConnection):void {
162			negotiating=false;
163			trace("Handing OFF NetConnection");
164			clearInterval(m_flashComConnectTimeOut);
165			_ncRef.connectStream();
166			_ncRef.onBWDone(null,_nc);
167			//dispatchEvent(event);
168			// Need to enable and pass to Jplayer event system- revisit
169			// right now handing back a hardcoded reference that is passed in
170			// Should come up with a more loosely coupled way via event dispatch
171
172		}
173
174		public function getNegotiating():Boolean {
175			return negotiating;
176		}
177
178		public function setNegotiating(bool:Boolean):void {
179			negotiating=bool;
180		}
181
182
183		public function stopAll(bool:Boolean=false):void {
184
185			//this.dispatchEvent ({type:"ncFailedToConnect", timeout:timeout});
186			// Need to enable and pass to Jplayer event system- revisit
187			// trace(_aNC+":"+m_flashComConnectTimeOut+":"+m_connList.length)
188			if(_aNC!=null && !isNaN(m_flashComConnectTimeOut) ) {
189				clearInterval(m_flashComConnectTimeOut);
190			for (var i:uint = 0; i < m_connList.length; i++)
191			{
192				if (_aNC[i]!=null)
193				{
194					clearInterval(_aNC["ncInt" + i]);
195					_aNC[i].close();
196					if(bool==false) {
197						_aNC[i].client = null;
198					}
199					_aNC[i] = null;
200					delete _aNC[i];
201				}
202			}
203			}
204
205		}
206
207
208		private function netStatus(event:NetStatusEvent):void {
209
210			trace(event.info.code);
211			if(event.info.description != undefined) {
212				trace(event.info.description);
213			}
214			_aNC[event.target.client.id].client.pending = true;
215
216				// this.owner.m_validNetConnection = this.client.owner[this.client.connIndex];
217				// if (info.description == "[ License.Limit.Exceeded ]") {
218
219				switch (event.info.code) {
220					case "NetConnection.Connect.IdleTimeOut":
221					trace("IDLE TIMEOUT OCCURRED!")
222					negotiating=true;
223					idleTimeOut=true;
224					_ncRef.shutDownNcNs();
225					break;
226				case "NetConnection.Connect.Closed":
227					if(!negotiating && !idleTimeOut) {
228						idleTimeOut = false;
229						_ncRef.doneYet();
230					}
231					break;
232				case "NetConnection.Connect.InvalidApp":
233				case "NetConnection.Connect.Rejected":
234					//handleRejectedOrInvalid(event)
235    				break;
236				case "NetConnection.Call.Failed":
237					/*
238					if (event.info.description.indexOf("_checkbw") != -1) {
239						event.target.expectBWDone = true;
240						event.target.call("checkBandwidth",null);
241					}
242					*/
243					break;
244					case "NetConnection.Connect.Success":
245						var i:uint=0;
246						for ( i = 0; i<_aNC.length; i++) {
247						if (_aNC[i] && (i != event.target.client.id)) {
248							_aNC[i].close();
249							_aNC[i] = null;
250						}
251					}
252					var _nc:NetConnection = NetConnection(event.target);
253					var connID:Number = event.target.client.id;
254					var _actualPort:String = m_connList[m_connListCounter].port;
255					var _actualProtocol:String = m_connList[m_connListCounter].protocol;
256
257					// See if we have version info
258					var _serverVersion:String = "UNKNOWN";
259					if (event.info.data && event.info.data.version) {
260						_serverVersion = event.info.data.version;
261					}
262					trace("Connect ID: "+connID+" - PORT: "+_actualPort+" - PROTOCOL: "+_actualProtocol+" - FMS Version: "+_serverVersion);
263
264					clearInterval(_aNC["ncInt" + connID]);
265					clearInterval(_aNC["ncInt" + m_connListCounter]);
266
267					handleGoodConnect(_nc);
268					break;
269				}
270		}
271
272
273		/** Catches any netconnection net security errors
274		 * @private
275		 */
276		private function netSecurityError(event:SecurityErrorEvent):void {
277			trace("SECURITY ERROR:"+event);
278			//dispatchEvent(event);
279			// Need to enable and pass to Jplayer event system- revisit
280    	}
281
282    	/** Catches any async errors
283    	 * @private
284    	 */
285		private function asyncError(event:AsyncErrorEvent):void {
286			trace("ASYNC ERROR:"+event.error);
287			//dispatchEvent(event);
288			// Need to enable and pass to Jplayer event system- revisit
289    	}
290
291
292
293	}// class
294
295} //package
296