So let's start. First of all, take a look at how the application will be organized. There are three main components:
- Client Application - Running on the machine where the webcam is installed. This will be the application that will generate the main data to be transmitted.
- WCF Service - This will be the intermediary link that will grab the data from the client application and pass it to the WP7 app.
- WP7 Application - The actual mobile application that will display the data.
The client application is built exactly like I described it in one of my tutorials on creating a webcam viewing WPF application. You need to build that application since I am going to use it in this article as well. If you have the client application ready (at this moment, no modifications are needed), let's start building the service.
Create a new WCF Service Application and simply add this code to the main service class:
public class Service1
static byte imageData;
public byte GetData()
public void SetData(byte data)
imageData = data;
As simple as it can get. SetData receives the image bytes from the application and GetData returns existing bytes to the requesting application. Notice, however, that imageData (the byte array) is set to static. Both the desktop and the mobile applications will hold a reference to the service client. If I am not going to mark this field as static, every new instance of the service client will get a completely new array, and that's not what we want here. The data should be preserved and accessible no matter what instance is calling it.
Now let's go back to the client application. We already have the data from the webcam returned as a handle - deviceHandle. But that's an IntPtr, while we need a byte array. In fact, we have a data object and we can copy it to the clipboard and then perform the needed transformations.
So, in the WPF application I created a new method called StartSendingData that will handle data processing. What I do first, I create an instance of IDataObject and Image (System.Drawing) to store the image the data.
Now I need to actually pass the data object kept by the handle to the system clipboard. To do this, I am calling SendMessage with WM_CAP_EDIT_COPY constant passed as a parameter - this will store the current data from deviceHandle in the clipboard.
SendMessage(deviceHandle, WM_CAP_EDIT_COPY, IntPtr.Zero, IntPtr.Zero);
To get the object from the clipboard, I can use this:
dataObject = Clipboard.GetDataObject();
There is a slight chance that in between my operations the user will alter the data object in the clipboard, therefore I need to check whether the object in the clipboard can be converted to bitmap before going any further:
If the verification is passed, then I can convert the data object to an Image instance (once again, System.Drawing):
image = (System.Drawing.Image)dataObject.GetData(typeof(System.Drawing.Bitmap));
Depending on the installed webcam, the image can be really big (both resolution and size-wise). By default a WCF service has per-request data transfer limit set to 16384 bytes. Of course, you can adjust this limit (in fact, increase it) by modifying the default binding, but I generally wouldn't recommend doing it in this case.
Keep in mind the fact that you are transmitting data to a mobile device, that can receive data via a cellular network, where every extra megabyte is going to cost. Therefore, before passing the actual byte array to the service, I am minimizing the image:
System.Drawing.Image targetImage = image.GetThumbnailImage(image.Width / 2, image.Height / 2, null, IntPtr.Zero);
I can now work on the transmission mechanism. First of all, I need a stream to write the data to:
using (MemoryStream stream = new MemoryStream())
Here I can save the image directly to the stream and convert it to a byte array:
byte b = stream.ToArray();
Now, make sure that the service you developed is running. Add a reference to it in your desktop application (of course, as a service reference):
In the scope of the same MemoryStream, create an instance of BackgroundWorker, that will be executing the data transmission in a background thread:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
Notice that I am passing the byte array as a parameter to RunWorkerAsync - that way, I can get its value from the secondary thread.
The DoWork event handler looks like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
ImageService.Service1Client client = new ImageService.Service1Client();
All I do is instantiate the service client and pass the argument from RunWorkerAsync as a byte array (notice the explicit cast) to SetData.
So far so good, but how to make this process continuous? There is RunWorkerCompleted for that:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
When the data transmission is complete, it will be sent again, and so on. That's it for the client application. So what I did in the app itself is I added two buttons in the main window - one to start the capturing process and another to start data transmission:
The capturing button executes the same action for image stream acquisition like it was outlined in the webcam article. The sending button executes one single action:
private void SendButton_Click(object sender, RoutedEventArgs e)
You can now run the client application - capture first, and then send.
In the mobile application, add the same service reference you added for the client app - since the same service is used to retrieve binary content.
I implemented a method named ServiceCall that will instantiate the service client and will trigger the binary data acquisition process:
ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
client.GetDataCompleted += new EventHandler<ServiceReference1.GetDataCompletedEventArgs>(client_GetDataCompleted);
When the transfer completes, I can set the binary data to an image:
void client_GetDataCompleted(object sender, ServiceReference1.GetDataCompletedEventArgs e)
using (MemoryStream stream = new MemoryStream(e.Result))
BitmapImage image = new BitmapImage();
image1.Source = image;
It is also set up in a way that it will run continuously, triggering the same method when it completes.
Make sure you call ServiceCall somewhere in your WP7 application and once all three components are running (the client application should be set to send the data) you should get results similar to this:
Of course, there will be a lag to some extent (depending on the image quality and the connection speed) but nonetheless, it works. It is a project from the "just for fun" category for sure, but I can certainly see places where it can be applied.
You can download the source code here (ZIP archives - developed in Visual Studio 2010):