基于C#局域網(wǎng)視頻聊天設計_第1頁
基于C#局域網(wǎng)視頻聊天設計_第2頁
基于C#局域網(wǎng)視頻聊天設計_第3頁
基于C#局域網(wǎng)視頻聊天設計_第4頁
基于C#局域網(wǎng)視頻聊天設計_第5頁
已閱讀5頁,還剩30頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、1緒論視頻監(jiān)控是各行業(yè)重點部門或重要場所進行實時監(jiān)控的物理基礎,管理部門可通過它獲得有效數(shù)據(jù)、圖像或聲音信息,對突發(fā)性異常事件的過程進行及時的監(jiān)視和記憶,用以提供高效、及時地指揮和高度、布置警力、處理案件等。本系統(tǒng)采用DirectShow網(wǎng)絡組播技術實現(xiàn)了視頻捕獲、視頻壓縮、網(wǎng)絡傳輸、視頻解碼和實時回放,減小了網(wǎng)絡帶寬占用,高效的傳輸視頻數(shù)據(jù),獨立于硬件??蓴U展性好。完全利用現(xiàn)有1P數(shù)據(jù)網(wǎng)絡傳輸數(shù)據(jù)不需要單獨布線,顯著降低了系統(tǒng)成本,縮短了系統(tǒng)開發(fā)周期,并且可以容易的實現(xiàn)遠程監(jiān)測2 Windows服務一個Windows服務程序是在Windows操作系統(tǒng)下能完成特定功能的可執(zhí)行的應用程序。Win

2、dows服務程序雖然是可執(zhí)行的,但是它不像一般的可執(zhí)行文件通過雙擊就能開始運行了,它必須有特定的啟動方式。這些啟動方式包括了自動啟動和手動啟動兩種。對于自動啟動的Windows服務程序,它們在Windows啟動或是重啟之后用戶登錄之前就開始執(zhí)行了。而對于手動啟動的Windows服務程序,你可以通過命令行工具的NET START 命令來啟動它,或是通過控制面板中管理工具下的服務一項來啟動相應的Windows服務程序。同樣,一個Windows服務程序也不能像一般的應用程序那樣被終止。因為Windows服務程序一般是沒有用戶界面的,所以你也要通過命令行工具或是下面圖中的工具來停止它,或是在系統(tǒng)關閉時

3、使得Windows服務程序自動停止。因為Windows服務程序沒有用戶界面,為了能使一個Windows服務程序能夠正常并有效的在系統(tǒng)環(huán)境下工作,程序員必須實現(xiàn)一系列的方法來完成其服務功能。Windows服務程序的應用范圍很廣,典型的Windows服務程序包含了硬件控制、應用程序監(jiān)視、系統(tǒng)級應用、診斷、報告、Web和文件系統(tǒng)服務等功能。2.1 添加文件監(jiān)視服務將生成的服務名為Webcamservice的服務添加視頻監(jiān)視功能:Ø 首先,在C盤創(chuàng)建文件夾wØ 將程序生成的debug中的文件復制到w文件夾Ø 在C/windows/搜索installutil.exe執(zhí)行文件

4、,將其復制到w文件夾Ø 啟動cmd,打開命令提示符窗體鍵入如圖2-1所示 圖2-1 添加服務功能Ø 用net start命令啟動服務Webcamservice如圖2-2所示 圖2-2啟動Webcamservice3項目的設計原理3.1 DirectShow技術采用網(wǎng)絡攝像機的遠程視頻監(jiān)控具有錄像時間長、圖像質量好、查詢速度快等優(yōu)點,目前應用非常廣泛。對于網(wǎng)絡攝像機傳輸?shù)囊曨l數(shù)據(jù),需要專門的Filter 來處理并在DirectShow 的框架下或回放,或保存。監(jiān)控服務器通過Internet/Intranet 輪詢網(wǎng)絡攝像機獲取視頻。本文以視頻數(shù)據(jù)接收Filter 的設計過程介

