Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Web-Cam SecureChat

4.94/5 (16 votes)
12 Mar 2010CPOL6 min read 96K   7.4K  
This article will explain how to create a simple chat program using this remoting technology, which supports web-cam and sending files.

Introduction

This article explains how to create a simple Secure Chat with WebCam support using Pfz.Remoting.

Background

I've already written some articles about communication, in special Pfz.Remoting and Creating an Asymmetric/Symmetric Secure Stream without SSL, which is in fact part of the remoting DLL. This article will explain how to create a simple chat program using this remoting technology, which supports web-cam and sending files.

I then wanted to create an article explaining how to create a simple multi-user chat but, when I saw AForge and its video capture capabilities, I wanted to create a chat program with web-cam support. In the end, I created a very basic chat, almost without exception handling, but the important part is that it supports web-cam and audio capture with multiple clients.

The Principle

The principle is very simple. Data can be lost. This is not a very big problem for video, it can be for audio, but it is not possible to simple avoid it.

For example, the program receiving the web-cam frames can receive 10 frames, but the client which receives it can only be able to receive 5. Intermediate frames must be lost.

Also, in the architecture I made for this sample, everything goes throught the server. So, the web-cam owner receives 10 frames. The server receives only seven frames. And it can distribute the frames it receives among its clients, which one can be able to receive them all (the 7), another can receive only 3 and another can receive only 5. So, how do we distribute the data?

ActualValueEnumerator and EnumeratorDistributor

I created two main classes that help the distribution of data that "can be lost". The ActualValueEnumerator is a class that "enumerates" throught its values, and waits for new values when all values are already read. But, if two or more values appears before the "enumerator" is able to read them, only the last one (the actual) remains. That's why I called the class ActualValueEnumerator.

In the first version of the article I made the WebCamEnumerator class using ManualResetEvent, but it was ugly. Now it is very simple. So, look at the code:

C#
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using AForge.Video;
using AForge.Video.DirectShow;
using Pfz.Collections;
 
namespace SecureChat.Client
{
	public sealed class WebCamEnumerator:
		ActualValueEnumerator<byte[]>
	{
		private volatile VideoCaptureDevice fDevice;
		public WebCamEnumerator(string monikerString)
		{
			var device = new VideoCaptureDevice(monikerString);
			fDevice = device;
			device.DesiredFrameSize = new Size(160, 120);
			device.NewFrame += p_NewFrame;
			device.Start();
		}
		protected override void Dispose(bool disposing)
		{
			var device = fDevice;
			if (device != null)
			{
				fDevice = null;
				device.SignalToStop();
				device.WaitForStop();
			}
 
 			 base.Dispose(disposing);
		}
 
		private void p_NewFrame(object sender, NewFrameEventArgs eventArgs)
		{
			var image = (Bitmap)eventArgs.Frame;
			
			using(var stream = new MemoryStream())
			{
				image.Save(stream, ImageFormat.Jpeg);
				ActualValue = stream.ToArray();
			}
		}
	}
}

The important things here are: In the constructor, it starts the web-cam and sets the NewFrame event. In the dispose it stops the web-cam. And, in the new frame, it generates the byte array of the image and sets the ActualValue to such bytes. I convert the image to bytes so the server does not need to know how to process graphics... it only receives bytes and distribute bytes to the clients. It may not be "visible" in the first moment, but as this class inherits from ActualValueEnumerator, it can be used in a foreach (must use Pfz.Extensions.FastEnumeratorExtensions and the AsEnumerable method) and it already deals with the possibility of loosing frames.

The server is always a bit more complicated, and I must admit I made it hard. I will try to make it easier and re-update the article, but to help the server be less-complicated I created the EnumeratorDistributor class. Similar to the ActualValueEnumerator, it allows values to be lost. But, instead of being an enumerator itself, it must be used over another enumerator, and each client gets its own enumerator which allows the frames to be lost. So, different speed clients will receive different number of values. So, to use it, the server receives the enumerator from one client (for example, the server receives the WebCamEnumerator from one client), and creates an EnumeratorDistributor over it. Each client that is now interested in such web-cam will get a new enumerator (throught the CreateClient method), which will always receive the last frame from such base enumerator. If frames are to be skipped, they will be.

The project As I said earlier, the server is still more complicated than it needs to be, but the basic idea here is: There is the Common library, in which the classes and interfaces used by both client and server are present. Note that any method the client needs to call in the server or that the client must call in the client must be done via interfaces, which are here.

There is the server project, which supports the client connections and implements the server part, so it is responsible for telling all other clients when a new one connects and to find another client, requests its web-cam or sound-capture device and returning the appropriate enumerator when someone asks for the web-cam or sound from another client. And the client project. It is "simpler" than the server as it does not need to deals with many clients, but it must be able to capture video and sound and must be able to play them. Actually, when one client wants the video or sound from another one, it must request the Server for such information, which will be returned as a IFastEnumerator. The IFastEnumerator is faster than a custom enumerator over the TCP/IP, as instead of first calling "MoveNext" (which sends and receives information) and then Current (which agains sends and receives information) it uses only GetNext, which can return null to tell there are no more values. Well, I hope this little explanation and the source-code is enough for anyone trying to create an application which remote web-cam or sound-capture support.

The Samples

If you want to try the samples, you must:

  • Run the server in command prompt with any parameter (I use the parameter DEBUG), or install it with installutil and then start the service.
  • Run a local client normally, if you want.
  • Change the app.config to point to the server host and run a client from another computer. Having more clients is better.
  • Actually the user-name is taken from the user logged in the system, and this user can't log many times in the same server. Someone must start his own web-cam (be it a fake web-cam or a real one). Then, the others can ask to start that clients' web-cam.

The program supports simple chat and file transfer (drag-and-drop a file over a user name). Dragging a file to your own name will do a really wasteful work of sending the file to the server and then sending the file back to the client.

History

  • 17th December, 2009: Initial post
  • 11th January, 2010: New version of webcam source - This version has corrected a bug in the server, where a client with two webcams will only send the first one for the clients, not importing which one the client asked. Also, it has corrected a bug when sending files with more than 2GB.
  • 12th March, 2010: Added very basic support to sound capture.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)