AnDevCon – Developing Media Applications

Just finished a day long workshop in San Francisco at Andevcon. It went well and hopefully the participants found it useful.

Here are the slides and examples:
Camera via Intent
Custom Camera
Custom Camera with Parameters
Custom Audio Player
Audio Recorder
Video Capture

Of course, similar examples plus more can be found in my book, Pro Android Media and the source for the examples in the book is available

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);			
		}
	} 
}

JavaScript Serial Applet

A couple of weeks ago I put together a process for allowing access to a hardware serial port from JavaScript. I thought I would point to the documentation page here and allow the comments section of this post to be a place where questions could be answered and so on.

Without further ado, here it is:
JavaScript Serial

Update: I can no longer maintain or provide any kind of support on this software.

Mobile Art && Code

I am here at CMU in Pittsburgh at a conference called Mobile Art && Code. Great talks, great workshops, glad I came.

I am doing a workshop called Interactive Telephony for New Media Arts, here is the full set of notes.

One thing that I did for the workshop is a put together an Asterisk driven webservice in PHP for getting at live phone call data. If you have been working projects that get information from asterisk or use phone calls to control other applications, it might be worth checking out.

PHP AGI Script:

#!/usr/bin/php -q
<?PHP
require('/var/lib/asterisk/agi-bin/phpagi.php');

$agi = new AGI();

$agi->stream_file("vm-extension");
$return = $agi->wait_for_digit(10000);
while ($return['result'] > 0)
{
        $ascii = chr($return['result']);
        $agi->say_number($ascii);
        file_put_contents("/var/www/html/webservice/data.txt",time() . "," . $agi->request["agi_uniqueid"] . "," . $agi->request["agi_callerid"] . "," . $ascii . "\n",FILE_APPEND);
        $return = $agi->wait_for_digit(100000);
}
?>

PHP Web Service Script:

<?
	// The client will send a timestamp if it want's new stuff from the timestamp
	$timestamp = 0;
	if (isset($_GET['ts']))
	{
		$timestamp = $_GET['ts'];
	}

	$data = file_get_contents("data.txt");
	$dataarray = explode("\n",$data);
	if ($timestamp > 0)
	{
		// Send everything from the timestamp forward
		for ($i = sizeof($dataarray) - 10; $i < sizeof($dataarray); $i++)
		{
			$currentline = explode(",",$dataarray[$i]);
			if (sizeof($currentline) > 0)
			{
				if ($currentline[0] > $timestamp)
				{
					echo($dataarray[$i]."\n");
				}
			}
		}
	}
	else
	{
		// Just send the last one
		if (sizeof($dataarray) > 1)
		{
			echo($dataarray[sizeof($dataarray)-2]);
		}
	}
?>

Processing Example

I should note that this was built very quickly and therefore somewhat buggy. I don’t think the Processing example is thread safe and the PHP should really be using a database.. Also, the Processing example is a riff off of something Dan Shiffman put together for getting Asterisk and Processing to talk through a Java server.

Mobile, 5 Years in the future

I was just interviewed for an upcoming book and one of the questions was:
Fast forward 5 years into the future, can you paint me picture of the mobile world?

Here is what I said:
I am going to beg out of this one and instead paint a picture of my utopia.

My mobile utopia 5 years from now:

Carriers have accepted the fact that they are too large and slow to beat the current crop of DIY wireless systems that are being built. They have realized that the cost of maintaining service such as the little used voice platform is not worthwhile when all that anyone cares about is the openness and speed of their internet connection. Besides, they are sick of battling the hackers who continually figure out how to bypass their restrictions and really sick of spending their lobbying money to battle Googlezon and the like over whether or not they have to carry 3rd party data without charge.

They have finally realized and accepted their place in the world as “dumb pipes”, wireless ISPs.

They have given up on locking down phones. Nobody will sign a 2 year contract anymore for a free phone that they can’t install any of the open source software on.

On the other side of the coin, Googlezon, DIYers, hackers and hipsters are developing and deploying game changing hardware and applications at a phenomenal pace.

A prolific open source community has introduced a kit based mobile phone with every feature imaginable and battery life that puts devices from 5 years ago to shame. Tourists are carrying around monstrous looking home built teleconferencing systems with them as they gawk at the Naked Cowboy in Time Square and talk with their relatives and friends back home.

Hipsters in Bushwick no longer carry laptops and projectors to their VJ gigs but rather bring their mobile projector enabled high-speed wireless video mixing system and no longer have to be hunched over a keyboard and mouse. They simply mingle with the crowd or dance until they drop with every movement being tracked by sensors programmed to project and mix particular clips or dynamically generated visuals.

I can’t think of anyone who uses a laptop computer anymore. Everyone seems to have adopted the projected keyboard and gesture controlled interfaces that are common on mobile devices now.

Data flows pretty seamlessly and just by pointing to a contact in the sky a voice, data or text channel is opened to that person.

Wow.. Things are different now that the networks have been broken..

(Perhaps we can dream..)

Java Media – It is sad but I don’t care anymore…

Rebooting Java Media, Part III: Conclusion – O’Reilly ONJava Blog