5、紹基于DirectShow 的視頻數(shù)據(jù)流的傳輸以及通過自定義的通訊協(xié)議的數(shù)據(jù)解析過程和Filter 程序設計與實現(xiàn)過程。DirectShow1是微軟公司在ActiveMovie 和Video for Windows 的基礎上推出的基于COM 的流媒體處理的開發(fā)包,與DirectX 開發(fā)包一起發(fā)布。DirectShow 為多媒體流的捕捉和回放以及二次開發(fā)提供了強有力的支持。運用DirectShow,可以很方便地從支持WDM 驅動模型的采集卡上采集數(shù)據(jù),并且調用其API 函數(shù)進行后期處理。它廣泛地支持各種媒體格式,包括Asf,Mpeg,Avi,Dv,Mp3,Wave等等,使得多媒體數(shù)據(jù)的回放變得輕

6、而易舉。DirectShow 是一個開放的框架,因此只要有合適的Filter 來分析和解碼,可以支持任何格式。3.2 TCP/IP協(xié)議在TCP/IP協(xié)議組分兩種協(xié)議:網(wǎng)絡層的協(xié)議,應用層的協(xié)議Ø 網(wǎng)絡層協(xié)議 網(wǎng)絡層協(xié)議管理離散的計算機間的數(shù)據(jù)傳輸。這些協(xié)議是在系統(tǒng)表層以下工作的。比如,IP協(xié)議為用戶和遠程計算機提供了信息包的傳輸方法。它是在許多信息的基礎上工作的,好比說是機器的IP地址。在機器IP地址和其它信息的基礎上,IP確保信息包能正確地到達目的機器。通過這一過程,IP和其它網(wǎng)絡層的協(xié)議共同用于數(shù)據(jù)傳輸。如果沒有網(wǎng)絡工具,用戶就看不到在系統(tǒng)里工作的IP。Ø 應用層協(xié)議相

7、反地,應用層協(xié)議用戶是可以看得到的。比如,文件傳輸協(xié)議(FTP)用戶是看得到的。用戶為了傳輸一個文件請求一個和其它計算機的連接,連接建立后,就開始傳輸文件。在傳輸時,用戶和遠程計算機的交換的一部分是能看到的。Ø IPIP層接收由更低層(網(wǎng)絡接口層例如以太網(wǎng)設備驅動程序)發(fā)來的數(shù)據(jù)包,并把該數(shù)據(jù)包發(fā)送到更高層-TCP或UDP層;相反,IP層也把從TCP或UDP層接收來的數(shù)據(jù)包傳送到更低層。IP數(shù)據(jù)包是不可靠的,因為IP并沒有做任何事情來確認數(shù)據(jù)包是按順序發(fā)送的或者沒有被破壞。IP數(shù)據(jù)包中含有發(fā)送它的主機的地址(源地址)和接收它的主機的地址(目的地址)。高層的TCP和UDP服務在接收數(shù)據(jù)

8、包時,通常假設包中的源地址是有效的。也可以這樣說,IP地址形成了許多服務的認證基礎,這些服務相信數(shù)據(jù)包是從一個有效的主機發(fā)送來的。Ø TCP如果IP數(shù)據(jù)包中有已經封好的TCP數(shù)據(jù)包,那么IP將把它們向上傳送到TCP層。TCP將包排序并進行錯誤檢查,同時實現(xiàn)虛電路間的連接。TCP數(shù)據(jù)包中包括序號和確認,所以未按照順序收到的包可以被排序,而損壞的包可以被重傳。TCP將它的信息送到更高層的應用程序,例如Telnet的服務程序和客戶程序。應用程序輪流將信息送回TCP層,TCP層便將它們向下傳送到IP層,設備驅動程序和物理介質,最后到接收方。3.3 C/S架構服務器客戶端客戶端 . . .在網(wǎng)

