VB.NET实现远程视频监视

前端之家收集整理的这篇文章主要介绍了VB.NET实现远程视频监视前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

新闻简介:

我们已经为在.NET中创建有趣的和引人入胜的网络视频监察应用提供了多种解决方案.在这篇文章里,我们让这个想法得到扩展,可以让更多的客户端同时在web上分享网络视频图像
关 键 词: vb.net 视频 .NET

英文原文:http://www.devx.com/dotnet/Article/33637

译者Jack 五邑技术网

在我早期为DevX写的两篇文章中,"旧摄像头玩新把戏:在您的.NET程序中应用视频捕捉" 和 "用网络视频和伺服器构建一个强大的安防系统," 我已展示如何将你的网络摄像头与你的.NET程序线路合,并利用它来实现监视的目的. 然而,使用这些解决方案,只能在本地的电脑上看运行网络摄像头的图像. 这一次我们要实现一个有趣的扩大应用,使视频可以通过因特网远程观看.

然后在这篇文章里我将教你您怎么使用socket通信从服务器到远程客户发送一个在线视频图像。这篇文章中的样例包括:

  • 服务器端播放从网络摄像头上视频捕获的图像
  • 客户端从服务器接收直播图像

服务器将允许多个客户端同时连接到它。尤其,这是对您能使用它在您的办公室监测您的家或孩子的家庭环境的有用应用程序.

创造服务器端
首先我要创造服务器端。使用Visual Studio 2005,创造一种新Windows 应用程序并命名为RemoteMonitoring 。在默认的Form1中,增加PictureBox 控件(参见 图 1) 和设置它的属性如下:

  • Size—449,253
  • SizeMode—StretchImage


图 1.添加一个PictureBox控件到Form1.

图 2. 现在您能在应用程序中预览来自助网络摄像头的图像
切换到后台代码窗口并导入以下namespace:
Imports System.Runtime.InteropServices
定义用于显示摄像头图像的常量和变量轮: 
Public Class Form1
    '---constants for capturing the video from webcam---
    Const WM_CAP_START = &H400S
    Const WS_CHILD = &H40000000
    Const WS_VISIBLE = &H10000000

    Const WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10
    Const WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11
    Const WM_CAP_EDIT_COPY = WM_CAP_START + 30
    Const WM_CAP_SEQUENCE = WM_CAP_START + 62
    Const WM_CAP_FILE_SAVEAS = WM_CAP_START + 23

    Const WM_CAP_SET_SCALE = WM_CAP_START + 53
    Const WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52
    Const WM_CAP_SET_PREVIEW = WM_CAP_START + 50

    Const SWP_NOMOVE = &H2S
    Const SWP_NOSIZE = 1
    Const SWP_NOZORDER = &H4S
    Const HWND_BOTTOM = 1

    '---used as a window handle---
    Dim hWnd As Integer
定义常量后,你需要为视频捕捉功能定义函数,可以在avicap32.dll库中找到,而以下三个函数(可以在user32.dll找到)被应用于多个不同的窗口.
'--The capGetDriverDescription function retrieves the version 
    ' description of the capture driver--
    Declare Function capGetDriverDescriptionA Lib "avicap32.dll" _
       (ByVal wDriverIndex As Short,_
        ByVal lpszName As String,ByVal cbName As Integer,_
        ByVal lpszVer As String,_
        ByVal cbVer As Integer) As Boolean

    '--The capCreateCaptureWindow function creates a capture window--
    Declare Function capCreateCaptureWindowA Lib "avicap32.dll" _
       (ByVal lpszWindowName As String,ByVal dwStyle As Integer,_
        ByVal x As Integer,ByVal y As Integer,_
        ByVal nWidth As Integer,_
        ByVal nHeight As Short,ByVal hWnd As Integer,_
        ByVal nID As Integer) As Integer

    '--This function sends the specified message to a window or 
    ' windows--
    Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
       (ByVal hwnd As Integer,ByVal Msg As Integer,_
        ByVal wParam As Integer,_
        <MarshalAs(UnmanagedType.AsAny)> ByVal lParam As Object) _
        As Integer

    '--Sets the position of the window relative to the screen buffer--
    Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" _
       (ByVal hwnd As Integer,_
        ByVal hWndInsertAfter As Integer,ByVal x As Integer,_
        ByVal y As Integer,_
        ByVal cx As Integer,ByVal cy As Integer,_
        ByVal wFlags As Integer) As Integer

    '--This function destroys the specified window--
    Declare Function DestroyWindow Lib "user32" _
       (ByVal hndw As Integer) As Boolean
