/***************************************************** * * 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. * *****************************************************/ package org.osmf.elements { import __AS3__.vec.Vector; import flash.events.ErrorEvent; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.SecurityErrorEvent; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import org.osmf.elements.f4mClasses.DRMAdditionalHeader; import org.osmf.elements.f4mClasses.Manifest; import org.osmf.elements.f4mClasses.ManifestParser; import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait; import org.osmf.events.MediaError; import org.osmf.events.MediaErrorEvent; import org.osmf.media.DefaultMediaFactory; import org.osmf.media.MediaElement; import org.osmf.media.MediaFactory; import org.osmf.media.MediaResourceBase; 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; /** * F4MLoader is a loader that is capable of loading F4M files. F4M files are * XML documents that adhere to the Flash Media Manifest format, and which * represent all of the information needed to load and play a media file. * * @see http://opensource.adobe.com/wiki/display/osmf/Flash%2BMedia%2BManifest%2BFile%2BFormat%2BSpecification Flash Media Manifest File Format Specification * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion OSMF 1.0 */ public class F4MLoader extends LoaderBase { /** * The MIME type for F4M files. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion OSMF 1.0 */ public static const F4M_MIME_TYPE:String = "application/f4m+xml"; /** * Constructor. * * @param factory The factory that is used to create MediaElements based on the * media specified in the manifest file. If no factory is provided, the F4MLoader * will use a DefaultMediaFactory. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion OSMF 1.0 */ public function F4MLoader(factory:MediaFactory = null) { super(); supportedMimeTypes.push(F4M_MIME_TYPE); if (factory == null) { factory = new DefaultMediaFactory(); } this.parser = new ManifestParser(); this.factory = factory; } /** * @private */ override public function canHandleResource(resource:MediaResourceBase):Boolean { var supported:int = MediaTypeUtil.checkMetadataMatchWithResource(resource, new Vector.(), supportedMimeTypes); if (supported == MediaTypeUtil.METADATA_MATCH_FOUND) { return true; } else if (resource is URLResource) { var urlResource:URLResource = URLResource(resource); var extension:String = new URL(urlResource.url).extension; return extension == F4M_EXTENSION; } else { return false; } } /** * @private */ override protected function executeLoad(loadTrait:LoadTrait):void { updateLoadTrait(loadTrait, LoadState.LOADING); var manifest:Manifest; var manifestLoader:URLLoader = new URLLoader(new URLRequest(URLResource(loadTrait.resource).url)); manifestLoader.addEventListener(Event.COMPLETE, onComplete); manifestLoader.addEventListener(IOErrorEvent.IO_ERROR, onError); manifestLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); function onError(event:ErrorEvent):void { manifestLoader.removeEventListener(Event.COMPLETE, onComplete); manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); updateLoadTrait(loadTrait, LoadState.LOAD_ERROR); loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(0, event.text))); } function onComplete(event:Event):void { manifestLoader.removeEventListener(Event.COMPLETE, onComplete); manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); try { manifest = parser.parse(event.target.data, getRootUrl(URLResource(loadTrait.resource).url)); } catch (parseError:Error) { updateLoadTrait(loadTrait, LoadState.LOAD_ERROR); loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(parseError.errorID, parseError.message))); } if (manifest != null) { var unfinishedLoads:Number = 0; // Load externals for each (var item:DRMAdditionalHeader in manifest.drmAdditionalHeaders) { // DRM Metadata - we may make this load on demand in the future. if (item.url != null) { function completionCallback(success:Boolean):void { if (success) { unfinishedLoads--; } if (unfinishedLoads == 0) { finishLoad(); } } unfinishedLoads++; loadAdditionalHeader(item, completionCallback, onError); } } if (unfinishedLoads == 0) // No external resources { finishLoad(); } } } function finishLoad():void { try { var netResource:MediaResourceBase = parser.createResource(manifest, URLResource(loadTrait.resource)); var loadedElem:MediaElement = factory.createMediaElement(netResource); if (loadedElem.hasOwnProperty("defaultDuration") && !isNaN(manifest.duration)) { loadedElem["defaultDuration"] = manifest.duration; } LoadFromDocumentLoadTrait(loadTrait).mediaElement = loadedElem; updateLoadTrait(loadTrait, LoadState.READY); } catch (error:Error) { updateLoadTrait(loadTrait, LoadState.LOAD_ERROR); loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(error.errorID, error.message))); } } } /** * @private */ override protected function executeUnload(loadTrait:LoadTrait):void { updateLoadTrait(loadTrait, LoadState.UNINITIALIZED); } private function loadAdditionalHeader(item:DRMAdditionalHeader, completionCallback:Function, onError:Function):void { var drmLoader:URLLoader = new URLLoader(); drmLoader.dataFormat = URLLoaderDataFormat.BINARY; drmLoader.addEventListener(Event.COMPLETE, onDRMLoadComplete); drmLoader.addEventListener(IOErrorEvent.IO_ERROR, onError); drmLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); function onDRMLoadComplete(event:Event):void { event.target.removeEventListener(Event.COMPLETE, onDRMLoadComplete); event.target.removeEventListener(IOErrorEvent.IO_ERROR, onError); event.target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); item.data = URLLoader(event.target).data; completionCallback(true); } drmLoader.load(new URLRequest(item.url)); } private function getRootUrl(url:String):String { var path:String = url.substr(0, url.lastIndexOf("/")); return path; } private static const F4M_EXTENSION:String = "f4m"; private var supportedMimeTypes:Vector. = new Vector.(); private var factory:MediaFactory; private var parser:ManifestParser; } }