9、絡連接模式中除對等網(wǎng)外,還有另一種形式的網(wǎng)絡,即客戶機/服務器網(wǎng),Client/Server。在客戶機/服務器網(wǎng)絡中,服務器是網(wǎng)絡的核心,而客戶機是網(wǎng)絡的基礎,客戶機依靠服務器獲得所需要的網(wǎng)絡資源,而服務器為客戶機提供網(wǎng)絡必須的資源。 圖3-1 c/s結構4程序流程圖及設計4.1程序時序圖與系統(tǒng)架構本系統(tǒng)采用面向連接的客戶/服務模型,服務器必須首先啟動,否則客戶進程的Connect()系統(tǒng)調用將返回錯誤代碼表示連接失敗。無連接的服務進程也必須首先啟動以指定本地的套接字地址否則客戶進程的數(shù)據(jù)服務請求傳送不到服務器進程。面向連接的c/s時序圖如圖4-1所示Accept()Recv()Send()C

10、lose()Listen()Bind()Socket()服務器發(fā)送數(shù)據(jù)<-<-確認發(fā)送數(shù)據(jù)<-請求建立連接Recv()Close()Send()Connect()Socket()客戶 圖4-1 程序時序圖系統(tǒng)由服務器終端采集傳輸系統(tǒng)和客戶端接收系統(tǒng)兩部分組,系統(tǒng)構架如圖4-2所示攝像頭采集卡服務器服務程序網(wǎng)絡傳輸模塊網(wǎng)絡接收模塊客戶端程序解碼顯示視頻流控制指令視頻流控制指令 圖4-2 系統(tǒng)架構4.2程序設計分析4.2.1任務目標服務器端程序目標:服務器服務器端服務程序進行數(shù)據(jù)采集(捕捉攝像頭捕獲數(shù)據(jù)),提供IP端口實現(xiàn)數(shù)據(jù)流的傳輸??蛻舳顺绦蚰繕耍嚎蛻舳顺绦蛲ㄟ^IP協(xié)議與服務

11、器端通信,接收并回放服務器端采集的視頻數(shù)據(jù)流。4.2.2程序描述Socket類:socket之間的連接可以分為三種類型:客戶端連接,監(jiān)聽連接以及服務器端連接。 客戶端連接是指由客戶端的socket提出連接請求,要連接的目標是服務器端的socket。為此,客戶端的socket必須首先描述它要連接的服務器端socket(主要是指服務器 端socket的地址和端口號),然后再定位所要連接的服務器端socket,找到以后,就向服務器端 socket請求連接。當然,服務器端的socket此時未必正好處于準備好狀態(tài),不過,服務器端的 socket會自動維護客戶請求連接的隊列,然后在它認為合適的時候向客戶端

12、socket發(fā)出"允許連接" (accept)的信號,這時客戶端socket與服務器端socket的連接就建立了。監(jiān)聽連接,服務器端 socket并不定位具體的客戶端socket,而是處于等待連接的狀態(tài)。當服務器端socket監(jiān)聽到或者說 接收到客戶端socket的連接請求,它就響應客戶端socket的請求建立一個新的socket句柄并與客戶 端連接,而服務器端socket繼續(xù)處于監(jiān)聽狀態(tài),還可以接收其它客戶端socket的連接請求。服務器端連接,是指當服務器端socket接收到客戶端socket的連接請求后,就把服務器端socket的描述發(fā)給客戶端,一旦客戶端確認了此描述

13、,連接就建立了。在本文中的聊天程序用的就是監(jiān)聽連接,即服務器設置連接個數(shù)后進行監(jiān)聽,客戶端進行對服務器端的連接,這樣就可以進行相互通信了。TcpService類namespace TCP internal class TcpServer : IDisposable / This is not the max number of connections you can have, it's the number / that can queue up waiting for you to Accept them. If more than MAXCONNECTION / more cli

14、ents try to connect while you are servicing another, OnConnect is / probably taking too long. const int MAXCONNECTIONS = 3; #region Member variables private ArrayList m_aryClients; private Socket m_sockListener; private volatile bool m_bShuttingDown; private ManualResetEvent ShutDownReady; #endregio

15、n / Return an array of the ip addresses assigned to this pc public static IPAddress GetAddresses() IPAddress aryLocalAddr = null; string strHostName = "" / NOTE: DNS lookups are nice and all but quite time consuming. strHostName = Dns.GetHostName();#if USING_NET11 IPHostEntry ipEntry = Dns