定义PreviewVideo()了程序,可以让你在PictureBox控件里播放捕捉于视频摄像头的图像: 
'---preview the selected video source---
    Private Sub PreviewVideo(ByVal pbCtrl As PictureBox)
        hWnd = capCreateCaptureWindowA(0,_
            WS_VISIBLE Or WS_CHILD,_
            0,pbCtrl.Handle.ToInt32,0)
        If SendMessage( _
           hWnd,WM_CAP_DRIVER_CONNECT,_
           0,0) Then
            '---set the preview scale---
            SendMessage(hWnd,WM_CAP_SET_SCALE,True,0)
            '---set the preview rate (ms)---
            SendMessage(hWnd,WM_CAP_SET_PREVIEWRATE,30,0)
            '---start previewing the image---
            SendMessage(hWnd,WM_CAP_SET_PREVIEW,0)
            '---resize window to fit in PictureBox control---
            SetWindowPos(hWnd,HWND_BOTTOM,_
               pbCtrl.Width,pbCtrl.Height,_
               SWP_NOMOVE Or SWP_NOZORDER)
        Else
            '--error connecting to video source---
            DestroyWindow(hWnd)
        End If
    End Sub
你现要可以测试了,如果你能通过在Form1_Load事件中调用 PreviewVideo()子程序进行预览视频:
Private Sub Form1_Load( _
       ByVal sender As System.Object,_
       ByVal e As System.EventArgs) Handles MyBase.Load
        PreviewVideo(PictureBox1)
    End Sub
按F5 测试应用程序(当然,要确定你的视频头连接着您的计算机) 。 图2 显示摄像头的图像被显示在PictureBox控件里.
你现在修改服务器,使之可以作为视频服务器,接受客户端的连接,给他们发送来自摄像头的图像.

第一步要认识到来自摄像头的视频可以保存为各自的图象. 在客户端显示一系列连续图象,类似观看视频流. 为了捕捉一个图像,我将定义下面的子程序:

   
   
这里,我先复制一个显示在PictureBox 控件里的图象到剪贴板里,然后把它转换成图形对象,并保存进一个内存流里,最后,我将内存流作为一系列的字节写出,这些字节被保存进一个全局变量Image里,这个变量定义在Module1.vb (右键点击项目名称,在解决方案资源管理器中选择添加|新项目的末尾. 然后选择 模块):


   
   
保存图象作为字节序列阵让我更容易地通过socket联接传送图像

为了确保Image变量总是包含最新的图像,添加一个Timer控件到Form1里,并设置它的属性如下:

  • Enabled—True
  • Interval—100

双击在Timer定时器控件(位于在Form1 之下) 显示出它的Tick事件控件。编码Tick事件如下:



   
   
基本上,你将每100毫秒(10次每秒)引用captureimage()子程序一次,使Image变量总是含有最新视频图像.
最后一步是写代码通过进行沟通Socket连接与客户端沟通.,我之前给你的代码就是如此做的,你应该了解如何将客户端与服务器端.
沟通客户端和服务器端

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:



   
   
Declare the following constant and variables:


   
   
Define the constructor for the WebCamClient class as follows:


   
   
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):


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


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


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


   
   
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:


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


'---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)

        '---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 SubModule Module1
    Public Image As Byte()
End Module'---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 SubImports System.Net.Sockets'---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'---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,_
           CInt(_client.ReceiveBufferSize),_
           AddressOf ReceiveMessage,Nothing)
    End Sub'---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,i - start)
                End If
            End If

            '---continue reading from client---
            SyncLock _client.GetStream
                _client.GetStream.BeginRead(data,_
                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'---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,data.Length)
            End SyncLock
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        End Try
    End SubPublic 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'---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 SubPrivate 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 SubPrivate Sub Form1_FormClosing( _
       ByVal sender As Object,_
       ByVal e As System.Windows.Forms.FormClosingEventArgs) _
       Handles Me.FormClosing
        t.Abort()
        End
    End SubUsing Visual Studio 2005,create a new Windows application and name it RemoteMonitoringClient. In the default Form1,populate the controls as shown in Figure 4. 
     

   
