topical media & game development

talk show tell print

mashup-rmx-10-VideoBitmapDisplay.ax

mashup-rmx-10-VideoBitmapDisplay.ax (swf ) [ flash ] flex


  package com.almerblank.flex.components.video
  {
          
          import flash.display.Bitmap;
          import flash.display.BitmapData;
          import flash.events.AsyncErrorEvent;
          import flash.events.Event;
          import flash.events.NetStatusEvent;
          import flash.events.ProgressEvent;
          import flash.events.SecurityErrorEvent;
          import flash.events.TimerEvent;
          import flash.geom.Rectangle;
          import flash.media.SoundTransform;
          import flash.media.Video;
          import flash.net.NetConnection;
          import flash.net.NetStream;
          import flash.net.ObjectEncoding;
          import flash.utils.Timer;
          
          import mx.containers.Canvas;
          import mx.controls.Image;
          import mx.events.FlexEvent;
          import mx.events.VideoEvent;
          import mx.utils.ObjectUtil;
          
          
The VideoBitmapDisplay class works almost identically to the VideoDisplay class to display video. It provides extra functionality and extendability that the VideoDisplay class does not.
author: Omar Gonzalez

   
           [Bindable]
          public class VideoBitmapDisplay extends Canvas
          {
                  
                  // VideoBitmapDisplay properties
                  private var _videoHeight:int = 240;
                  private var _videoWidth:int = 320;
                  private var _playing:Boolean = false;
                  private var _maintainAspectRatio:Boolean = false;
                  private var _scaleContent:Boolean = true;
                  private var _smoothing:Boolean = true;
                  private var _volume:Number = .75;
                  private var _playheadUpdateInterval:int = 50;
                  private var _source:String = null;
                  private var _autoPlay:Boolean = true;
                  private var _autoRewind:Boolean = true;
                  private var _autoBandwidthDetection:Boolean = false;
                  private var _bufferTime:Number = 3;
                  private var _idleTimeout:Number = 300000;
                  private var _cuePointManagerClass:Class = null;
                  private var _cuePoints:Array = null;
                  private var _live:Boolean = false;
                  private var _playheadTime:Number;
                  private var _progressInterval:int = 250;
                  private var _totalTime:Number;
                  private var _backgroundColor:Number = 0x000000;
                  private var _clearEndScreen:Boolean = true;
                  private var _playheadManager:Timer;
                  private var _progressManager:Timer;
                  private var _bytesLoaded:Number;
                  private var _bytesTotal:Number;
                  private var _bufferEmpty:Boolean = true;
                  private var _downloadComplete:Boolean = false;
                  private var _sourceChanged:Boolean = false;
                  
                  
                  // video objects
                  private var _netConn:NetConnection = null;
                  private var _connected:Boolean = false;
                  private var _videoStream:NetStream = null;
                  private var _video:Video;
                  private var _sound:SoundTransform;
                  private var _refreshManager:Timer;
                  private var _streamName:String;
                  
                  // display objects
                  private var _display:Image;
                  public var bitmapData:BitmapData;
                  private var _videoBitmap:Bitmap;
                  
                  
Class constructor.

                  
                  public function VideoBitmapDisplay()
                  {
                          super();
                          super.addEventListener ( FlexEvent.CREATION_COMPLETE, init );
                  }
                  
Retrieves the totalTime from the metadata in the video when it is received.
parameter: info

  
                  public function onMetaData(info:Object):void
                  {
                      //trace("metadata: duration=" + info.duration + " width=" + info.width + " height=" + info.height + " framerate=" + info.framerate);
                      totalTime = Number ( info.duration );
                  }
                  
Handles cuePoint info from the video.
parameter: info

                  
                  public function onCuePoint(info:Object):void 
                  {
                      trace("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
                  }
                  
Handles AsyncErrorEvent to prevent rte.
parameter: e

                  
                  private function asyncErrorHandler(e:AsyncErrorEvent):void
                  {
                          return;
                  }
         
The <code>connStatus()</code> method handles status events dispatched by the NetConnection object.
parameter: event

                  
         private function connStatus (event:NetStatusEvent):void
         {
                          
                          switch ( event.info.code )
                          {
                                  case 'NetConnection.Connect.Success' :
                                          _connected = true;
                                          if (!live )
                                          {
                                                  openStream();
                                          }
                                          else if ( live )
                                          {
                                                  subscribe(); // this custom method is used to subscribe to the FMS application.
                                          }
                                          break;
                                  
                                  case 'NetConnection.Connect.Failed' :
                                  case 'NetConnection.Connect.Rejected' :
                                          openConnection();
                                          break;
                                          
                                  case 'NetConnection.Connect.Closed' :
                                          _connected = false;
                                  default : break ;
                          }
          }
          
This method is used to subscribe to a specific CDN's FMS server. This could/should change for your specific implementations of live video. The <code>onFCSSubscribe()</code> method is also an expected callback of this CDN to complete the connection.

          
          private function subscribe():void
          {
                  //var res:Responder = new Responder( onFCSubscribe );
                  _netConn.call( "FCSubscribe", null, _streamName );
          }
          
Callback method called by the server on the client of the NetConnection object. This method is specific to the CDN this class was implemented for. You could change this for your specific implementation.
parameter: info

          
          public function onFCSubscribe ( info:Object ):void
          {
                  openStream();
          }
  
          private function securityErrorHandler(event:SecurityErrorEvent):void
          {
              trace("securityErrorHandler: " + event);
          }
          
                  
This method opens the NetConnection object, has some specific logic for connection to FMS2, which uses AMF0 to communicate.

                  
                  private function openConnection():void
                  {
                          _netConn = new NetConnection();
                          _netConn.addEventListener( NetStatusEvent.NET_STATUS, connStatus );
              _netConn.addEventListener( SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler );
              
              if ( !live )
                          {
                                  _netConn.connect( null );
                          }
                          else if ( live )
                          {
                                  _netConn.client = this;
                                  _netConn.objectEncoding = ObjectEncoding.AMF0; // used to connect to FMS2
                                  _netConn.connect( source );
                          }
                  }
                  
This method opens the NetStream object, and sets up the sound and video display. It also autoplays the video, if autoPlay is true.

                  
                  private function openStream():void
                  {
                          _videoStream = new NetStream( _netConn );
                          _videoStream.client = this;
                          _videoStream.bufferTime = _bufferTime;
                          
                          _videoStream.addEventListener( NetStatusEvent.NET_STATUS, streamStatus );
                          _videoStream.addEventListener( AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler );
                          
                          _video = new Video( _videoWidth, _videoHeight );
                          _video.smoothing = _smoothing;
                          _video.width = _videoWidth;
                          _video.height = _videoHeight;
                          
                          _video.attachNetStream( _videoStream );
                                                  
                          updateSound();
                          
                          createVideoDisplay();
                          
                          // dispatch video ready event.
                          var e:VideoEvent = new VideoEvent ( VideoEvent.READY, true );
                          dispatchEvent( e );
                          
                          if ( _autoPlay )
                          {
                                  play();
                          }
                  }
                  
Updates the sound on the video stream.

                  
                  private function updateSound():void
                  {
                          var s:SoundTransform = new SoundTransform();
                          s.volume = _volume;
                          _videoStream.soundTransform = s;
                  }
                  
Creates the video display, using an Image object.

                  
                  private function createVideoDisplay():void
                  {
                          _display = new Image();
                          _display.width = _videoWidth;
                          _display.height = _videoHeight;
                          _display.setStyle('backgroundColor', 0x000000);
                          _display.setStyle('backgroundAlpha', 1);
                          
  
                          // Create the BitmapData
                          bitmapData = new BitmapData( _videoWidth, _videoHeight, false, 0x000000 );
                          
                          // Create the Bitmap display object
                          _videoBitmap = new Bitmap( bitmapData );
                          _videoBitmap.width = _videoWidth;
                          _videoBitmap.height = _videoHeight;
                          
                          // attach bitmap to image display
                          _display.source = _videoBitmap;
                          
                          // add to stage
                          addChild ( _display );
                  }
                  
Handles the creationComplete event for this component.
parameter: event

                  
                  private function init ( event:FlexEvent ):void
                  {
                          
                          setStyle('backgroundColor', backgroundColor);
                          
                          _playheadManager = new Timer ( _playheadUpdateInterval );
                          _playheadManager.addEventListener( TimerEvent.TIMER, updatePlayhead );
                          
                          _progressManager = new Timer ( _progressInterval );
                          _progressManager.addEventListener( TimerEvent.TIMER, updateProgress );
                                                  
                          openConnection();
  
                  }
                  
Updates the playback progress and dispatches a ProgressEvent.PROGRESS event.
parameter: event

                  
                  private function updateProgress ( event:TimerEvent ):void
                  {
                          _bytesLoaded = _videoStream.bytesLoaded;
                          _bytesTotal = _videoStream.bytesTotal;
                          
                          var e:ProgressEvent = new ProgressEvent ( ProgressEvent.PROGRESS, true );
                          e.bytesLoaded = _videoStream.bytesLoaded;
                          e.bytesTotal = _videoStream.bytesTotal;
                          dispatchEvent( e );
                          
                          if ( e.bytesLoaded == e.bytesTotal )
                          {
                                  _progressManager.stop();
                                  _downloadComplete = true;
                          }        
                          
                          
                  }
                  
Updates the _playheadTime and dispatches a VideoEvent.PLAYHEAD_UPDATE event.
parameter: event

                  
                  private function updatePlayhead ( event:TimerEvent ):void
                  {
                          //trace('updating playhead...' );
                          
                          _playheadTime = _videoStream.time;
                          
                          var e:VideoEvent = new VideoEvent ( VideoEvent.PLAYHEAD_UPDATE, true );
                          e.playheadTime = _videoStream.time;
                          dispatchEvent( e ); 
                  }
                  
Handles the stream status events. Custom logic for end of stream can be added in the NetStream.Buffer.Empty switch case.
parameter: event

  
                  private function streamStatus (event:NetStatusEvent):void
                  {
                          
                          switch ( event.info.code )
                          {
                                  
                                  case 'NetStream.Play.Start' :
                                          _refreshManager = new Timer ( _playheadUpdateInterval );
                                          _refreshManager.addEventListener( TimerEvent.TIMER, updateDisplay );
                                          _refreshManager.start();
                                          _playing = true;
                                          break;
                                          
                                  case 'NetStream.Buffer.Full' :
                                          _bufferEmpty = false;
                                          break;
  
                                  case 'NetStream.Buffer.Empty' :
                                          _bufferEmpty = true;
                                          
                                          
                                          if ( _videoStream.bufferLength < .1 && ((Math.floor( _totalTime ) - Math.floor( _playheadTime )) <= 1) )
                                          {
                                                  //trace("video ended...");
                                                  
                                                  playing = false;
                                                  
                                                  if ( _autoRewind )
                                                          _videoStream.seek( 0 );
  
                                                  if ( _clearEndScreen )
                                                          clearVideo();
                                                          
                                                  // dispatch videoComplete event.
                                                  var e:VideoEvent = new VideoEvent ( VideoEvent.COMPLETE, true );
                                                  e.playheadTime = _videoStream.time;
                                                  dispatchEvent( e );
  
                                          }
                                          
                                          break;
                                          
                                  case 'NetStream.Buffer.Flush' :
                                          
                                          break;
                                  
                                  default : break ;
                                  
                          }
                          
                  }
                  
Updates the display using the BitmapData.draw() method.
parameter: e

                  
                  private function updateDisplay( e:Event ):void
                  {
                          if ( _video && _playing )
                          {
                                  _video.attachNetStream( null );
                                  bitmapData.draw( _video );
                                  _video.attachNetStream( _videoStream );
                          }
                  }
                  
                  
                  // PUBLIC METHODS
                  
Stops playback of video.

                  
                  public function stop():void
                  {
                          playing = false;
                          _videoStream.seek( 0 );
                  }
                  
Closes the video stream, stopping the download if not complete.

                  
                  public function close():void
                  {
                          playing = false;
                          if (_videoStream)
                          {
                                  _videoStream.close();
                          }
                  }
                  
Pauses playback of video.

                  
                  public function pause():void
                  {
                          playing = false;
                  }
                  
Plays the video loaded.

                  
                  public function play():void
                  {
                          if ( _videoStream )
                          {
                                  if ( _videoStream.time > 0 && !_bufferEmpty )
                                  {
                                          playing = true;
                                  }
                                  else if ( _videoStream.time == 0 )
                                  {
                                          if ( _sourceChanged )
                                          {
                                                  if ( !live )
                                                  {
                                                          _videoStream.play( _source );
                                                          _playheadManager.start();
                                                          _progressManager.start();
                                                          _sourceChanged = false;
                                                  }
                                                  else if ( live )
                                                  {
                                                          trace( ' source changed for live ' );
                                                          _videoStream.play( _streamName );
                                                          _playheadManager.start();
                                                          _progressManager.start();
                                                          _sourceChanged = false;
                                                  }
                                          }
                                          else if ( _downloadComplete && !live )
                                          {
                                                  playing = true;
                                          }
                  
                                  }
                          }
                  }
                  
Clears the video left at the end of the video playback with a black screen.

                  
                  public function clearVideo():void
                  {
                          trace('clearing video...');
                          var rect:Rectangle = new Rectangle ( 0, 0, _videoWidth, _videoHeight );
                          bitmapData.fillRect( rect, 0x00000000 );
                  }
                  
                  // GETTERS / SETTERS
                  
The playing property can be used to toggle playback. It starts and stops the bitmap drawing to the screen when the video is not playing.
parameter: isPlaying

                  
                  public function set playing ( isPlaying:Boolean ):void
                  {
                          _playing = isPlaying;
                          
                          if ( _playing && _videoStream )
                          {
                                  _videoStream.resume();
                                  _playheadManager.start();
                          }
                          else if ( !_playing && _videoStream )
                          {
                                  _videoStream.pause();
                                  _playheadManager.stop();
                          }
                          
                          //isPlaying ? _videoStream.play() :_videoStream.pause();
                          if ( _playing  && _refreshManager ) 
                          {
                                  _refreshManager.start()
                          }
                          else if ( _refreshManager )
                          {
                                  _refreshManager.stop();
                          }
                  }
                  
                  public function get playing ():Boolean
                  {
                          return _playing;
                  }
                  
                  public function set bufferTime( time:Number ):void
                  {
                          _bufferTime = time;
                          if ( _videoStream )  
                                  _videoStream.bufferTime = time;
                  }
                  
                  public function get bufferTime():Number
                  {
                          return _bufferTime;
                  }
                  
                  
                  public function get volume():Number
                  {
                          return _volume;
                  }
                  
                  public function set volume( value:Number ):void
                  {
                          _volume = value;
                          if ( _videoStream ) 
                                  updateSound();
                  }
                  
                  public function set totalTime(time:Number):void
                  {
                          _totalTime = time;
                  }
                  
                  public function get totalTime():Number
                  {
                          return _totalTime;
                  }
                  
                  public function set backgroundColor ( color:Number ):void
                  {
                          _backgroundColor = color;
                  }
                  
                  public function get backgroundColor():Number
                  {
                          return _backgroundColor;
                  }
                  
                  public function set source ( value:String ):void
                  {
                          if ( _videoStream && playing )
                          {
                                  playing = false;
                                  _videoStream.close();
                          }
                                  
                          _source = value;
                          _sourceChanged = true;
                          if ( _videoStream )
                          {
                                  openConnection();
                          }
                          getStreamName();
                          
                          if ( _videoStream && _autoPlay )
                                  play();
                  }
                  
                  private function getStreamName():void
                  {
                          if ( source.search( "/" ) > -1 )
                          {
                                  var urlParts:Array = source.split("/");
                                  _streamName = urlParts[urlParts.length - 1]; // grabs last bit of rtmp:// url, from end of string to first "/"
  //                                trace('_streamName = ' + _streamName );
                          }
                  }
                  
                  public function get source():String
                  {
                          return _source;
                  }
                  
                  public function set autoPlay( play:Boolean ):void
                  {
                          _autoPlay = play;
                  }
                  
                  public function get autoPlay():Boolean
                  {
                          return _autoPlay;
                  }
                  
                  public function set playheadTime( time:Number ):void
                  {
                          _playheadTime = time;
                          _videoStream.seek( time );
                  }
                  
                  public function get playheadTime():Number
                  {
                          if ( _videoStream )
                          {
                                  return _videoStream.time;
                          }
                          else return 0;
                  }
                  
                  public function get bytesLoaded():Number
                  {
                          return _bytesLoaded;
                  }
                  
                  public function get bytesTotal():Number
                  {
                          return _bytesTotal;
                  }
                  
                  public function get playheadUpdateInterval():Number
                  {
                          return _playheadUpdateInterval;
                  }
                  
                  public function get progressInterval():Number
                  {
                          return _progressInterval;
                  }
                  
                  public function set playheadUpdateInterval( value:Number ):void
                  {
                          _playheadUpdateInterval = value;
                  }
                  
                  public function set progressInterval( value:Number ):void
                  {
                          _progressInterval = value;
                  }
                  
                  public function set live(on:Boolean):void
                  {
                          _live = on;
                  }
                  
                  public function get live():Boolean
                  {
                          return _live;
                  }
                  
The set width setter is overridden to update the _videoWidth property.
parameter: value

                  
                  override public function set width(value:Number):void
                  {
                          super.width = value;
                          _videoWidth = value;
                  }
                  
The set height setter is overridden to update the _videoHeight property.
parameter: value

                  
                  override public function set height(value:Number):void
                  {
                          super.height = value;
                          _videoHeight = value;
                  }
                  
                  
          }
  }
  


(C) Æliens 18/6/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.