16、.GetHostByName( strHostName );#else IPHostEntry ipEntry = Dns.GetHostEntry( strHostName );#endif aryLocalAddr = ipEntry.AddressList; / Verify we got an IP address. if( aryLocalAddr = null | aryLocalAddr.Length < 1 ) throw new Exception( "Unable to get local address" ); return aryLocalAd

17、dr; public TcpServer(int nPortListen) _TcpServer(nPortListen, GetAddresses()0); public TcpServer(int nPortListen, IPAddress ip) _TcpServer(nPortListen, ip); / Shut down the listener public void Dispose() / Shutting down is a real PITA. You can't close the listener / while there is an outstanding

18、 async call active. And you can't / cancel the async call. Grr. As a workaround, this routine / makes a connection to the port. The OnConnect routine, recognizing / that we are in shutdown, doesn't create a new async call. TcpClient t = null; / Only want one thread to be preforming shutdown

19、at a time. lock (this) / Have we already shutdown? if (!m_bShuttingDown) m_bShuttingDown = true; / Disconnect each client foreach (SockWrapper s in m_aryClients) try s.Client.Shutdown(SocketShutdown.Both); s.Client.Close(); catch m_aryClients = null; / Connect to the port to trigger the async listen

20、er IPEndPoint ep = (IPEndPoint)m_sockListener.LocalEndPoint; t = new TcpClient(ep.Address.ToString(), ep.Port); if (t != null) / Listen for the async listener to let go. This must be done / outside the crit section since the listener needs to lock it. ShutDownReady.WaitOne(3000, false); lock (this)

21、/ close everything down m_sockListener.Close(); m_sockListener = null; t.Close(); ShutDownReady.Close(); ShutDownReady = null; TcpServer() / If Dispose is not called against our class and the destructor is / called, some of the member variables in this class have already / been disposed. Such being

22、the case, there's no way to clean up / nicely. Moral: Always call Dispose. /Dispose(); / Send to all connected clients public void SendToAll(MemoryStream m) _SendToAll(m.GetBuffer(), (int)m.Length); public void SendToAll(byte b) _SendToAll(b, b.Length); public int Connections get return m_aryCli

23、ents.Count; public event TcpConnected Connected; public event TcpConnected Disconnected; public event TcpReceive DataReceived; public event TcpSend Send; private void _TcpServer(int nPortListen, IPAddress ip) try / Initialize member vars m_aryClients = new ArrayList(5); ShutDownReady = new ManualRes

24、etEvent(false); m_bShuttingDown = false; / Create the listener socket in this machine's IP address m_sockListener = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); m_sockListener.Bind( new IPEndPoint( ip, nPortListen ) ); m_sockListener.Listen( MAXCONNECTIONS ); /

25、Setup a callback to be notified of connection requests m_sockListener.BeginAccept( new AsyncCallback( OnConnectRequest ), m_sockListener ); catch m_bShuttingDown = true; throw; private void _SendToAll(byte b, int iLength) lock (this) if (!m_bShuttingDown) foreach (SockWrapper s in m_aryClients) _Sen

26、dOne(s, b, iLength); private void _SendOne(SockWrapper s, byte b, int iLength) try bool bSend = true; if (Send != null) Send(this, ref s.obj, ref bSend); if (bSend) s.Client.Send(b, iLength, SocketFlags.None); catch / Ignore the error. If the client is dead, OnReceiveData / will be called to close t

27、he connection. I would remove it / anyway, except bad things happen if you remove an entry / from a list while using foreach. / Client has connected private void OnConnectRequest( IAsyncResult ar ) / Get the listener and client Socket listener = (Socket)ar.AsyncState; Socket client = listener.EndAcc

28、ept( ar ); lock (this) if (!m_bShuttingDown) / Wrap the client and add it to the array SockWrapper s = new SockWrapper(client); m_aryClients.Add( s ); / Fire the Connected event if (Connected != null) Connected(this, ref s.obj); / Set up an async wait for packets from the client AsyncCallback receiv

