Robert Whitman’s ‘Passport,’ in Two States at Once – NYTimes.com

Nice article in the Times about Whitman’s latest piece.  I built and ran the video network that connected the two locations for the performance.  Scenes from each location were transmitted and shown live in the other location.

Robert Whitman’s ‘Passport,’ in Two States at Once – NYTimes.com.

Flash Media Server Sending Images

One of my students in my live web class is developing an interesting application that sends screenshots to other people. I put together some sample code to help him along and thought this would be of general interest.

Using the Flash Media Server with Remote Shared Objects this can be built. Here is a walk through of the code:

First of all, this uses the JPGEncoder class from the AS3CoreLib so you probably want to grab that and import it.

	import com.adobe.images.JPGEncoder;

This example uses a SharedObject, a NetConnection and NetStreams for sending the video as well as the screen shots. Once the NetConnection is established, the SharedObject can be setup:

        // Listener for connection
        private function connectionHandler (e:NetStatusEvent):void 
        { 
        	// If we are connected
             if (e.info.code == "NetConnection.Connect.Success") 
             {
				// Set up the shared object
				// We'll call it SimpleSO, pass in the app url and not make it persistent
				sharedObject = SharedObject.getRemote("SimpleSO",nc.uri,false);
				
				// Tell the shared object to call methods in this class if requested
				sharedObject.client = this;				
				
				// Add a listener for when shared object is changed
			   	sharedObject.addEventListener (SyncEvent.SYNC,syncEventCallBack); 

				// Connect the shared object to our netConnection
				sharedObject.connect(nc);
				
				// All of the video streaming setup
				doEverything();
				
				// Register mouseclicks, how we'll determine when to send a frame
				stage.addEventListener(MouseEvent.MOUSE_DOWN, saveFrame);
             } 
        }

Here is the method that is called when the mouse is clicked. It creates a bitmapdata object, encodes as a JPEG and sends that as a bytearray through the shared object:

        private function saveFrame(e:MouseEvent):void
        {        
        	// Save the frame to the bitmapdata object
        	var bmpd:BitmapData = new BitmapData(320,240);
        	bmpd.draw(videoIn1);
                	
        	// First encode as JPEG so it is smaller
			var jpgEncoder:JPGEncoder = new JPGEncoder(100);
			var jpgByteArray:ByteArray = jpgEncoder.encode(bmpd);
			
			// Send it via the shared object
			sharedObject.send("newBitmap",jpgByteArray);
        }

The SharedObject.send method calls the function “newBitmap” on everyone who is connected and passes in the jpgByteArray. The newBitmap function uses the Loader to uncompress the JPG and when it is done calls “gotBitmapData”:

		public function newBitmap(jpgByteArray:ByteArray):void
		{
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gotBitmapData)
			loader.loadBytes(jpgByteArray);
		}

gotBitmap data just creates a regular Bitmap and displays it:

	private function gotBitmapData(e:Event):void
	{
		var decodedBitmapData:BitmapData = Bitmap(e.target.content).bitmapData

        	// Create the bitmap image
        	var bmp:Bitmap = new Bitmap(decodedBitmapData);
        	
        	// Add it to the stage
        	bmp.x = 0;
        	bmp.y = 240;
        	addChild(bmp);			
	}

Here is the full AS3 class (it could be improved but it works):

