Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Monitor Your Web Cam from a Remote Computer : Page 2

We've offered a few solutions for working with web cams within .NET to create fun and intriguing monitoring applications. In this article, we extend those ideas so that web cam images can be shared with multiple clients over the Web.


advertisement
Saving the Video as Images
You will now modify the server so that it can act as a video server, accepting connections from clients and sending them images captured by the web cam.

The first step is to recognize that the video captured by the web cam can be saved as individual images. By displaying a series of continuous images on the client, it is similar to watching a video stream. To capture an image, I have defined the following subroutine:

'---save the video data into the Image global variable--- Public Sub CaptureImage() Dim data As IDataObject Dim bmap As Image Dim ms As New IO.MemoryStream() '---copy the image to the clipboard--- SendMessage(hWnd, WM_CAP_EDIT_COPY, 0, 0) '---retrieve the image from clipboard and convert it ' to the bitmap format data = Clipboard.GetDataObject() If data Is Nothing Then Exit Sub If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then '---convert the data into a Bitmap--- bmap = CType(data.GetData(GetType( _ System.Drawing.Bitmap)), Image) '---save the Bitmap into a memory stream--- bmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp) '---write the Bitmap from stream into a byte array--- Image = ms.GetBuffer End If End Sub

Here, I first copy the image displayed in the PictureBox control to the clipboard. I then convert it to an Image object and save it to a memory stream. Finally, I use the memory stream to write out the image as an array of bytes. The array of bytes is saved into a global variable, Image, which is defined in Module1.vb (right-click on project name in Solution Explorer and select Add | New Item…. Then select Module):


Module Module1 Public Image As Byte() End Module

Saving the image as a byte array allows me to easily transmit the image over a socket connection.

To ensure that the Image variable is always containing the latest image, add a Timer control to Form1 and set its properties as follows:

  • Enabled—True
  • Interval—100

Double-click on the Timer control (located underneath Form1) to reveal its Tick event handler. Code the Tick event handler as follows:

'---save the video image at regular intervals--- Private Sub Timer1_Tick( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Timer1.Tick CaptureImage() End Sub

Essentially, you are invoking the CaptureImage() subroutine every 100 milliseconds (10 times per second) so that the Image variable is always containing the latest video image.

Communication between the Clients and Server
The final step is to write the code for communicating with clients over a socket connection. Before I show you the code to do so, you should understand how the client will communicate with the server.

Figure 3. Communication between a client and the server involves a Send message that receives a video capture as a reply.
As shown in Figure 3, upon connecting to the server, the client first sends a "Send" message to the server. When the server receives a "Send" message, it sends back to the client an image captured by the web cam (specifically, the data contained within the Image global variable). The transfer takes place synchronously and the client will only send back another "Send" message when it is ready to accept another image from the server. This technique prevents the server from overwhelming the client, especially if the client is connected to the server over a slow connection.

Now, to enable this communication, add a new class to the project and name it WebCamClient.vb. Import the following namespace:

Imports System.Net.Sockets

Declare the following constant and variables:

'---class to contain information of each client--- Public Class WebCamClient '--constant for LineFeed character--- Private Const LF As Integer = 10 '---contains a list of all the clients--- Public Shared AllClients As New Hashtable '---information about the client--- Private _client As TcpClient Private _clientIP As String '---used for sending/receiving data--- Private data() As Byte '---used to store partially received data--- Private partialStr As String

Define the constructor for the WebCamClient class as follows:

'---when a client is connected--- Public Sub New(ByVal client As TcpClient) _client = client '---get the client IP address--- _clientIP = client.Client.RemoteEndPoint.ToString '---add the current client to the hash table--- AllClients.Add(_clientIP, Me) '---start reading data from the client in a separate thread--- ReDim data(_client.ReceiveBufferSize - 1) _client.GetStream.BeginRead(data, 0, _ CInt(_client.ReceiveBufferSize), _ AddressOf ReceiveMessage, Nothing) End Sub

The ReceiveMessage() subroutine reads the data sent from the client. All messages sent from the client will end with a LineFeed (LF) character. Because a single message may be broken up into a few blocks during transmission, it is thus important that you detect for a LF character to ensure that you have received the entire message. Once a message is received and it contains the word "Send," the web cam image is sent to the client using the SendData() subroutine (defined next):

'---receiving a message from the client--- Public Sub ReceiveMessage(ByVal ar As IAsyncResult) '---read from client--- Dim bytesRead As Integer Try SyncLock _client.GetStream bytesRead = _client.GetStream.EndRead(ar) End SyncLock '---client has disconnected--- If bytesRead < 1 Then AllClients.Remove(_clientIP) Exit Sub Else Dim messageReceived As String Dim i As Integer = 0 Dim start As Integer = 0 '---loop until no more chars--- While data(i) <> 0 '---do not scan more than what is read--- If i + 1 > bytesRead Then Exit While '---if LF is detected--- If data(i) = LF Then messageReceived = partialStr & _ System.Text.Encoding.ASCII.GetString( _ data, start, i - start) If messageReceived.StartsWith("Send") Then SendData(Image) End If start = i + 1 End If i += 1 End While '---partial string--- If start <> i Then partialStr = _ System.Text.Encoding.ASCII.GetString( _ data, start, i - start) End If End If '---continue reading from client--- SyncLock _client.GetStream _client.GetStream.BeginRead(data, 0, _ CInt(_client.ReceiveBufferSize), _ AddressOf ReceiveMessage, Nothing) End SyncLock Catch ex As Exception '---remove the client from the HashTable--- AllClients.Remove(_clientIP) Console.WriteLine(ex.ToString) End Try End Sub

The SendData() subroutine sends the data contained in the Image global variable over to the client:

'---send the data to the client--- Public Sub SendData(ByVal data As Byte()) Try Dim ns As System.Net.Sockets.NetworkStream SyncLock _client.GetStream ns = _client.GetStream ns.Write(data, 0, data.Length) End SyncLock Catch ex As Exception Console.WriteLine(ex.ToString) End Try End Sub

Back in Form1, you can now wire up the rest of the code to make the server functional. Add the following constants and variable:

Public Class Form1 '---port no for listening and sending data--- Const IP_Address As String = "127.0.0.1" Const portNo As Integer = 500 '---use to spin off a thread to listen for incoming connections--- Dim t As System.Threading.Thread

Define the Listen() subroutine to listen for incoming socket connections:

'---listen for incoming connections--- Private Sub Listen() Dim localAdd As System.Net.IPAddress = _ System.Net.IPAddress.Parse(IP_Address) Dim listener As New System.Net.Sockets.TcpListener( _ localAdd, portNo) listener.Start() While True Dim user As New WebCamClient(listener.AcceptTcpClient) End While End Sub

In the Form1_Load event, preview the video by calling the PreviewVideo() subroutine and then spin off a separate thread to listen for incoming connections from clients:

Private Sub Form1_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load '---preview the selected video source PreviewVideo(PictureBox1) '---listen for incoming connections from clients--- t = New System.Threading.Thread(AddressOf Listen) t.Start() End Sub

Finally, if Form1 is closed, abort the thread (for listening for connections) and end the application:

Private Sub Form1_FormClosing( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms.FormClosingEventArgs) _ Handles Me.FormClosing t.Abort() End End Sub



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap