本文是本系列三篇文章中的最後一篇,在前兩篇文章中,介紹了開發海康監控的方案及中轉服務器的實現,本篇文章介紹Web端的功能實現,經過本篇文章中的項目開發,我們就實現了最初的項目需求。 項目中需要海康官方播放器SDK,請各位根據項目需要下載相應版本。先來作者就帶著大家一步步來實現客戶端插件的開發。
播放器開發:
根據海康官方的SDK,我們要做的播放器其實只是將海康的播放庫引入到我們自己的項目中,然後接收來自中轉服務器的數據,最後播放顯示到客戶Web浏覽器上。其實整個就是一個引入了海康播放庫的ActiveX插件開發。下面按照開發流程一步步實現播放器插件開發。
新建C#類庫項目
將播放器SDK中文件引入到項目中
在項目中添加Winform用戶控件
新建安裝項目
發布項目
數字簽名
發布Web網站
關於C#開發ActiveX的詳細過程,園子裡有好多文章寫的非常詳細,我這裡不在累述,有不太了解的朋友,我這裡推薦一篇比較詳細的文章《ASP.NET C# 之 Activex用C#編寫ActiveX控件》。本篇文章寫播放器插件的核心功能。
首先來看SDK的C#調用的內容:
public class PlayCtrlSDK { #region 解碼庫 [DllImport("PlayCtrl.dll")] public static extern uint PlayM4_GetLastError(int nPort); [DllImport("PlayCtrl.dll")] public static extern int PlayM4_GetCaps(); /// <summary> /// 獲取未使用的通道號 /// </summary> /// <param name="nPort"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_GetPort(ref int nPort); /// <summary> /// 設置流播放模式 /// </summary> /// <param name="port"></param> /// <param name="mode"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_SetStreamOpenMode(int nPort, int mode); /// <summary> /// 打開流 /// </summary> /// <param name="nPort"></param> /// <param name="pFileHeadBuf"></param> /// <param name="nSize"></param> /// <param name="nBufPoolSize"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_OpenStream(int nPort, byte[] pFileHeadBuf, UInt32 nSize, uint nBufPoolSize); /// <summary> /// 設置播放緩沖區最大緩沖幀數 /// </summary> /// <param name="nPort"></param> /// <param name="nNum"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_SetDisplayBuf(int nPort, uint nBufPoolSize); /// <summary> /// 開啟播放 /// </summary> /// <param name="nPort"></param> /// <param name="hWnd"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_Play(int nPort, IntPtr hWnd); /// <summary> /// 開始倒放 /// </summary> /// <param name="nPort"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_ReversePlay(int nPort); /// <summary> /// 輸入流數據 /// </summary> /// <param name="nPort"></param> /// <param name="pBuf"></param> /// <param name="nSize"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_InputData(int nPort, byte[] pBuf, uint nSize); /// <summary> /// 關閉播放 /// </summary> /// <param name="nPort"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_Stop(int nPort); /// <summary> /// 關閉流 /// </summary> /// <param name="nPort"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_CloseStream(int nPort); /// <summary> /// 釋放已使用的通道號 /// </summary> /// <param name="nPort"></param> /// <returns></returns> [DllImport("PlayCtrl.dll")] public static extern bool PlayM4_FreePort(int nPort); #endregion }
本欄目
根據SDK文檔內的說明,這幾個函數的調用順序如下:
下面就該組織我們自己的程序接收來自中轉服務器的數據,並將這些數據交給播放庫的方法來處理。
[Guid("A4277AC0-7F3F-4950-9130-27025D6E18F8")] public partial class Player : UserControl, IObjectSafety { int prot = -1; static Socket socket; bool isPlayInit = false; IPEndPoint iep; Thread thread; #region Web頁面調用的接口 /// <summary> /// 釋放Socket連接和SDK播放庫資源 /// </summary> public void PageClose() { isPlayInit = false; if (thread != null && thread.IsAlive) { thread.Abort(); } if (socket != null && socket.Connected) { socket.Shutdown(SocketShutdown.Both); socket.Close(); socket = null; } if (prot != -1) { PlayCtrlSDK.PlayM4_CloseStream(prot); } } //插件初始化 public Player() { InitializeComponent(); } /// <summary> /// 初始化網絡連接 /// </summary> /// <param name="ip">中轉服務器IP</param> /// <param name="iPprot">中轉服務器端口</param> public void InitPlay(string ip, int iPprot) { PageClose(); iep = new IPEndPoint(IPAddress.Parse(ip), iPprot); socket = new Socket(iep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); thread = new Thread((object o) => { socket.Connect(iep); byte[] bt = new byte[262144]; while (true) { int count = socket.Receive(bt); StreamType st = count == 40 ? StreamType.Head : StreamType.Body; setPlay(bt, (uint)count, st); Thread.Sleep(100); } }); thread.IsBackground = true; } /// <summary> /// 開始播放 /// </summary> /// <returns></returns> public bool ShowVideo() { thread.Start(); return true; } #endregion /// <summary> /// 調用SDK播放庫 /// </summary> /// <param name="buffer">從中轉服務器接收到的數據</param> /// <param name="size">中轉服務器發送的數據大小</param> /// <param name="type">數據類型</param> private void setPlay(byte[] buffer, uint size, StreamType type) { switch (type) { case StreamType.Head: if (size > 40) { return; } if (!PlayCtrlSDK.PlayM4_GetPort(ref prot)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); break; } if (!PlayCtrlSDK.PlayM4_SetStreamOpenMode(prot, 0)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); break; } if (!PlayCtrlSDK.PlayM4_OpenStream(prot, buffer, size, 1024 * 1024)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); break; } if (!PlayCtrlSDK.PlayM4_SetDisplayBuf(prot, 15)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); break; } this.Invoke(new MethodInvoker(delegate { //使用PictureBox控件句柄作為參數傳給播放器,播放器將其作為容器進行圖像渲染 if (!PlayCtrlSDK.PlayM4_Play(prot, this.pictureBox1.Handle)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); } })); isPlayInit = true; break; case StreamType.Body: if (isPlayInit && !PlayCtrlSDK.PlayM4_InputData(prot, buffer, size)) { PageClose(); showError(PlayCtrlSDK.PlayM4_GetLastError(prot)); } break; } } /// <summary> /// 錯誤提示 /// </summary> /// <param name="p"></param> private void showError(uint p) { MessageBox.Show(p.ToString()); } #region IObjectSafety接口方法 public void GetInterfacceSafyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions) { pdwEnabledOptions = 2; pdwSupportedOptions = 1; } public void SetInterfaceSafetyOptions(int riid, int dwOptionsSetMask, int dwEnabledOptions) { } #endregion enum StreamType { Head, Body } }
如上文所提到的,PlayCtrlSDK.PlayM4_OpenStream(int nPort, byte[] pFileHeadBuf, UInt32 nSize, uint nBufPoolSize);方法必須以中轉服務器發送的長度為40個字節的數據作為形參pFileHeadBuf的實參傳入,否則播放器無法正常工作。另外,InitPlay(string ip, int iPprot)用JS直接調用,講實參傳入即可,利於中轉服務調整和擴展。
到這裡,Web浏覽器插件的內容介紹完了。最後直接貼出Web網站的內容給大家,相信一看就明白,真的不用不用說什麼了吧。。。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>視頻播放</title> <style type="text/css"> * { padding: 0; margin: 0; } body { background: #000; } a { display: block; text-decoration: none; line-height: 40px; color: #fff; width: 293px; height: 40px; text-align: center; background: #333; } a:hover { color: #000; background: #eee; } </style> <script type="text/javascript" language="javascript"> var channel = 1; window.onbeforeunload = function () { player.PageClose(); var result = getServer("unload"); }; function getServer(operate) { var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } //說明: //******這裡的方法是為取得視頻中轉服務器的IP以及端口*************** xmlhttp.open("GET", "ServerHandler.ashx?channel=" + channel + "&operate=" + operate, false); xmlhttp.send(); return xmlhttp.responseText; } function Show(ch) { getServer("unload"); var ctrls = document.getElementsByTagName("a"); for (var i = 0; i < ctrls.length; i++) { ctrls[i].disabled = ""; } channel = ch; var host = getServer("load").split(':'); document.getElementById("errorDiv").innerHTML = "IP:" + host[0] + " Port:" + host[1] + " Count:" + host[2]; player.PageClose(); player.InitPlay(host[0], host[1]); var result = player.ShowVideo(); document.getElementById("but" + channel).disabled = result; } function refreshSession() { getServer("refresh"); } setInterval("refreshSession()", 3000); </script> </head> <body> <table width="600" align="center"> <tr> <td colspan="2" align="center"> <object id="player" classid="clsid:A4277AC0-7F3F-4950-9130-27025D6E18F8" width="600" height="400" codebase="Setup.exe"> </object> </td> </tr> <tr> <td height="40" width="300" align="center"> <a href="#" onclick="Show(1)" id="but1">視頻1</a> </td> <td width="300" align="center"> <a href="#" onclick="Show(2)" id="but2">視頻2</a> </td> </tr> <tr> <td colspan="2"> <div id="errorDiv"> </div> </td> </tr> </table> <script language="javascript"> Show(1); </script> </body> </html>
至此,關於海康威視視頻中轉系統的所有內容都已經介紹完了,希望本人的文章能給您帶來一點幫助。