Chris Adamson has put together a nice 3 part series of posts that explore the state of media support in Java. Long has this been a point of frustration for me and many of my colleagues (we tend to use QuickTime for Java but that is changing). I have been constantly on the look-out for a solution but one hasn’t been forthcoming. After reading Chris’ wrap-up, I have reached many of the same conclusions but I have a slightly different idea that I would like to propose.

Here are a couple of points that bring me to my conclusion:
1: I am not interested in Flash beyond what it can do with video. Flash does not have a desktop playback interface and it is not easy (as far as I know) to make a desktop app out of it. It is also seriously hindered without a plugin interface or the ability to playback other formats/codecs.
2: AJAX is open, well supported and not proprietary for rich browser based interfaces. It is very successful and is pushing hard against Flash (if it weren’t for Flash Video, I think we would be witnessing Flash’s demise (at least in terms of interfaces)).

What is missing is a truly open video format and player with the features that we all expect (codecs, wide distribution, browser integration, a plugin interface) that we can use with AJAX. QuickTime isn’t this, Real isn’t this, Windows Media isn’t this, Flash isn’t this..

This is what I think is needed.. Forget about Java Media. The people we have relied on have failed us (Apple, Sun and IBM), we should give them an F and move on.

Am I dreaming..? Can the mozilla-vlc-plugin become this?

Academic Asterisk Adventures

In my continuing adventures as an adjunct professor at NYU’s Interactive Telecommunications Program, I taught a new course last semester entitled “Redial: Interactive Telephony“.

The purpose of the course was to utilize emerging telephony technologies, concepts and services such as VoIP as a tool for building interactive applications and devices. ITP students are famous for their imaginative use of new technology. One of my goals in this course was to help them apply some of their creative and critical thinking to new telephony technology in the context of the rich history of telephony.

The course content was focused around voice and touch-tone based applications using Asterisk, SIP, RTP, text to speech (using Festival), speech recognition (using Sphinx) and the like.

The results of this course were truely fantastic and I will take a bit of time in the coming days/weeks to highlight their projects.

For a quick taste, checkout these projects

For now I want to write a bit about using Asterisk in an environment such as this in the hopes that some Asterisk users/developers offer suggestions and perhaps implement some of my suggestions.

We (18+ students in my class, myself and a couple of students following along without actually being members of the class) used an older Intel P3 machine running RedHat Enterprise Linux 4 (as I recall) with the Secure Linux enabled. We used Asterisk 1.2 and each student had a normal linux user account on the machine. We also had Apache 2, PHP, MySQL (running on another machine), PHP AGI, Perl, Festival and Sphinx.

Many of the issues that we ran into were a direct result of running PHP for both AGI scripting in Asterisk and normal web development with Apache. PHP’s safe mode and SE linux contributed to these difficulties.

The problem is that Asterisk was running as the “asterisk” user and Apache was running as a different user. PHP safe mode was relaxed to allow the group to execute the files but this still threw many of us for a loop several times.

I don’t want to whine about this too much as it is what was available and we just had to deal with it. One thing that might help would be to enable suexec within Asterisk so that AGI scripts could run as the owner of the script. This, I believe is how Apache can be setup to handle things and would go a long way towards alleviating many of the issues we had with both security and usability.

The next major problem we had was in developing dialplans and editing other Asterisk configuration files. At first, I setup an extensions file for each student that was included (using #include) in the main Asterisk extensions.conf file. Unfortunately, we ran up against a hard limit to the number of includes that Asterisk would handle and half of the files never got included.

To alleviate that problem, I put together a PHP script and a shell script that would cat together all of the extension files that needed to be included. These scripts also took care of issuing the reload command to the asterisk manager interface. This worked reasonably well but didn’t have any error checking so that if one user’s extensions file had errors or if they used a context that was named the same as another user problems would arise.

This is probably a harder problem to solve in the current design of Asterisk. I am interested in hearing other’s thoughts on how these problem could be solved. My thoughts are that Asterisk could somehow take some pointers from Apache and allow individual users to have a set of configuration files that get included at run time when their extension is entered. Something similar to the concept of a public_html directory. Asterisk when told to go to a specific user’s context would look in a specific directory and include the dialplan from there.

Perhaps I am just dreaming.. ;-) What do you think?

In any case, none of this would have been possible in a world without Asterisk and on behalf of my students and myself a big thank you to all those who have contributed to Asterisk!

Asterisk 1.4 Coming Soon!

Digium – The Asterisk Telephony Company

Ok, this is a big deal. The next version of Asterisk supports GoogleTalk!

From the Press Release:
Asterisk 1.4 is the first major release of Asterisk since the release of Asterisk 1.2 in November 2005. With over 20 new functionality additions including IPFAX compatibility, unified messaging capabilities and Jabber/Jingle/GoogleTalk protocol compatibilities, Asterisk 1.4 features overall quality and performance improvements, as well as increased scalability and interoperability.

ITJ Project Beta Released

Interactive Tele-Journalism
So.. I have finally released ITJ on SourceForge.net.

With support from Konscious and Manhattan Neighborhood Network we have packaged and uploaded the latest version and it can be downloaded at: http://sourceforge.net/projects/itv-ism/.