Showing posts with label examples. Show all posts
Showing posts with label examples. Show all posts

Tuesday, March 3, 2009

File Sharing using Stratus (Part 2: Send file example)

As described in previous post, I was experimenting with the new Stratus service introduced by Adobe with the relase 10 of Flash Player.

After going over the VideoPhoneLabs example by Jozsef
Vass, I was curious about trying to use the same approach for a different need, in this case a binary file transfer, having in mind P2P file sharing in Flash.

Here it is the resulting app:


Remember Flash 10 or newer is required. Test your current verision here.

Notes:
By a quick search, I came across this forum post where someone asked about the possibility of doing this and there was the confirmation by using the NetStream’s “send()” method to send any kind of content.

A guy called Kris in Duesseldorf did also introduce his work in that thread, and these posts about transferring Classes between peers were also quite interesting.
communicating arbitrary data via rtfmp (pt. 1)
communicating arbitrary data via rtfmp (pt. 2)



So hands on, let’s code a proof of concept that is capable of sending files through peers!

I. Use case:

A user will log in into the application using a nick name and he/she will get a List of other users connected to it. He/she will be able to choose one of the connected users, select one of the files in his/her local filesystem and send it to the target peer. He/she (the destination user), will be prompted to accept/deny the new incoming file.

This is just a proof of concept and is not intended to work as a production application, so there’s no work related to security, performance or other possible improvements.


II. User management service:

As described in the previous post, Stratus is a service used to manage the connection between 2 peers and allow them to communicate directly, but the way one discovers each other, how they meet, is out of scope of the service.

In Josez Vass example, a “reg.cgi” file with the structure of a python web app is provided, with the idea of people interested in running the example deploying it into his/hers own systems. It’s great he provided this script, b
ut it was a subject and a technology totally disconnected from were the example is about, Flex and Stratus.

I started looking how to deploy this script when I thought that it would be great if I could offer access to it to anyone trying my example, to avoid this non related subject/technology barrier. So it was a great excuse to st
art with Python, Django and Google’s AppEngine, and my first dummy Google AppEngine application came out!

If you run the example, you’ll be connecting to a Django application hosted in http://stratus-demo-service.appspot.com/ . It’s so simple and, probably, so bad coded, so I’ll keep the code, but feel free to ask for it if
someone wants to check.

It just implements the same API as Josez’s python script, so if you want to use it to try VideoPhoneLabs application, just go and change the WebServiceUrl constant in VideoPhoneLabs.mxml file to

private const WebServiceUrl:String = "http://stratus-demo-service.appspot.com/"


This app has three possible calls:

  • http://stratus-demo-service.appspot.com/?username=myUserName&identity=12345678901234567890 will register the requesting usename with the nearID passed as “identity” in to the system. This just stores a triple username-nearID-updatetime into a Django Model (in this case, a google.appengine.ext.db.Model object), that contains those three files. Successive calls with the same username will result in updating this updatetime field, used to timeout unactive users.
  • http://stratus-demo-service.appspot.com/?listUsers=true will retrieve a list of all currently conneceted users. Are considered connected users those that made a “PING” call to the system (a user register call) before than 2 minutes ago (so user connections expire after 2 minutes).
  • http://stratus-demo-service.appspot.com/?friends=username1&friends=username2 will retrieve the list of just the given usernames with their ids. This called is provided for compatibility with the VideoPhoneLabs example but it’s not used for the File Sharing one.



As said before, this is my first Python, Django and AppEngine app, so please don't be very strict if something is not working ;-)


III. File Sharing itself:
So, with the user management problem solved, I proceed to creat a project to illustrate the file sending functionallity. The structure is similar to the VideoPhoneLabs application, but without the camera/mic setup and managing the user connection to the user management system all in a single .mxml file. I just wanted to make the code easy to follow, so I tried to write it in the execution order, and avoid adding any auxiliar file.

Then in the file, you’ll see the following structure:

0. Stratus service connection code. Here, a NetConnection object that handles the connection to the Stratus service is set up.
1. Registering to the user management service. A call to the AppEngine service is done here to register the new nearID provided by the Stratus service in section 0. This code will be called regularly to maintain the user session alive. Once the user is registered, a NetStream “listenerStream” is created to recieve incoming file transfers.
2. Userlist request. The result is parsed into an Array used as binding source for a ComboBox of users.

This first sections are common for both, sending and recieving users.

3. Makes the handling for File selection from the browsed file system.
4. Waits for the “Send” button to be pressed. If it is the case and a destination user has been chosen, a NetStream “outgoingStream” is published. This is stream is the one the receiving user will register to to download the transfered file. So once it is published, the sending user asks the receiving if he/she would be so kind to ask it “please”. This sounds ironic, but is the only way of the transfer to start... if the destination user asks it, that is, if the destination user registers to a origin’s user NetStream (in this case, the “outgoingStream” we’ve just created).
5. The recieving user is pinged in his listenerStream then, and he/she answers subscribing to the sender’s “outgoingStream”, published with the name “fileRequest”.
6. The sender recieves some events, and we’ll wait until a NetStream.Play.Start to invoke the desired function:

switch (event.info.code){
case "NetStream.Play.Start":
log(INFO,"Sending "+sendFile.name+" ("
+sendFile.size+" bytes)");
outgoingStream.send("receiverFunction",
sendFile.data as ByteArray,userName.text,sendFile.name);
break;
}

Receiver “receiverFunction” is called, and the user is prompted to get the file. If he/she answers affirmatively, a new File with the transfered content is stored were the user decided.



So that’s much it! Try the app at the top of the post, entering a username and waiting for someone to connect... or being more realistic, opening a couple of browsers and simulating being two different users :-P

Here the code. Any kind of feedback is appreciated :-)

Wednesday, December 17, 2008

Webcam picture taking with Flash and AS3

Long time since last post... It's been nearly 2 months since I changed my internet provider at home... and have no connection yet :-(


I recently had to implement a cool feature for a project, consisting in a picture-taker using the webcam, for a user registration process. The feature has been added recently in Facebook, allowing users to upload pictures and videos directly to their friends' walls.


Displaying the webcam content in a Flash movie is quite easy:


// 1. Access the default camera

var cam:Camera = Camera.getCamera();
// 2. Create a video display object

var video:Video = new Video(550,400);

// 3. Attach the camera to the video display
video.attachCamera(cam);
// 4. Add the video display object to the stage
stage.addChild(video);


Then, through the BitmapData object, it's possible to take a snapshot at the desired moment drawing the data from the Video object into a ByteArray (and encoding this information using an encoder JPEG/PN
G/...). This binary data can be sent to the server, with a URLRequest and there processed by whatever server side technology.

// 1. Create the req
uest object
var req:URLRequest = new URLRequest(remoteURL);
// 2. Specify we're sending binary data
req.contentType = "application/octet-stream";
// 3. Send by POST (GET doesn't allow enough data)
req.method = URLRequestMethod.POST;
// 4. Put the image binary data in the request body
req.data = binaryArray;

It's quite easy to get this point. But there's a usability issue with all this stuff, which is that to access the webcam, Flash needs the user authorization, requested through a dialog. First thing that drove me crazy, because if the Flash movie doesn't have the minimum dimensions (215 x 138) to show this dialog, there's no advice, but the video won't be displayed. It's clearly explained in the ActionScript 3 language reference (link to API), it's true. My fault, my fault :-P


But then, a second usability problem. Camera.getCamera() retrieves the "default" camera in the computer. In my MacBook Pro, Flash detects 3 webcams (even I just have the built-in one), which are displayed as "FireWire Video","DV Video" and "USB Video Class Video".


The problem is that just from the "USB Video Class Video" I'm able to capture video images. So it's not just asking for permission to access the webcam to the user, but also requiring him/her to go to the camera tab in the Flash settings menu, to try between this unknown video devices to check which one works? But wait, Facebook is detecting my webcam automatically! Even if I choose one of the non valid... when I try again the "USB Video Class Video" it's being used. So it must be possible to select the correct one somehow. And then I came across this article (Sander Kruger's blog):

Here, the author explains a tricky way to detect the valid one. The basics:

- Camera.getCameras() returns an array of Camera objects with the system cameras (in my MacBook Pro [FireWire Video, DV Video. USB Video Class Video], so we can know how many are accessible.
- Camera.getCamera() will get the default one (could be any of the system ones, available or not), but there's a second method, Camera.getCamera(String), that retrieves one specifically, from the total we discovered using Camera.getCameras(). The String parameter, represents the index of every camera in the array Camera.getCameras(), [0 to N-1]. So in my case, we can get the USB Video Class Video Cam for example, doing Camera.getCamera("2").
- The Camera object will dispatch a StatusEvent notifying if the user has accepted or rejected the access to the webcam from the Flash built dialog.
- The Camera object will dispatch ActivityEvents when it detects movement in the scene, but only if the camera is attached to a Video object where to be displayed.

With all this ingredients then you can cook a camera detection process. Once the Camera says it's accessible from the user request dialog (StatusEvent), you can loop over each cam object from Camera.getCameras, adding an activity handler for the ActivityEvents it could dispatch, and attach the Camera to a Video object, that's not added to the Stage (always hidden then). Then starting a Timer, we can give the Camera some time to detect activity... if it was not the case and the countdown finishes, then we should remove the activity event, detach the camera from the video and move to try the next Camera from the Array. Keep looping this way until one of the Cameras gives some activity news... or all of them are checked and none response to activity.

Hope the experience is helpful to someone :-)

Thursday, October 23, 2008

Flex performance tips

One of the most interesting sessions I assisted in MIF Onsite III was "Flex optimization" by Alberto Alacaraz. He talked about optimizing Flex applications, performance and memory usage, and some tips to achieve it by using the Flex Profiler.

Performance/memory usage are both equally important issues while developing successful applications, but focusing in performance, it's a key aspect when talking about user experience. When working with interactive aplications, the user expects an inmediate response to a button click, or a key press, a drag & drop... and if these events fire any inefficient processes, the overall user impression about the application can be damaged.

Alberto pointed some simple tips to have in mind when coding Flex applications. Very simple things in fact, but I often forget those when coding...

  • Remembering ActionScript statements such as "break" or "continue" when iterating in loops can save some precious time. Come on, review ActionScript3.0 statements!
  • Bindings in Flex are very powerful, but involve some overhead we must be aware of. They are very useful to ensure we're displaying the latest data in our views, but do we want to notify the view of every change we make over this data? Perhaps notifying sets of changes, between consistent states could be enough...
  • The object pool pattern, that a very elegant way to improve object instantiation times.

Here, I try reproduce some of the examples Alberto showed (here the source code):





MAXwidget