Figure 4. Populate Form1 with the varIoUs controls shown.
Set the properties of PictureBox1 as follows:
  • Size—449,253
  • SizeMode—StretchImage
PictureBox2 is set to display the image of an LCD monitor (you can do so via its Image property). Be sure to bring PictureBox1 to the front (right-click on PictureBox1 and select Bring to Front).

Switching to the code-behind of Form1,import the following namespace:


Imports System.Net.Sockets
Imports System.IO
Declare the following constant and member variables:

Public Class Form1
    '---get own IP address---
    Private ips As Net.IPHostEntry = _
       Net.Dns.GetHostEntry(Net.Dns.GetHostName())

    '---port nos and server IP address---
    Const PORTNO As Integer = 500
    Private server_IP As String = " 127.0.0.1"

    '---size of the video image---
    Const SIZEOFIMAGE As Integer = 341504

    '---use for connecting to the server---
    Private client As TcpClient

    '--used for sending and receiving data---
    Private data() As Byte

    '---used for receiving images from the server---
    Private t As System.Threading.Thread
Code the Click event handler of the Start button control as follows:

    Private Sub btnStartStop_Click( _
       ByVal sender As System.Object,_
       ByVal e As System.EventArgs) _
       Handles btnStartStop.Click

        If CType(sender,Button).Text = "Start" Then
            Try
                '---set the server IP address---
                server_IP = txtServerIP.Text

                '---connect to the server---
                client = New TcpClient
                client.Connect(server_IP,PORTNO)
                ReDim data(client.ReceiveBufferSize - 1)

                '---send message---
                SendMessage("Send")

                '---begin reading data asynchronously from the 
                ' server---
                t = New System.Threading.Thread( _
                   AddressOf ReceiveImageLoop)
                t.Start()

                '---change the text on the Button---
                CType(sender,Button).Text = "Stop"
            Catch ex As Exception
                Console.WriteLine(ex.ToString)
            End Try
        Else
            '---send message---
            SendMessage("Stop")
            t.Abort()

            '---change the text on the Button---
            CType(sender,Button).Text = "Start"
        End If
    End Sub
You first connect to the server using its IP address and port number. You then send a "Send" message to the server to indicate that you are ready to receive the image. You spin off a thread so that you can receive images (via the ReceiveImageLoop() subroutine) asynchronously.

The ReceiveImageLoop() subroutine calls the ReceiveImage() function indefinitely,until an error occurs:


    Private Sub ReceiveImageLoop()
        '---keep on receiving image until an error occurs---
        While ReceiveImage()
        End While

        '---display error message---
        MsgBox("Server has stopped responding. Please try" & _
           & " restarting the video.")
    End Sub
The ReceiveImage() function reads the incoming image data (in blocks of 8192 bytes,as defined by the ReceiveBufferSize property of the TcpClient class) sent from the server. As each image sent is 341504 bytes (defined by the SIZEOFIMAGE constant; this value is dependent on the web cam used),you will therefore read the number of bytes as expected. Once the image is received,display it in the PictureBox control. To receive the next image from the server,send another "Send" message:

    '---receive video image from server---
    Public Function ReceiveImage() As Boolean
        Dim s As New MemoryStream
        Try
            Dim nws As NetworkStream = client.GetStream
            Dim counter As Integer = 0
            Dim totalBytes As Integer = 0

            Do
                '---read the incoming data---
                Dim bytesRead As Integer = _
                   nws.Read(data,client.ReceiveBufferSize)
                totalBytes += bytesRead
                '---write the byte() array into the memory stream---
                s.Write(data,bytesRead)
                counter += 1
            Loop Until totalBytes >= SIZEOFIMAGE

            '---display the image in the PictureBox control---
            PictureBox1.Image = Image.FromStream(s)
        Catch ex As InvalidOperationException
            '---ignore this error---
            Console.WriteLine(ex.ToString)
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
            Return False
        End Try

        '---ask the server to send the next image---
        SendMessage("Send")
        Return True
    End Function
