/* CASA Framework for ActionScript 3.0 Copyright (c) 2008, Contributors of CASA Framework All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the CASA Framework nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.casaframework.load { import flash.events.AsyncErrorEvent; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.NetStatusEvent; import flash.events.SecurityErrorEvent; import flash.media.Video; import flash.net.NetConnection; import flash.utils.getTimer; import org.casaframework.events.LoadEvent; import org.casaframework.events.VideoLoadEvent; import org.casaframework.load.LoadItem; import org.casaframework.math.Percent; import org.casaframework.time.EnterFrame; import org.casaframework.util.LoadUtil; import flash.net.NetStream; [Event(name="cuePoint", type="org.casaframework.events.VideoInfoEvent")] [Event(name="metaData", type="org.casaframework.events.VideoInfoEvent")] [Event(name="buffered", type="org.casaframework.events.VideoLoadEvent")] [Event(name="progress", type="org.casaframework.events.VideoLoadEvent")] /** Provides an easy and standardized way to load video files. VideoLoad also includes {@link VideoLoadEvent buffer progress information} in the progress event. @author Aaron Clinger @version 03/08/08 @example package { import flash.display.MovieClip; import org.casaframework.events.VideoLoadEvent; import org.casaframework.load.VideoLoad; public class MyExample extends MovieClip { protected var _videoLoad:VideoLoad; public function MyExample() { super(); this._videoLoad = new VideoLoad("test.flv"); this._videoLoad.pauseStart = true; this._videoLoad.addEventListener(VideoLoadEvent.PROGRESS, this._onProgress); this._videoLoad.addEventListener(VideoLoadEvent.BUFFERED, this._onBuffered); this._videoLoad.start(); this.addChild(this._videoLoad.video); } protected function _onProgress(e:VideoLoadEvent):void { trace(e.millisecondsUntilBuffered + " milliseconds until buffered"); trace(e.buffer.percentage + "% buffered"); trace(e.progress.percentage + "% loaded"); } protected function _onBuffered(e:VideoLoadEvent):void { e.target.netStream.resume(); } } } */ public class VideoLoad extends LoadItem { protected var _buffered:Boolean; protected var _connection:NetConnection; protected var _duration:Number = -1; protected var _framePulse:EnterFrame; protected var _pauseStart:Boolean; protected var _buffer:Percent = new Percent(); protected var _readyToCalculateBuffer:Boolean; protected var _millisecondsUntilBuffered:int = -1; protected var _video:Video; public function VideoLoad(request:*) { this._connection = new NetConnection(); this._connection.connect(null); super(new NetStream(this._connection), request); this._initListeners(this._loadItem); this._framePulse = EnterFrame.getInstance(); this._video = new Video(); this._video.attachNetStream(this._loadItem); } public function get duration():Number { return this._duration; } public function set duration(seconds:Number):void { this._duration = seconds; } public function get pauseStart():Boolean { return this._pauseStart; } public function set pauseStart(shouldPause:Boolean):void { this._pauseStart = shouldPause; } public function get video():Video { return this._video; } public function get connection():NetConnection { return this._connection; } public function get netStream():NetStream { return NetStream(this._loadItem); } public function get millisecondsUntilBuffered():int { return this._millisecondsUntilBuffered; } public function get buffer():Percent { return this._buffer.clone(); } public function get buffered():Boolean { return this._buffered; } /** @exclude */ override public function stop():void { this._framePulse.removeEventListener(Event.ENTER_FRAME, this._onFrameFire); super.stop(); } override protected function _initListeners(dispatcher:EventDispatcher):void { dispatcher.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this.dispatchEvent, false, 0, true); dispatcher.addEventListener(NetStatusEvent.NET_STATUS, this._netStatus, false, 0, true); dispatcher.addEventListener(IOErrorEvent.IO_ERROR, this._onLoadError, false, 0, true); this._connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.dispatchEvent, false, 0, true); this._loadItem.client = new VideoLoadNetStreamClient(this); this._dispatcher = dispatcher; } override protected function _calculateLoadProgress():void { var justBuffered:Boolean = false; var currentTime:int = getTimer(); this._Bps = LoadUtil.calculateBps(this.bytesLoaded, this._startTime, currentTime); this._progress.decimalPercentage = this.bytesLoaded / this.bytesTotal; if (!this.buffered && this.duration > 0) { if (this._readyToCalculateBuffer) { var videoLength:uint = uint(Math.max((this.duration - 2) * 1000, 0)); this._buffer = LoadUtil.calculateBufferPercent(this.bytesLoaded, this.bytesTotal, this._startTime, currentTime, videoLength); this._millisecondsUntilBuffered = LoadUtil.calculateMillisecondsUntilBuffered(this.bytesLoaded, this.bytesTotal, this._startTime, currentTime, videoLength); } else if (currentTime - this._startTime > 2000) { this._readyToCalculateBuffer = true; } } if (!this.buffered) if (this.buffer.decimalPercentage == 1 || this._progress.decimalPercentage == 1) this._buffered = justBuffered = true; if (this.buffered) { this._buffer = new Percent(1); this._millisecondsUntilBuffered = 0; } this.dispatchEvent(this._createDefinedVideoLoadEvent(VideoLoadEvent.PROGRESS)); if (justBuffered) this.dispatchEvent(this._createDefinedVideoLoadEvent(VideoLoadEvent.BUFFERED)); if (this.bytesLoaded >= this.bytesTotal && this.buffered) this._complete(); } override protected function _complete():void { super._complete(); this._framePulse.removeEventListener(Event.ENTER_FRAME, this._onFrameFire); this.dispatchEvent(this._createDefinedLoadEvent(LoadEvent.COMPLETE)); } override protected function _load():void { this._loadItem.play(this._request.url); if (this.pauseStart) this._loadItem.pause(); this._buffer = new Percent(); this._millisecondsUntilBuffered = -1; this._buffered = this._readyToCalculateBuffer = false; this._framePulse.addEventListener(Event.ENTER_FRAME, this._onFrameFire, false, 0, true); } protected function _createDefinedVideoLoadEvent(type:String):VideoLoadEvent { var vidLoadEvent:VideoLoadEvent = new VideoLoadEvent(type); vidLoadEvent.bytesLoaded = this.bytesLoaded; vidLoadEvent.bytesTotal = this.bytesTotal; vidLoadEvent.progress = this.progress; vidLoadEvent.Bps = this.Bps; vidLoadEvent.buffer = this.buffer; vidLoadEvent.millisecondsUntilBuffered = this.millisecondsUntilBuffered; return vidLoadEvent; } protected function _netStatus(e:NetStatusEvent):void { if (e.info.level == 'error') { this._onLoadError(e); } else { this.dispatchEvent(e); } } protected function _onFrameFire(e:Event):void { this._calculateLoadProgress(); } } } import org.casaframework.events.VideoInfoEvent; import org.casaframework.load.VideoLoad; class VideoLoadNetStreamClient { protected var _videoLoad:VideoLoad; public function VideoLoadNetStreamClient(sender:VideoLoad) { this._videoLoad = sender; } public function onCuePoint(infoObject:Object):void { var vidInfoEvent:VideoInfoEvent = new VideoInfoEvent(VideoInfoEvent.CUE_POINT); vidInfoEvent.infoObject = infoObject; this._videoLoad.dispatchEvent(vidInfoEvent); } public function onMetaData(infoObject:Object):void { if (this._videoLoad.duration == -1) this._videoLoad.duration = infoObject.duration; var vidInfoEvent:VideoInfoEvent = new VideoInfoEvent(VideoInfoEvent.META_DATA); vidInfoEvent.infoObject = infoObject; this._videoLoad.dispatchEvent(vidInfoEvent); } }