1  /**
     2   * com.sekati.data.FLV
     3   * @version 1.2.0
     4   * @author jason m horwitz | sekati.com
     5   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     6   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     7   */
     8  
     9  import com.sekati.core.App;
    10  import com.sekati.core.CoreObject;
    11  import com.sekati.events.FramePulse;
    12  import com.sekati.utils.Delegate;
    13  
    14  /**
    15   * FLV class to be used with {@link com.sekati.ui.FLVPlayer}
    16   */
    17  class com.sekati.data.FLV extends CoreObject {
    18  
    19  	private var _this:FLV;
    20  	private var _ns:NetStream;
    21  	private var _nc:NetConnection;
    22  	private var _video:Video;
    23  	private var _videoURL:String;
    24  	private var _audio:Sound;
    25  	private var _audioContainer:MovieClip;
    26  	private var _paused:Boolean;
    27  	private var _started:Boolean;
    28  	private var _duration:Number;
    29  	private var _metadata:Object;
    30  	private var _lastSeekableTime:Number;
    31  
    32  	//event stubs
    33  	public function onProgress():Void {
    34  	}
    35  
    36  	public function onEvent():Void {
    37  	}
    38  
    39  	//constructor
    40  	public function FLV() {
    41  		super( );
    42  		_this = this;
    43  		_paused = false;
    44  		_started = false;
    45  		// fix for bufferPreloader flakiness
    46  		_duration = 1000000000;
    47  	}
    48  
    49  	/**
    50  	 * load video and begin playback
    51  	 * @param url (String) flv url
    52  	 * @param videoInstance (Video)
    53  	 * @param width (Number) video width
    54  	 * @param height (Number) video height
    55  	 * @param audioContainer (MovieClip) audio becon holder
    56  	 * @return Void
    57  	 */
    58  	public function load(url:String, videoInstance:Video, width:Number, height:Number, audioContainer:MovieClip):Void {
    59  		_videoURL = url;
    60  		_video = videoInstance;
    61  		_audioContainer = audioContainer;
    62  		//netconnection & netstream
    63  		_nc = new NetConnection( );
    64  		_nc.connect( null );
    65  		_ns = new NetStream( _nc );
    66  		//detach audio
    67  		audioContainer.attachAudio( _ns );
    68  		_audio = new Sound( audioContainer );
    69  		//init video
    70  		_video._width = width;
    71  		_video._height = height;
    72  		//_video.smoothing = true;
    73  		_video.attachVideo( _ns );
    74  		//define buffer settings - higher will make loops and seeks VERY choppy, but play smoother: default is 0.1, good avg is 4
    75  		var buffer:Number = (!App.FLV_BUFFER_TIME) ? 4 : App.FLV_BUFFER_TIME;
    76  		//trace ("App.BUFFER_TIME: " + App.BUFFER_TIME);
    77  		_ns.setBufferTime( buffer );
    78  		//define NetStream & NetConnection events triggers
    79  		_ns.onMetaData = Delegate.create( _this, ns_onMetaData );
    80  		_ns.onStatus = Delegate.create( _this, ns_onStatus );
    81  		_nc.onResult = Delegate.create( _this, nc_onResult );
    82  		_nc.onStatus = Delegate.create( _this, nc_onStatus );
    83  		//start to load movie, pause and put on first frame to see something
    84  		playVideo( );
    85  		stopVideo( );
    86  		//init onEnterFrame beacon (this should be a beacon & should clean after itself)
    87  		//audioContainer.onEnterFrame = Delegate.create (_this, _onEnterFrame);
    88  		FramePulse.getInstance( ).addFrameListener( _this );
    89  	}
    90  
    91  	//==========================================================
    92  	//beacon & cleanup
    93  	private function _onEnterFrame():Void {
    94  		//onProgress should be a generic update that gives preloadPercent, bufferPercent, currentTime
    95  		//so that the same event can update the scroller, preloader and time indicator		
    96  		onProgress( getPercent( ), getPercentTimePlayed( ) );
    97  	}
    98  
    99  	public function clean(scope:MovieClip, obj:String):Void {
   100  		//deconstructor that will clean the beacon and then remove the instance
   101  		delete _audioContainer.onEnterFrame;
   102  		_audioContainer.onEnterFrame = null;
   103  		delete scope[obj];
   104  	}
   105  
   106  	//===========================================================
   107  	// Basic controls
   108  	public function playVideo():Void {
   109  		_started = true;
   110  		_ns.play( _videoURL );
   111  	}
   112  
   113  	public function stopVideo():Void {
   114  		_ns.pause( true );
   115  		// dunno why this was here: maybe important?
   116  		//seek (0);
   117  		_started = false;
   118  		_paused = false;
   119  	}
   120  
   121  	public function pause():Void {
   122  		if (!_paused && _started) {
   123  			_paused = true;
   124  			_ns.pause( true );
   125  		}
   126  	}
   127  
   128  	public function resume():Void {
   129  		if (_paused && _started) {
   130  			_paused = false;
   131  			_ns.pause( false );
   132  		}
   133  	}
   134  
   135  	public function pauseResume():Void {
   136  		if(_paused) {
   137  			resume( );
   138  		} else {
   139  			pause( );
   140  		}
   141  	}
   142  
   143  	public function setVolume(val:Number):Void {
   144  		_audio.setVolume( val );
   145  	}
   146  
   147  	//=======================================
   148  	//seek stuff
   149  	public function seek(time:Number):Void {
   150  		_ns.seek( resolveTime( time ) );
   151  	}
   152  
   153  	public function seekToPercent(percent:Number):Void {
   154  		seek( _duration * percent / 100 );
   155  	}
   156  
   157  	public function ff():Void {
   158  		//optional step size as param
   159  		seek( getTime( ) + 2 );
   160  	}
   161  
   162  	public function rew():Void {
   163  		//optional step size as param
   164  		seek( getTime( ) - 2 );
   165  	}
   166  
   167  	private function resolveTime(time:Number):Number {
   168  		//formats time so that it fits inside the available seek scope
   169  		var maxTime:Number = (_lastSeekableTime != null) ? _lastSeekableTime : _duration;
   170  		return Math.max( Math.min( time, maxTime ), 0 );
   171  	}
   172  
   173  	//=======================================
   174  	//status tools
   175  	public function isPaused():Boolean {
   176  		return _paused;
   177  	}
   178  
   179  	public function isPlaying():Boolean {
   180  		return _started;
   181  	}
   182  
   183  	public function isStopped():Boolean {
   184  		return !_started;
   185  	}
   186  
   187  	public function getTime():Number {
   188  		return _ns.time;
   189  	}
   190  
   191  	public function getTotalTime():Number {
   192  		return _duration;
   193  	}
   194  
   195  	public function getPercentTimePlayed():Number {
   196  		return getTime( ) * 100 / _duration;
   197  	}
   198  
   199  	//========================================
   200  	//preload status tools
   201  	public function getPercent():Number {
   202  		return Math.round( _ns.bytesLoaded / _ns.bytesTotal * 100 );
   203  	}
   204  
   205  	public function getBufferPercent():Number {
   206  		var total:Number = Math.min( _duration, _ns.bufferTime );
   207  		return Math.min( Math.round( _ns.bufferLength / total * 100 ), 100 );
   208  	}
   209  
   210  	//=======================================
   211  	//Netstream & Netconnection events
   212  	private function nc_onResult(info:Object):Void {
   213  		trace( "unknown ncOnResult: " + info.code );
   214  	}
   215  
   216  	private function nc_onStatus(info:Object):Void {
   217  		trace( "unknown ncOnStatus: " + info.code );
   218  	}
   219  
   220  	private function ns_onStatus(info:Object):Void {
   221  		switch (info.code) {
   222  			case "NetStream.Buffer.Empty" :
   223  				onEvent( "bufferEmpty" );
   224  				break;
   225  			case "NetStream.Buffer.Full" :
   226  				onEvent( "bufferFull" );
   227  				break;
   228  			case "NetStream.Buffer.Flush" :
   229  				onEvent( "bufferFlush" );
   230  				break;
   231  			case "NetStream.Play.Start" :
   232  				onEvent( "start" );
   233  				break;
   234  			case "NetStream.Play.Stop" :
   235  				onEvent( "stop" );
   236  				break;
   237  			case "NetStream.Play.StreamNotFound" :
   238  				onEvent( "play_streamNotFound" );
   239  				break;
   240  			case "NetStream.Seek.InvalidTime" :
   241  				onEvent( "seek_InvalidTime" );
   242  				break;
   243  			case "NetStream.Seek.Notify" :
   244  				onEvent( "seek_notify" );
   245  				break;
   246  			default :
   247  				trace( "unrecognized onStatus value: " + info.code );
   248  		}
   249  	}
   250  
   251  	private function ns_onMetaData(metadata:Object):Void {
   252  		_duration = metadata.duration;
   253  		_lastSeekableTime = metadata.lastkeyframetimestamp;
   254  		_metadata = metadata;
   255  		//depends on which application was used to encode the FLV file
   256  		//trace("nsOnMetaData event at " + _ns.time);
   257  		/*
   258  		SORENSON - INITIAL META:
   259  		creationdate - Mon Jun 12 16:21:12 2006 
   260  		framerate - 29.9699859619141
   261  		audiocodecid - 2
   262  		audiodatarate - 64
   263  		videocodecid - 5
   264  		canSeekToEnd - false
   265  		videodatarate - 600
   266  		height - 358
   267  		width - 150
   268  		duration - 17.65
   269  		*/
   270  		//find what metadata is available now
   271  		/*
   272  		for (var i in metadata) {
   273  			trace (i + " - " + metadata[i]);
   274  		}
   275  		//trace cuepoints
   276  		for (var i in metadata.cuePoints) {
   277  			trace ("CUEPOINTS: " + i + " - " + metadata.cuePoints[i]);
   278  			for (var y in metadata.cuePoints[i]) {
   279  				trace ("  CUEPOINTS: " + y + " - " + metadata.cuePoints[i][y]);
   280  			}
   281  		}
   282  		*/
   283  	}
   284  
   285  	/**
   286  	 * calls superclasses destroy and executes its own destroy behaviors.
   287  	 * @return Void
   288  	 */
   289  	public function destroy():Void {
   290  		FramePulse.getInstance( ).removeFrameListener( _this );
   291  		super.destroy( );
   292  	}
   293  }