声明:本系列文章只提供交流与学习使用。文章中所有涉及到海康威视设备的SDK均可在海康威视官方网站下载得到。文章中所有除官方SDK意外的代码均可随意使用,任何涉及到海康威视公司利益的非正常使用由使用者自己负责,与本人无关。
前言:
上一篇文章《海康威视频监控设备Web查看系统(一):概要篇》笼统的介绍了关于海康视频中转方案的思路,本文将一步步实现方案中的视频中转服务端。文中会涉及到一些.net socket处理和基础的多线程操作。我用的是SDK版本是SDK_Win32_V4.2.8.1 。大家根据自己实际情况想在相应的SDK,页面的说明里有详细的设备型号列表。
分析官方SDK的Demo:
首先来看看官方SDK中的C#版本的Demo,官方Demo分为两个版本,分别是“实时预览示例代码一”和“实时预览示例代码二”,因为有现成的C#版本,所以我们使用示例代码一中的内容。首先关注名为CHCNetSDK的类,这个类封中装了SDK中的所有非托管方法接口,我们需要来把这个类以及SDK中的DLL文件一起引入到我们的项目中,如果有对C#调用C++类库不了解的朋友请自己Google一下,资料非常多,博客园里也有很多作者写过这一类的文章,本文就不就这个内容做深入讨论。
调用SDK没有问题了,接下来看看SDK的使用,根据SDK使用文档,SDK接口的调用需要通过一个标准流程,流程图如下:
按照这个流程,我们第一步要做的是初始化SDK,然后是三个可选回调函数的设置,接着要做用户注册设备即设备登录,紧接着就是核心的部分了,根据上一篇文章中讲的思路,除了预览模块外其他几个模块的调用不在我们要解决的问题范畴,因此不予考虑。最后一步是注销设备,释放SDK资源。所以,最后根据我们的需求,流程简化如下:
虽然标准流程如此,但是我们的服务端程序只有一个单一的任务,所以也没有必要对为托管资源进行释放,因为如果退出程序以后资源就会释放,不退出程序的话,SDK资源就不应该被释放。因此再简化一下流程每个节点都有相应的代码实现如如下所示:
1 //初始化SDK 2 CHCNetSDK.NET_DVR_Init(); 3 4 //用户登录 5 CHCNetSDK.NET_DVR_DEVICEINFO_V30 DeviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30(); 6 CHCNetSDK.NET_DVR_Login_V30(设备ip地址, 设备端口, 用户名, 密码, ref DeviceInfo); 7 //说明:关于设备IP、端口、用户名及密码信息请根据自己要访问设备的设置正确填写 8 9 //预览模块10 CHCNetSDK.NET_DVR_CLIENTINFO lpClientInfo = new CHCNetSDK.NET_DVR_CLIENTINFO();11 lpClientInfo.lChannel = channel;12 lpClientInfo.lLinkMode = 0x0000;13 lpClientInfo.sMultiCastIP = "";14 m_fRealData = new CHCNetSDK.REALDATACALLBACK(RealDataCallBack);15 IntPtr pUser = new IntPtr();16 CHCNetSDK.NET_DVR_RealPlay_V30(m_lUserID, ref lpClientInfo, m_fRealData, pUser, 1);17 //说明:这里的NET_DVR_CLIENTINFO类中缺少预览窗口的句柄,需要预览时,要根据自己的项目设置NET_DVR_CLIENTINFO对象的hPlayWnd属性
可能有朋友看到这里已经忍受不了了,说好的视频中转功能在哪呢?别着急,一切的处理都在回调函数RealDataCallBack中,先耐心看一下这个回调函数的签名
void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
第一个lRealHandle是预览控件的句柄,第二个参数dwDataType说明回调接收到的数据类型,pBuffer 存放数据的缓冲区指针, dwBufSize 缓冲区大小 ,pUser 用户数据的句柄。我做的这个视频的中转功能其实就是在这个回调函数中实现的。
好了,核心的代码都摘出来了,大家按照SDK提供的Demo照猫画虎就可以把预览功能实现出来了。
服务端设计:
实现了预览功能,下面看看中转服务的实现。其中包含三个类:Server,Client以及ClientList类。
Server类主要负责从设备读取数据并将数据缓存到服务器上,并且作为Socket监听服务端;ClientList维护一个客户端列表,并在Server获取到设备数据时便利客户端列表发送数据到客户端;Client类主要负责将服务端缓存的数据分发到各个终端请求上。
三个类的关系及主要成员请看下图:
Server类:
1 class Server 2 { 3 int m_lUserID = -1; 4 //头数据 5 byte[] headStream; 6 7 ClientList clientList = ClientList.GetClientList(); 8 CHCNetSDK.REALDATACALLBACK m_fRealData; 9 Socket listenSocket; 10 Semaphore m_maxNumberAcceptedClients; 11 /// <summary> 12 /// Server构造函数,启动服务端Socket及海康SDK获取设备数据 13 /// </summary> 14 /// <param name="ipPoint">服务端IP配置</param> 15 /// <param name="numConnections">最大客户端连接数</param> 16 /// <param name="channel">设备监听通道</param> 17 public Server(IPEndPoint ipPoint, int numConnections, int channel) 18 { 19 if (!InitHK()) 20 { 21 return; 22 } 23 RunGetStream(channel); 24 25 listenSocket = new Socket(ipPoint.AddressFamily, SocketType.Stream, PRotocolType.Tcp); 26 listenSocket.Bind(ipPoint); 27 m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); 28 listenSocket.Listen(100); 29 Console.WriteLine("开始监听客户端连接......"); 30 StartAccept(null); 31 } 32 33 #region HKSDK 34 35 private void RunGetStream(int channel) 36 { 37 if (m_lUserID != -1)//初始化成功 38 { 39 CHCNetSDK.NET_DVR_CLIENTINFO lpClientInfo = new CHCNetSDK.NET_DVR_CLIENTINFO(); 40 lpClientInfo.lChannel = channel; 41 lpClientInfo.lLinkMode = 0x0000; 42 lpClientInfo.sMultiCastIP = ""; 43 m_fRealData = new CHCNetSDK.REALDATACALLBACK(RealDataCallBack); 44 IntPtr pUser = new IntPtr(); 45 int m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V30(m_lUserID, ref lpClientInfo, m_fRealData, pUser, 1); 46 Console.WriteLine("开始获取视频数据......"); 47 } 48 else//初始化 失败,因为已经初始化了 49 { 50 Console.WriteLine("视频数据获取失败......"); 51 } 52 } 53 54 private bool InitHK() 55 { 56 bool m_bInitSDK = CHCNetSDK.NET_DVR_Init(); 57 if (m_bInitSDK == false) 58 { 59 return false; 60 } 61 else 62 { 63 Console.WriteLine("设备SDK初始化成功......."); 64 CHCNetSDK.NET_DVR_DEVICEINFO_V30 DeviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30(); 65 m_lUserID = CHCNetSDK.NET_DVR_Login_V30("设备IP", 连接端口, "连接用户名", "连接密码", ref DeviceInfo); 66 if (m_lUserID != -1) 67 { 68 Console.WriteLine("监控设备登录成功......."); 69 return true; 70 } 71 else 72 { 73 Console.WriteLine("监控设备登录失败,稍后再试......."); 74 return false; 75 } 76 } 77 } 78 79 private void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser) 80 { 81 byte[] data = new byte[dwBufSize]; 82 Marshal.Copy(pBuffer, data, 0, (int)dwBufSize); 83 Console.WriteLine("监控设备连接正常......"); 84 if (dwDataType == CHCNetSDK.NET_DVR_SYSHEAD) 85 { 86 headStream = data; 87 } 88 clientList.SetSendData(data); 89 return; 90 } 91 92 #endregion 93 94 #region Socket 95 /// <summary> 96 /// 监听客户端 97 /// </summary> 98 /// <param name="acceptEventArg"></param> 99 private void StartAccept(SocketAsyncEventArgs acceptEventArg)100 {101 if (acceptEventArg == null)102 {103 acceptEventArg = new SocketAsyncEventArgs();104 acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);105 }106 else107 {108 acceptEventArg.AcceptSocket = null;109 }110 111 m_maxNumberAcceptedClients.WaitOne();112 bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);113 if (!willRaiseEvent)114 {115 ProcessAccept(acceptEventArg);116 }117 }118 /// <summary>119 /// 增加客户端列表120 /// </summary>121 /// <param name="e"></param>122 private void ProcessAccept(SocketAsyncEventArgs e)123 {124 clientList.AddClient(new Client(e.AcceptSocket, headStream));125 StartAccept(e);126 }127 128 /// <summary>129 /// Socket回调函数130 /// </summary>131 /// <param name="
新闻热点
疑难解答