29、eData = new AsyncCallback( OnReceivedData ); s.Client.BeginReceive( s.byBuff, 0, s.byBuff.Length, SocketFlags.None, receiveData, s ); / (Re)Setup a callback to be notified of connection requests listener.BeginAccept(new AsyncCallback( OnConnectRequest ) , listener ); else / If we are in shutdown mod

30、e, DON'T add / the connection to the array, DON'T setup / the async listen, and DO set the event / to say we are done. ShutDownReady.Set(); / Client has sent data, or has disconnected private void OnReceivedData( IAsyncResult ar ) / Socket was the passed in object SockWrapper s = (SockWrappe

31、r)ar.AsyncState; lock (this) if (!m_bShuttingDown) / Check if we got any data try int nBytesRec = s.Client.EndReceive( ar ); if( nBytesRec > 0 ) if (DataReceived != null) DataReceived(this, ref s.obj, ref s.byBuff, nBytesRec); / Restablish the callback AsyncCallback receiveData = new AsyncCallbac

32、k( OnReceivedData ); s.Client.BeginReceive( s.byBuff, 0, s.byBuff.Length, SocketFlags.None, receiveData, s ); else / If no data was received then the connection is probably dead RemoveConnection(s); catch RemoveConnection(s); / Remove a connection from the list of active connections private void Rem

33、oveConnection(SockWrapper s) try s.Client.Shutdown( SocketShutdown.Both ); s.Client.Close(); catch / Remove it from the array try m_aryClients.Remove( s ); catch / Fire the Disconnected event if (Disconnected != null) Disconnected(this, ref s.obj); / Wrapper for each client (stored in m_aryClients)

34、internal class SockWrapper / The buffer is used by receive public Socket Client; public byte byBuff; public object obj; public SockWrapper(Socket client) Client = client; byBuff = new byte256; obj = new object(); public delegate void TcpConnected(Object sender, ref object o); public delegate void Tc

35、pSend(Object sender, ref object o, ref bool b); public delegate void TcpReceive(Object sender, ref object o, ref byte b, int ByteCount);WebCamService 類:namespace WebCamService public class WebCamService : ServiceBase #region Required Service Related Methods private System.ComponentModel.Container co

36、mponents = null; public WebCamService() InitializeComponent(); private void InitializeComponent() components = new System.ComponentModel.Container(); this.ServiceName = "WebCamService" protected override void Dispose( bool disposing ) if( disposing ) if (components != null) components.Disp

37、ose(); base.Dispose( disposing ); #endregion STAThread public static void Main(string args) Thread.CurrentThread.Name = "Main thread" WebCamService ServiceToRun = new WebCamService() ; if ( Debugger.IsAttached ) ServiceToRun.Run(); else ServiceToRun.CanPauseAndContinue = false; ServiceBase

38、.Run(ServiceToRun); #region Member Variables private const int MAXOUTSTANDINGPACKETS = 3; / <summary> / The thread will run the job. / The job is the Method Run() below / </summary> protected Thread thread = null; private ManualResetEvent ConnectionReady; private volatile bool bShutDown;

39、 private volatile int iConnectionCount; #endregion / <summary> / Set things in motion so your service can do its work. / 為了服務器可以工作而設置的選項 / </summary> protected override void OnStart(string args) ThreadStart starter = new ThreadStart(Run);/實例化進程 thread = new Thread(starter); thread.Start(

40、); / <summary> / Stop this service. / 停止服務 / The Run() Method tests for this thread state each second / 每秒都為這個進程啟動方法測試 / </summary> protected override void OnStop() / Set exit condition /設置退出狀態(tài) bShutDown = true; / Need to get out of wait /需要退出等待 ConnectionReady.Set(); public void Run() const int VIDEODEVICE = 0; / zero based index of video capture device to use const int FRAMERATE = 15; / Depends on video device caps. Generally 4-30. const int VIDEOWIDTH = 640; / Depends on video device caps con

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論