/***************************************************** * * Copyright 2009 Adobe Systems Incorporated. All Rights Reserved. * ***************************************************** * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * * The Initial Developer of the Original Code is Adobe Systems Incorporated. * Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems * Incorporated. All Rights Reserved. * * Contributor(s): Akamai Technologies * *****************************************************/ package org.osmf.net { import __AS3__.vec.Vector; import flash.net.NetConnection; import flash.net.NetStream; import flash.utils.Dictionary; import org.osmf.events.MediaError; import org.osmf.events.MediaErrorCodes; import org.osmf.events.MediaErrorEvent; import org.osmf.events.NetConnectionFactoryEvent; import org.osmf.media.MediaResourceBase; import org.osmf.media.MediaType; import org.osmf.media.MediaTypeUtil; import org.osmf.media.URLResource; import org.osmf.traits.LoadState; import org.osmf.traits.LoadTrait; import org.osmf.traits.LoaderBase; import org.osmf.utils.URL; /** * The NetLoader class extends LoaderBase to provide * loading support to the AudioElement and VideoElement classes. *

Supports both streaming and progressive media resources. * If the resource URL is RTMP, connects to an RTMP server by invoking a NetConnectionFactoryBase. * NetConnections may be shared between LoadTrait instances. * If the resource URL is HTTP, performs a connect(null) * for progressive downloads.

* The NetLoader supports Flash Media Token Authentication, * for passing authentication tokens through the NetConnection. * * @includeExample NetLoaderExample.as -noswf * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion OSMF 1.0 */ public class NetLoader extends LoaderBase { /** * Constructor. * * @param factory The NetConnectionFactoryBase instance to use for managing NetConnections. * If factory is null, a NetConnectionFactory will be created and used. Since the * NetConnectionFactory class facilitates connection sharing, this is an easy way of * enabling global sharing, by creating a single NetConnectionFactory instance within * the player and then handing it to all NetLoader instances. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion OSMF 1.0 */ public function NetLoader(factory:NetConnectionFactoryBase=null) { super(); netConnectionFactory = factory || new NetConnectionFactory(); netConnectionFactory.addEventListener(NetConnectionFactoryEvent.CREATION_COMPLETE, onCreationComplete); netConnectionFactory.addEventListener(NetConnectionFactoryEvent.CREATION_ERROR, onCreationError); } /** * @private * * The NetLoader returns true for URLResources which support the media and mime-types * (or file extensions) for streaming audio and streaming or progressive video, or * implement one of the following schemes: http, https, file, rtmp, rtmpt, rtmps, * rtmpe or rtmpte. * * @param resource The URL of the source media. * @return Returns true for URLResources which it can load * @inheritDoc **/ override public function canHandleResource(resource:MediaResourceBase):Boolean { var rt:int = MediaTypeUtil.checkMetadataMatchWithResource(resource, MEDIA_TYPES_SUPPORTED, MIME_TYPES_SUPPORTED); if (rt != MediaTypeUtil.METADATA_MATCH_UNKNOWN) { return rt == MediaTypeUtil.METADATA_MATCH_FOUND; } /* * The rules for URL checking is outlined as below: * * If the URL is null or empty, we assume being unable to handle the resource * If the URL has no protocol, we check for file extensions * If the URL has protocol, we have to make a distinction between progressive and stream * If the protocol is progressive (file, http, https), we check for file extension * If the protocol is stream (the rtmp family), we assume that we can handle the resource * * We assume being unable to handle the resource for conditions not mentioned above */ var res:URLResource = resource as URLResource; var extensionPattern:RegExp = new RegExp("\.flv$|\.f4v$|\.mov$|\.mp4$|\.mp4v$|\.m4v$|\.3gp$|\.3gpp2$|\.3g2$", "i"); var url:URL = res != null ? new URL(res.url) : null; if (url == null || url.rawUrl == null || url.rawUrl.length <= 0) { return false; } if (url.protocol == "") { return extensionPattern.test(url.path); } if (NetStreamUtils.isRTMPStream(url.rawUrl)) { return true; } if (url.protocol.search(/file$|http$|https$/i) != -1) { return (url.path == null || url.path.length <= 0 || url.path.indexOf(".") == -1 || extensionPattern.test(url.path)); } return false; } /** * * The factory function for creating a NetStream. * * @param connection The NetConnection to associate with the new NetStream. * @param resource The resource whose content will be played in the NetStream. * * @return A new NetStream associated with the NetConnection. **/ protected function createNetStream(connection:NetConnection, resource:URLResource):NetStream { return new NetStream(connection); } /** * The factory function for creating a NetStreamSwitchManagerBase. * * @param connection The NetConnection that's associated with the NetStreamSwitchManagerBase. * @param netStream The NetStream upon which the NetStreamSwitchManagerBase will operate. * @param dsResource The resource upon which the NetStreamSwitchManagerBase will operate. * * @return The NetStreamSwitchManagerBase for the NetStream, null if multi-bitrate switching * is not enabled for the NetStream. **/ protected function createNetStreamSwitchManager(connection:NetConnection, netStream:NetStream, dsResource:DynamicStreamingResource):NetStreamSwitchManagerBase { return null; } /** * @private * * Subclass stub that can be used to do special processing just upfront * the loader finishing loading. Also, the overriding method must * call the updateLoadTrait method at the end. * * @param loadTrait */ protected function processFinishLoading(loadTrait:NetStreamLoadTrait):void { updateLoadTrait(loadTrait, LoadState.READY); } /** * @private * * Validates the LoadTrait to verify that this class can in fact load it. Examines the protocol * associated with the LoadTrait's resource. If the protocol is HTTP, calls the startLoadingHTTP() * method. If the protocol is RTMP-based, calls the startLoadingRTMP() method. If the URL protocol is invalid, * dispatches a mediaErroEvent against the LoadTrait and updates the LoadTrait's state to LoadState.LOAD_ERROR. * * @param loadTrait LoadTrait requesting this load operation. * @see org.osmf.traits.LoadTrait * @see org.osmf.traits.LoadState * @see org.osmf.events.MediaErrorEvent * @inheritDoc **/ override protected function executeLoad(loadTrait:LoadTrait):void { updateLoadTrait(loadTrait, LoadState.LOADING); var url:URL = new URL((loadTrait.resource as URLResource).url); switch (url.protocol) { case PROTOCOL_RTMP: case PROTOCOL_RTMPS: case PROTOCOL_RTMPT: case PROTOCOL_RTMPE: case PROTOCOL_RTMPTE: startLoadingRTMP(loadTrait); break; case PROTOCOL_HTTP: case PROTOCOL_HTTPS: case PROTOCOL_FILE: case PROTOCOL_EMPTY: startLoadingHTTP(loadTrait); break; default: updateLoadTrait(loadTrait, LoadState.LOAD_ERROR); loadTrait.dispatchEvent ( new MediaErrorEvent ( MediaErrorEvent.MEDIA_ERROR , false , false , new MediaError(MediaErrorCodes.URL_SCHEME_INVALID) ) ); break; } } /** * @private * * Unloads the media after validating the unload operation against the LoadTrait. * Closes the NetStream defined within the NetStreamLoadTrait object, * as well as the NetConnection defined within the trait object. Dispatches the * loadStateChange event with every state change. * * @throws IllegalOperationError if the parameter is null. * @param loadTrait LoadTrait to be unloaded. * @see org.osmf.loaders.LoaderBase#event:loadStateChange **/ override protected function executeUnload(loadTrait:LoadTrait):void { var netLoadTrait:NetStreamLoadTrait = loadTrait as NetStreamLoadTrait; updateLoadTrait(loadTrait, LoadState.UNLOADING); netLoadTrait.netStream.close(); if (netLoadTrait.netConnectionFactory != null) { netLoadTrait.netConnectionFactory.closeNetConnection(netLoadTrait.connection); } else { netLoadTrait.connection.close(); } updateLoadTrait(loadTrait, LoadState.UNINITIALIZED); } /** * Establishes a new NetStream on the connected NetConnection and signals that loading is complete. * * @private **/ private function finishLoading(connection:NetConnection, loadTrait:LoadTrait, factory:NetConnectionFactoryBase = null):void { var netLoadTrait:NetStreamLoadTrait = loadTrait as NetStreamLoadTrait; if (netLoadTrait != null) { netLoadTrait.connection = connection; var netStream:NetStream = createNetStream(connection, netLoadTrait.resource as URLResource); netStream.client = new NetClient(); netLoadTrait.netStream = netStream; netLoadTrait.switchManager = createNetStreamSwitchManager(connection, netStream, netLoadTrait.resource as DynamicStreamingResource); netLoadTrait.netConnectionFactory = factory; processFinishLoading(loadTrait as NetStreamLoadTrait); } } /** * Initiates the process of creating a connected NetConnection * * @private */ private function startLoadingRTMP(loadTrait:LoadTrait):void { addPendingLoad(loadTrait); netConnectionFactory.create(loadTrait.resource as URLResource); } /** * Called once the NetConnectionFactoryBase has successfully created a NetConnection * * @private */ private function onCreationComplete(event:NetConnectionFactoryEvent):void { finishLoading ( event.netConnection , findAndRemovePendingLoad(event.resource) , event.currentTarget as NetConnectionFactoryBase ); } /** * Called once the NetConnectionFactoryBase has failed to create a NetConnection * TBD - error dispatched at lower level. * * @private */ private function onCreationError(event:NetConnectionFactoryEvent):void { var loadTrait:LoadTrait = findAndRemovePendingLoad(event.resource); if (loadTrait != null) { loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, event.mediaError)); updateLoadTrait(loadTrait, LoadState.LOAD_ERROR); } } /** * Initiates a HTTP connection. * * @private * */ private function startLoadingHTTP(loadTrait:LoadTrait):void { var connection:NetConnection = new NetConnection(); connection.client = new NetClient(); connection.connect(null); finishLoading(connection, loadTrait); } private function addPendingLoad(loadTrait:LoadTrait):void { // It's an edge case, but we don't want to assume that we'll never // have two LoadTraits that use the same URLResource, so we have to // maintain an Array. if (pendingLoads[loadTrait.resource] == null) { pendingLoads[loadTrait.resource] = [loadTrait]; } else { pendingLoads[loadTrait.resource].push(loadTrait); } } private function findAndRemovePendingLoad(resource:URLResource):LoadTrait { var loadTrait:LoadTrait = null; var pendingLoadsArray:Array = pendingLoads[resource]; if (pendingLoadsArray != null) { if (pendingLoadsArray.length == 1) { loadTrait = pendingLoadsArray[0] as LoadTrait; delete pendingLoads[resource]; } else { for (var i:int = 0; i < pendingLoadsArray.length; i++) { loadTrait = pendingLoadsArray[i]; if (loadTrait.resource == resource) { pendingLoadsArray.splice(i, 1); break; } } } } return loadTrait; } private var netConnectionFactory:NetConnectionFactoryBase; private var pendingLoads:Dictionary = new Dictionary(); private static const PROTOCOL_RTMP:String = "rtmp"; private static const PROTOCOL_RTMPS:String = "rtmps"; private static const PROTOCOL_RTMPT:String = "rtmpt"; private static const PROTOCOL_RTMPE:String = "rtmpe"; private static const PROTOCOL_RTMPTE:String = "rtmpte"; private static const PROTOCOL_HTTP:String = "http"; private static const PROTOCOL_HTTPS:String = "https"; private static const PROTOCOL_FILE:String = "file"; private static const PROTOCOL_EMPTY:String = ""; private static const MEDIA_TYPES_SUPPORTED:Vector. = Vector.([MediaType.VIDEO]); private static const MIME_TYPES_SUPPORTED:Vector. = Vector. ([ "video/x-flv", "video/x-f4v", "video/mp4", "video/mp4v-es", "video/x-m4v", "video/3gpp", "video/3gpp2", "video/quicktime", ]); } }