1/***************************************************** 2* 3* Copyright 2009 Adobe Systems Incorporated. All Rights Reserved. 4* 5***************************************************** 6* The contents of this file are subject to the Mozilla Public License 7* Version 1.1 (the "License"); you may not use this file except in 8* compliance with the License. You may obtain a copy of the License at 9* http://www.mozilla.org/MPL/ 10* 11* Software distributed under the License is distributed on an "AS IS" 12* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 13* License for the specific language governing rights and limitations 14* under the License. 15* 16* 17* The Initial Developer of the Original Code is Adobe Systems Incorporated. 18* Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems 19* Incorporated. All Rights Reserved. 20* 21* Contributor(s): Akamai Technologies 22* 23*****************************************************/ 24package org.osmf.net 25{ 26 import __AS3__.vec.Vector; 27 28 import org.osmf.utils.URL; 29 30 [ExcludeClass] 31 32 /** 33 * @private 34 * 35 * Parses a URL into properties specific to Flash Media Server. 36 * 37 * @see URL 38 * 39 * @langversion 3.0 40 * @playerversion Flash 10 41 * @playerversion AIR 1.5 42 * @productversion OSMF 1.0 43 */ 44 public class FMSURL extends URL 45 { 46 /** 47 * Set the URL this class will work with. 48 * 49 * @param url The URL this class will use to provide FMS-specific information such as app name and instance name. 50 * @param useInstance If true, then the second part of the URL path is considered the instance name, 51 * such as <code>rtmp://host/app/foo/bar/stream</code>. In this case the instance name would be 'foo' and the stream would 52 * be 'bar/stream'. 53 * If false, then the second part of the URL path is considered to be the stream name, 54 * such as <code>rtmp://host/app/foo/bar/stream</code>. In this case there is no instance name and the stream would 55 * be 'foo/bar/stream'. 56 * 57 * 58 * @langversion 3.0 59 * @playerversion Flash 10 60 * @playerversion AIR 1.5 61 * @productversion OSMF 1.0 62 */ 63 public function FMSURL(url:String, useInstance:Boolean=false) 64 { 65 super(url); 66 _useInstance = useInstance; 67 _appName = ""; 68 _instanceName = ""; 69 _streamName = ""; 70 _fileFormat = ""; 71 72 parsePath(); 73 parseQuery(); 74 } 75 76 /** 77 * Whether a named instance is being used within the URI 78 * 79 * @langversion 3.0 80 * @playerversion Flash 10 81 * @playerversion AIR 1.5 82 * @productversion OSMF 1.0 83 */ 84 public function get useInstance():Boolean 85 { 86 return _useInstance; 87 } 88 89 /** 90 * The FMS application name. 91 * 92 * @langversion 3.0 93 * @playerversion Flash 10 94 * @playerversion AIR 1.5 95 * @productversion OSMF 1.0 96 */ 97 public function get appName():String 98 { 99 return _appName; 100 } 101 102 /** 103 * The FMS instance name. 104 * 105 * @langversion 3.0 106 * @playerversion Flash 10 107 * @playerversion AIR 1.5 108 * @productversion OSMF 1.0 109 */ 110 public function get instanceName():String 111 { 112 return _instanceName; 113 } 114 115 /** 116 * The FMS stream name. 117 * 118 * @langversion 3.0 119 * @playerversion Flash 10 120 * @playerversion AIR 1.5 121 * @productversion OSMF 1.0 122 */ 123 public function get streamName():String 124 { 125 return _streamName; 126 } 127 128 /** 129 * The file format of the streaming media. Corresponds to one of the 130 * public constants defined in this class, such as MP4_STREAM, 131 * or the blank stream for flv media streams. 132 * 133 * @langversion 3.0 134 * @playerversion Flash 10 135 * @playerversion AIR 1.5 136 * @productversion OSMF 1.0 137 */ 138 public function get fileFormat():String 139 { 140 return _fileFormat; 141 } 142 143 /** 144 * The vector of edges. 145 * 146 * @see FMSHost 147 * 148 * @langversion 3.0 149 * @playerversion Flash 10 150 * @playerversion AIR 1.5 151 * @productversion OSMF 1.0 152 */ 153 public function get edges():Vector.<FMSHost> 154 { 155 return _edges; 156 } 157 158 /** 159 * The vector of origins. 160 * 161 * @see FMSHost 162 * 163 * @langversion 3.0 164 * @playerversion Flash 10 165 * @playerversion AIR 1.5 166 * @productversion OSMF 1.0 167 */ 168 public function get origins():Vector.<FMSHost> 169 { 170 return _origins; 171 } 172 173 /** 174 * Parse the path in the URL object into FMS specific properties. 175 * The path is everything after the host but before any query string parameters, with no leading or trailing slashes. 176 * <p> 177 * For example, in this URL: <code>"http://host.com:80/foo/bar/index.html?a=1&b=2"</code> 178 * path would be <code>"foo/bar/index.html"</code></p> 179 * 180 * @langversion 3.0 181 * @playerversion Flash 10 182 * @playerversion AIR 1.5 183 * @productversion OSMF 1.0 184 */ 185 private function parsePath():void 186 { 187 if ((path == null) || (path.length == 0)) 188 { 189 // Check the query string for a stream name since the path is empty 190 _streamName = getParamValue(QUERY_STRING_STREAM); 191 // Check the query string for stream type since the path is empty 192 _fileFormat = getParamValue(QUERY_STRING_STREAMTYPE); 193 return; 194 } 195 196 var pattern:RegExp = /(\/)/; 197 var result:Array = path.split(pattern); 198 199 if (result != null) 200 { 201 _appName = result[APPNAME_START_INDEX]; 202 _instanceName = ""; 203 _streamName = ""; 204 205 // If "_definst_" is in the path and in the right place, we'll assume everything after that is the stream 206 var definstPattern:RegExp = new RegExp("^.*\/" + DEFAULT_INSTANCE_NAME, "i"); 207 208 if (path.search(definstPattern) > -1) 209 { 210 _useInstance = true; 211 } 212 213 var streamStartNdx:uint = STREAMNAME_START_INDEX; 214 215 if (_useInstance) 216 { 217 _instanceName = result[INSTANCENAME_START_INDEX]; 218 } 219 else 220 { 221 streamStartNdx = INSTANCENAME_START_INDEX; 222 } 223 224 for (var i:int = streamStartNdx; i < result.length; i++) 225 { 226 _streamName += result[i]; 227 } 228 229 // If no streamName found in the path, check the query string 230 if (_streamName == null || _streamName == "") 231 { 232 _streamName = getParamValue(QUERY_STRING_STREAM); 233 } 234 235 if (_streamName.search(/^mp4:/i) > -1) 236 { 237 _fileFormat = MP4_STREAM; 238 } 239 else if (_streamName.search(/^mp3:/i) > -1) 240 { 241 _fileFormat = MP3_STREAM; 242 } 243 else if (_streamName.search(/^id3:/i) > -1) 244 { 245 _fileFormat = ID3_STREAM; 246 } 247 248 // If no stream type found check the query string 249 if (_fileFormat == null || _fileFormat == "") 250 { 251 _fileFormat = getParamValue(QUERY_STRING_STREAMTYPE); 252 } 253 } 254 } 255 256 /** 257 * Parse the query string for origin/edge info. 258 * A sample FMS URI with origin/edge info in the query string might look like this: 259 * "rtmp://edge1/?rtmp://edge2/?rtmp://origin/app/inst/mp4:foldera/folder/b/myfile.mp4" 260 * 261 * @langversion 3.0 262 * @playerversion Flash 10 263 * @playerversion AIR 1.5 264 * @productversion OSMF 1.0 265 */ 266 private function parseQuery():void 267 { 268 // If there is no query string or there are no protocols in the query string, there is nothing to do 269 if (query == null || query.length == 0 || (query.search(/:\//) == -1)) 270 { 271 return; 272 } 273 274 var edgeOriginURIs:Array = query.split("?"); 275 276 // Remove the items that don't have a protocol 277 for (var ndx:int = 0; ndx < edgeOriginURIs.length; ndx++) 278 { 279 var tempIndex:int = edgeOriginURIs[ndx].toString().search(/:\//); 280 if (tempIndex == -1) 281 { 282 edgeOriginURIs.splice(ndx, 1); 283 } 284 } 285 286 var hasEdge:Boolean = false; 287 var originIndex:int = 0; 288 289 // if it splits into more than one item, we assume it has an edge and the last one is the origin 290 if (edgeOriginURIs.length >= 2) { 291 hasEdge = true; 292 originIndex = edgeOriginURIs.length -1; 293 } 294 295 var tempSN:String = ""; // temporary server name 296 var tempPN:String = ""; // temporary port number 297 var colonIndex:int = 0; 298 var slashIndex:int = 0; 299 var startIndex:int = 0; 300 var endIndex:int = 0; 301 302 for (var i:int = 0; i < edgeOriginURIs.length; i++) 303 { 304 305 var tempNdex:int = edgeOriginURIs[i].toString().search(/:\//); 306 307 startIndex = tempNdex + 2; 308 309 if (edgeOriginURIs[i].charAt(startIndex) == '/') 310 { 311 // if not local URI (i.e. rtmp:/app/) then move index up 312 startIndex++; 313 } 314 315 // get server (and maybe port) 316 colonIndex = edgeOriginURIs[i].indexOf(":", startIndex); 317 slashIndex = edgeOriginURIs[i].indexOf("/", startIndex); 318 319 if (slashIndex < 0 && colonIndex < 0) 320 { 321 tempSN = edgeOriginURIs[i].slice(startIndex); 322 } 323 else if (colonIndex >= 0 && colonIndex < slashIndex) 324 { 325 endIndex = colonIndex; 326 tempSN = edgeOriginURIs[i].slice(startIndex, endIndex); 327 startIndex = endIndex + 1; 328 endIndex = slashIndex; 329 tempPN = edgeOriginURIs[i].slice(startIndex, endIndex); 330 } 331 else if (edgeOriginURIs[i].indexOf("://") != -1) 332 { 333 endIndex = slashIndex; 334 tempSN = edgeOriginURIs[i].slice(startIndex, endIndex); 335 } 336 else 337 { 338 endIndex = edgeOriginURIs[i].indexOf("/"); 339 tempSN = "localhost"; 340 } 341 342 // if it's the origin, we need to push the origin and get the app and stream name 343 if (i == originIndex) 344 { 345 if (_origins == null) 346 { 347 _origins = new Vector.<FMSHost>; 348 } 349 _origins.push(new FMSHost(tempSN, tempPN)); 350 351 var tempFMSURL:FMSURL = new FMSURL(edgeOriginURIs[i], _useInstance); 352 353 if (_appName == "") 354 { 355 _appName = tempFMSURL.appName; 356 } 357 358 if (_useInstance && _instanceName == "") 359 { 360 _instanceName = tempFMSURL.instanceName; 361 } 362 363 if (_streamName == "") 364 { 365 _streamName = tempFMSURL.streamName; 366 } 367 } 368 else if((edgeOriginURIs[i] != query) && hasEdge) 369 { 370 if (_edges == null) 371 { 372 _edges = new Vector.<FMSHost>; 373 } 374 _edges.push(new FMSHost(tempSN, tempPN)); 375 } 376 } 377 } 378 379 private var _useInstance:Boolean; 380 private var _appName:String; 381 private var _instanceName:String; 382 private var _streamName:String; 383 private var _fileFormat:String; 384 private var _origins:Vector.<FMSHost>; 385 private var _edges:Vector.<FMSHost>; 386 387 private static const APPNAME_START_INDEX:uint = 0; 388 private static const INSTANCENAME_START_INDEX:uint = 2; 389 private static const STREAMNAME_START_INDEX:uint = 4; 390 391 private static const DEFAULT_INSTANCE_NAME:String = "_definst_"; 392 393 public static const MP4_STREAM:String = "mp4"; 394 public static const MP3_STREAM:String = "mp3"; 395 public static const ID3_STREAM:String = "id3"; 396 397 public static const QUERY_STRING_STREAM:String = "streamName"; 398 public static const QUERY_STRING_STREAMTYPE:String = "streamType"; 399 } 400} 401