声明:本系列文章只提供交流与学习使用。文章中所有涉及到海康威视设备的SDK均可在海康威视官方网站下载得到。文章中所有除官方SDK以为的代码均可随意使用,任何涉及到海康威视公司利益的非正常使用由使用者自己负责,与本人无关。
前言:
本文是本系列三篇文章中的最后一篇,在前两篇文章中,介绍了开发海康监控的方案及中转服务器的实现,本篇文章介绍Web端的功能实现,经过本篇文章中的项目开发,我们就实现了最初的项目需求。 项目中需要海康官方播放器SDK,请各位根据项目需要下载相应版本。先来作者就带着大家一步步来实现客户端插件的开发。
播放器开发:
根据海康官方的SDK,我们要做的播放器其实只是将海康的播放库引入到我们自己的项目中,然后接收来自中转服务器的数据,最后播放显示到客户Web浏览器上。其实整个就是一个引入了海康播放库的ActiveX插件开发。下面按照开发流程一步步实现播放器插件开发。
- 新建C#类库项目
- 将播放器SDK中文件引入到项目中
- 在项目中添加Winform用户控件
- 新建安装项目
- 发布项目
- 数字签名
- 发布Web网站
关于C#开发ActiveX的详细过程,园子里有好多文章写的非常详细,我这里不在累述,有不太了解的朋友,我这里推荐一篇比较详细的文章《ASP.NET C# 之 Activex用C#编写ActiveX控件》。本篇文章写播放器插件的核心功能。
首先来看SDK的C#调用的内容:
1 public class PlayCtrlSDK 2 { 3 #region 解码库 4 [DllImport("PlayCtrl.dll")] 5 public static extern uint PlayM4_GetLastError(int nPort); 6 [DllImport("PlayCtrl.dll")] 7 public static extern int PlayM4_GetCaps(); 8 /// <summary> 9 /// 获取未使用的通道号 10 /// </summary> 11 /// <param name="nPort"></param> 12 /// <returns></returns> 13 [DllImport("PlayCtrl.dll")] 14 public static extern bool PlayM4_GetPort(ref int nPort); 15 /// <summary> 16 /// 设置流播放模式 17 /// </summary> 18 /// <param name="port"></param> 19 /// <param name="mode"></param> 20 /// <returns></returns> 21 [DllImport("PlayCtrl.dll")] 22 public static extern bool PlayM4_SetStreamOpenMode(int nPort, int mode); 23 /// <summary> 24 /// 打开流 25 /// </summary> 26 /// <param name="nPort"></param> 27 /// <param name="pFileHeadBuf"></param> 28 /// <param name="nSize"></param> 29 /// <param name="nBufPoolSize"></param> 30 /// <returns></returns> 31 [DllImport("PlayCtrl.dll")] 32 public static extern bool PlayM4_OpenStream(int nPort, byte[] pFileHeadBuf, UInt32 nSize, uint nBufPoolSize); 33 /// <summary> 34 /// 设置播放缓冲区最大缓冲帧数 35 /// </summary> 36 /// <param name="nPort"></param> 37 /// <param name="nNum"></param> 38 /// <returns></returns> 39 [DllImport("PlayCtrl.dll")] 40 public static extern bool PlayM4_SetDisplayBuf(int nPort, uint nBufPoolSize); 41 /// <summary> 42 /// 开启播放 43 /// </summary> 44 /// <param name="nPort"></param> 45 /// <param name="hWnd"></param> 46 /// <returns></returns> 47 [DllImport("PlayCtrl.dll")] 48 public static extern bool PlayM4_Play(int nPort, IntPtr hWnd); 49 /// <summary> 50 /// 开始倒放 51 /// </summary> 52 /// <param name="nPort"></param> 53 /// <returns></returns> 54 [DllImport("PlayCtrl.dll")] 55 public static extern bool PlayM4_ReversePlay(int nPort); 56 /// <summary> 57 /// 输入流数据 58 /// </summary> 59 /// <param name="nPort"></param> 60 /// <param name="pBuf"></param> 61 /// <param name="nSize"></param> 62 /// <returns></returns> 63 [DllImport("PlayCtrl.dll")] 64 public static extern bool PlayM4_InputData(int nPort, byte[] pBuf, uint nSize); 65 /// <summary> 66 /// 关闭播放 67 /// </summary> 68 /// <param name="nPort"></param> 69 /// <returns></returns> 70 [DllImport("PlayCtrl.dll")] 71 public static extern bool PlayM4_Stop(int nPort); 72 /// <summary> 73 /// 关闭流 74 /// </summary> 75 /// <param name="nPort"></param> 76 /// <returns></returns> 77 [DllImport("PlayCtrl.dll")] 78 public static extern bool PlayM4_CloseStream(int nPort); 79 /// <summary> 80 /// 释放已使用的通道号 81 /// </summary> 82 /// <param name="nPort"></param> 83 /// <returns></returns> 84 [DllImport("PlayCtrl.dll")] 85 public static extern bool PlayM4_FreePort(int nPort); 86 87 #endregion 88 }
根据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网站的内容给大家,相信一看就明白,真的不用不用说什么了吧。。。
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <title>视频播放</title> 5 <style type="text/css"> 6 * 7 { 8 padding: 0; 9 margin: 0; 10 } 11 body 12 { 13 background: #000; 14 } 15 a 16 { 17 display: block; 18 text-decoration: none; 19 line-height: 40px; 20 color: #fff; 21 width: 293px; 22 height: 40px; 23 text-align: center; 24 background: #333; 25 } 26 a:hover 27 { 28 color: #000; 29 background: #eee; 30 } 31 </style> 32 <script type="text/javascript" language="javascript"> 33 var channel = 1; 34 window.onbeforeunload = function () { 35 player.PageClose(); 36 var result = getServer("unload"); 37 }; 38 39 function getServer(operate) { 40 var xmlhttp; 41 if (window.XMLHttpRequest) { 42 xmlhttp = new XMLHttpRequest(); 43 } 44 else { 45 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 46 } 47 //说明: 48 //******这里的方法是为取得视频中转服务器的IP以及端口*************** 49 xmlhttp.open("GET", "ServerHandler.ashx?channel=" + channel + "&operate=" + operate, false); 50 51 xmlhttp.send(); 52 return xmlhttp.responseText; 53 } 54 55 function Show(ch) { 56 getServer("unload"); 57 var ctrls = document.getElementsByTagName("a"); 58 for (var i = 0; i < ctrls.length; i++) { 59 ctrls[i].disabled = ""; 60 } 61 channel = ch; 62 var host = getServer("load").split(':'); 63 document.getElementById("errorDiv").innerHTML = "IP:" + host[0] + " Port:" + host[1] + " Count:" + host[2]; 64 player.PageClose(); 65 player.InitPlay(host[0], host[1]); 66 var result = player.ShowVideo(); 67 document.getElementById("but" + channel).disabled = result; 68 } 69 70 function refreshSession() { 71 getServer("refresh"); 72 } 73 74 setInterval("refreshSession()", 3000); 75 </script> 76 </head> 77 <body> 78 <table width="600" align="center"> 79 <tr> 80 <td colspan="2" align="center"> 81 <object id="player" classid="clsid:A4277AC0-7F3F-4950-9130-27025D6E18F8" width="600" 82 height="400" codebase="Setup.exe"> 83 </object> 84 </td> 85 </tr> 86 <tr> 87 <td height="40" width="300" align="center"> 88 <a href="#" onclick="Show(1)" id="but1">视频1</a> 89 </td> 90 <td width="300" align="center"> 91 <a href="#" onclick="Show(2)" id="but2">视频2</a> 92 </td> 93 </tr> 94 <tr> 95 <td colspan="2"> 96 <div id="errorDiv"> 97 </div> 98 </td> 99 </tr> 100 </table> 101 <script language="javascript"> 102 Show(1); 103 </script> 104 </body> 105 </html>
至此,关于海康威视视频中转系统的所有内容都已经介绍完了,希望本人的文章能给您带来一点帮助。如果您还有任何问题,非常欢迎发送邮件到maidi-mao@163.com进行交流。
另外也非常欢迎大家加入QQ群:107943411来交流学习。