The SendMessage() subroutine sends a message to the server:

    '---Sends a message to the server---
    Private Sub SendMessage(ByVal message As String)
        '---adds a carriage return char---
        message += vbLf
        Try
            '---send the text---
            Dim ns As System.Net.Sockets.NetworkStream
            SyncLock client.GetStream
                ns = client.GetStream
                Dim bytesToSend As Byte() = _
                   System.Text.Encoding.ASCII.GetBytes(message)
                '---sends the text---
                ns.Write(bytesToSend,bytesToSend.Length)
            End SyncLock
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        End Try
    End Sub
When Form1 is closed,kill the thread that listens asynchronously for incoming image data:

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

Testing the Applications
You can test both the server and the client on one single machine. Simply set the IP_Address variable (on the server) to "127.0.0.1" and then press F5 in Visual Studio 2005 to test the application. For the client,type the server IP address (which is 127.0.0.1) and click the Start button. You should see the same image on both the client and the server (see Figure 5).

Figure 5. You can test the client and the server on the same machine.
You can also try the client and server on two separate machines. Just remember to set the server IP address accordingly. Best of all,you can connect multiple clients to the server!

Some Points to Note
Please note the following points when testing the client and the server:

  • The server application needs to be visible on screen. If it is minimized,the client will not be able to receive the image captured by the web cam. This is due to the fact that the application is capturing whatever images are shown on the screen.
  • For simplicity I have not added any security features into the server. In reality,you can add credentials information to the message sent to the server before the server sends the video image over to the client.
Imports System.Net.Sockets Imports System.IOPublic Class Form1 '---get own IP address--- Private ips As Net.IPHostEntry = _ Net.Dns.GetHostEntry(Net.Dns.GetHostName()) '---port nos and server IP address--- Const PORTNO As Integer = 500 Private server_IP As String = " 127.0.0.1" '---size of the video image--- Const SIZEOFIMAGE As Integer = 341504 '---use for connecting to the server--- Private client As TcpClient '--used for sending and receiving data--- Private data() As Byte '---used for receiving images from the server--- Private t As System.Threading.ThreadPrivate Sub btnStartStop_Click( _ ByVal sender As System.Object,Button).Text = "Start" End If End SubPrivate Sub ReceiveImageLoop() '---keep on receiving image until an error occurs--- While ReceiveImage() End While '---display error message--- MsgBox("Server has stopped responding. Please try" & _ & " restarting the video.") End Sub'---receive video image from server--- Public Function ReceiveImage() As Boolean Dim s As New MemoryStream Try Dim nws As NetworkStream = client.GetStream Dim counter As Integer = 0 Dim totalBytes As Integer = 0 Do '---read the incoming data--- Dim bytesRead As Integer = _ nws.Read(data,bytesRead) counter += 1 Loop Until totalBytes >= SIZEOFIMAGE '---display the image in the PictureBox control--- PictureBox1.Image = Image.FromStream(s) Catch ex As InvalidOperationException '---ignore this error--- Console.WriteLine(ex.ToString) Catch ex As Exception Console.WriteLine(ex.ToString) Return False End Try '---ask the server to send the next image--- SendMessage("Send") Return True End Function'---Sends a message to the server--- Private Sub SendMessage(ByVal message As String) '---adds a carriage return char--- message += vbLf Try '---send the text--- Dim ns As System.Net.Sockets.NetworkStream SyncLock client.GetStream ns = client.GetStream Dim bytesToSend As Byte() = _ System.Text.Encoding.ASCII.GetBytes(message) '---sends the text--- ns.Write(bytesToSend,bytesToSend.Length) End SyncLock Catch ex As Exception Console.WriteLine(ex.ToString) End Try End SubPrivate Sub Form1_FormClosing( _ ByVal sender As Object,_ ByVal e As System.Windows.Forms.FormClosingEventArgs) _ Handles Me.FormClosing t.Abort() End Sub

猜你在找的VB相关文章