package
{
	// Import JPEGEncoder Class from: http://code.google.com/p/as3corelib/
	import com.adobe.images.JPGEncoder;

	import flash.display.Sprite; 
	import flash.display.MovieClip; 
	import flash.events.NetStatusEvent; 
	import flash.net.NetConnection; 
	import flash.net.NetStream; 
	import flash.media.Camera; 
	import flash.media.Microphone; 
	import flash.media.Video; 
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.MouseEvent;
	import flash.utils.ByteArray;
	import flash.net.SharedObject; 
  	import flash.events.SyncEvent; 
  	import flash.events.Event;
	import flash.display.Loader;

	public class VideoCapture extends Sprite
	{
		// Shared Object for communication
   		private var sharedObject:SharedObject;

 		// Overall NetConnection for communicating with FMS
        private var nc:NetConnection; 
        
        // RTMP URL, same as directory on FMS
        private var rtmpURL:String = "rtmp://xxxx/webcam";
         
        // NetStreams for each stream 
        private var netStreamOut:NetStream; 
        private var netStreamIn1:NetStream; 
        
        // Camera
        private var camera:Camera; 
        // Microphone
        private var microphone:Microphone; 
                
        // My Video
        private var videoOut:Video; 
        
        // Video Components
        private var videoIn1:Video; 
        
        // Stream Names
        private var outStream:String; 
        private var inStream1:String; 

        public function VideoCapture() 
        { 
        	// Construct NetConnection and connect to server
             nc = new NetConnection(); 
             nc.connect(rtmpURL); 
             
             // Add a listener for connection
             nc.addEventListener (NetStatusEvent.NET_STATUS,connectionHandler); 
        } 
        
        
        // Listener for connection
        private function connectionHandler (e:NetStatusEvent):void 
        { 
        	// If we are connected
             if (e.info.code == "NetConnection.Connect.Success") 
             {

				// Set up the shared object
				// We'll call it SimpleSO, pass in the app url and not make it persistent
				sharedObject = SharedObject.getRemote("SimpleSO",nc.uri,false);
				
				// Tell the shared object to call methods in this class if requested
				sharedObject.client = this;				
				
				// Connect the shared object to our netConnection
				sharedObject.connect(nc);
				
				// All of the video streaming
				doEverything();
				
				// Register mouseclicks, how we'll determine when to send a frame
				stage.addEventListener(MouseEvent.MOUSE_DOWN, saveFrame);
             } 
        }
        
        // Gets the results from the server
        private function doEverything():void 
        { 
        	// Name of streams
			outStream="one"; 
			inStream1="one"; 
        
			// Setup the camera        
			camera = Camera.getCamera(); 

			// setup the microphone             
			microphone = Microphone.getMicrophone(); 

			// Video components
			videoOut = new Video(); 
    		videoIn1 = new Video();

			// Set positions 
			videoOut.x = 0;
			videoOut.y = 0;
			videoIn1.x = 320;
			videoIn1.y = 0;

			// Add them to the screen
			addChild(videoOut);
			addChild(videoIn1);

            // Publish our stream
            netStreamOut = new NetStream(nc); 
            netStreamOut.attachAudio(microphone); 
            netStreamOut.attachCamera(camera); 
            netStreamOut.publish(outStream, "live"); 
             
            // Attach camera to our video 
            videoOut.attachCamera(camera); 
             
            //Play incoming streamed video 
            netStreamIn1 = new NetStream(nc); 

			videoIn1.attachNetStream(netStreamIn1);

			netStreamIn1.play(inStream1);
        }
        
        private function saveFrame(e:MouseEvent):void
        {        
        	// Save the frame to the bitmapdata object
        	var bmpd:BitmapData = new BitmapData(320,240);
        	bmpd.draw(videoIn1);
                	
        	// First encode as JPEG so it is smaller
			var jpgEncoder:JPGEncoder = new JPGEncoder(100);
			var jpgByteArray:ByteArray = jpgEncoder.encode(bmpd);
			
			// Send it via the shared object
			sharedObject.send("newBitmap",jpgByteArray);
        }
        
		
		public function newBitmap(jpgByteArray:ByteArray):void
		{
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gotBitmapData)
			loader.loadBytes(jpgByteArray);
		}
		
		private function gotBitmapData(e:Event):void
		{
			var decodedBitmapData:BitmapData = Bitmap(e.target.content).bitmapData

        	// Create the bitmap image
        	var bmp:Bitmap = new Bitmap(decodedBitmapData);
        	
        	// Add it to the stage
        	bmp.x = 0;
        	bmp.y = 240;
        	addChild(bmp);			
		}
	} 
}