.net跨平台调用海康SDK实现对面阵相机的各种花操作

106 阅读6分钟

内容简单介绍: 海康官方只提供.netframework的版本库sdk只支持windows,我这里需要实现.netcore跨平台调用录制视频,所以才有了以下操作!!! 保姆教程如下:

1、 先去海康开放平台下载自己需要的系统库文件 (windows系统下载c#的dll即可,linux系统下载c++的so文件)如下图

linux库文件 image.png

windows库文件 image.png

2、 修改海康官网提供的sdk.cs文件内容改为适配windows和linux,样例代码如下:

注意:我这只是截图提供了万分之一的一小部分,需要把所有的方法都按如图适配成图中格式,如有需要可联系作者本人获取源文件!! image.png

image.png

3、开始使用(一些我自己用到的方法,大家需要啥自己发挥)

/// <summary>
/// 海康相机dto
/// </summary>
public class HKCameraDto
{
    /// <summary>
    /// 构造函数
    /// </summary>
    public HKCameraDto() 
    {
        M_lUserID = -1;
        M_lRealHandle = -1;
        M_bRecord = false;
        LoginCallBack = null;
        RealData = null;
        IpAddress = string.Empty;
        UserName = string.Empty;
        Password = string.Empty;
        VideoFilePath = string.Empty;
        VideoFileName = string.Empty;
    }
    /// <summary>
    /// 相机ip
    /// </summary>
    public string IpAddress { get; set; }
    /// <summary>
    /// 相机用户名
    /// </summary>
    public string UserName { get; set; }
    /// <summary>
    /// 密码
    /// </summary>
    public string Password { get; set; }
    /// <summary>
    /// 端口号
    /// </summary>
    public ushort Port { get; set; }
    /// <summary>
    /// 视频文件存放路径
    /// </summary>
    public string VideoFilePath { get; set; }
    /// <summary>
    /// 视频文件名称
    /// </summary>
    public string VideoFileName { get; set; }
    /// <summary>
    /// 日志回调函数
    /// </summary>
    public CHCNetSDK.LOGINRESULTCALLBACK LoginCallBack { get; set; }
    /// <summary>
    /// 设备信息
    /// </summary>
    public CHCNetSDK.NET_DVR_DEVICEINFO_V40 DeviceInfo;
    /// <summary>
    /// 登录设备的用户id
    /// </summary>
    public int M_lUserID { get; set; }
    /// <summary>
    /// 预览状态
    /// </summary>
    public int M_lRealHandle { get; set; }
    /// <summary>
    /// 预览数据流
    /// </summary>
    public CHCNetSDK.REALDATACALLBACK RealData { get; set; }
    /// <summary>
    /// 当前是否在录像
    /// </summary>
    public bool M_bRecord { get; set; }
    /// <summary>
    /// 相机时间
    /// </summary>
    public CHCNetSDK.NET_DVR_TIME StruTimeCfg;
}
    
 /// <summary>
 /// 海康相机业务逻辑
 /// </summary>
 public class HKCamareService
 {
     private readonly ILogger<HKCamareService> _logger;
     private ConcurrentDictionary<string, HKCameraDto> _hkCamareDic = new();
     public HKCamareService(ILogger<HKCamareService> logger)
     {
         _logger = logger;
     }

     /// <summary>
     /// 初始化海康sdk
     /// </summary>
     public async Task SDK_Init()
     {
         while (true)
         {
             try
             {
                 bool m_bInitSDK = CHCNetSDK.NET_DVR_Init();
                 if (m_bInitSDK)
                 {
                     string rootDirectory = AppDomain.CurrentDomain.BaseDirectory;
                     string logDirectory = Path.Combine(rootDirectory, "SdkLog");
                     if (!Directory.Exists(logDirectory))
                     {
                         Directory.CreateDirectory(logDirectory);
                     }
                     CHCNetSDK.NET_DVR_SetLogToFile(3, logDirectory, true);

                     _logger.LogInformation("海康SDK初始化成功");
                     break;
                 }
             }
             catch (Exception ex)
             {
                 _logger.LogError(ex, "海康SDK初始化异常,1秒后重试");
                 await Task.Delay(1000);
             }
         }
     }
     /// <summary>
     /// 初始化相机字典
     /// </summary>
     /// <param name="ip"></param>
     /// <param name="port"></param>
     public void InitCamareDic(string ip, string port, string userName, string pwd)
     {
         if (!_hkCamareDic.ContainsKey(ip))
         {
             ushort.TryParse(port, out ushort cameraPort);
             _hkCamareDic.GetOrAdd(ip, new HKCameraDto()
             {
                 IpAddress = ip,
                 Port = cameraPort,
                 UserName = userName,
                 Password = pwd,
             });
         }
     }
     /// <summary>
     /// 登录
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <returns></returns>
     public bool Login(string key)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];

         var struLogInfo = new CHCNetSDK.NET_DVR_USER_LOGIN_INFO();

         //设备IP地址或者域名
         byte[] byIP = System.Text.Encoding.Default.GetBytes(cameraDto.IpAddress);
         struLogInfo.sDeviceAddress = new byte[129];
         byIP.CopyTo(struLogInfo.sDeviceAddress, 0);

         //设备用户名
         byte[] byUserName = System.Text.Encoding.Default.GetBytes(cameraDto.UserName);
         struLogInfo.sUserName = new byte[64];
         byUserName.CopyTo(struLogInfo.sUserName, 0);

         //设备密码
         byte[] byPassword = System.Text.Encoding.Default.GetBytes(cameraDto.Password);
         struLogInfo.sPassword = new byte[64];
         byPassword.CopyTo(struLogInfo.sPassword, 0);

         //设备服务端口号
         struLogInfo.wPort = cameraDto.Port;

         cameraDto.LoginCallBack ??= new CHCNetSDK.LOGINRESULTCALLBACK(CbLoginCallBack);//注册回调函数                    
         struLogInfo.cbLoginResult = cameraDto.LoginCallBack;
         struLogInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是 

         cameraDto.DeviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V40();

         //登录设备 Login the device
         cameraDto.M_lUserID = CHCNetSDK.NET_DVR_Login_V40(ref struLogInfo, ref cameraDto.DeviceInfo);
         if (cameraDto.M_lUserID < 0)
         {
             uint code = CHCNetSDK.NET_DVR_GetLastError();
             _logger.LogInformation(key + "登录失败 " + code);//登录失败,输出错误号
             return false;
         }
         return true;
     }
     /// <summary>
     /// 注册回调函数
     /// </summary>
     /// <param name="lUserID"></param>
     /// <param name="dwResult"></param>
     /// <param name="lpDeviceInfo"></param>
     /// <param name="pUser"></param>
     public void CbLoginCallBack(int lUserID, int dwResult, IntPtr lpDeviceInfo, IntPtr pUser)
     {
         string strLoginCallBack = "登录设备,lUserID:" + lUserID + ",dwResult:" + dwResult;

         if (dwResult == 0)
         {
             uint code = CHCNetSDK.NET_DVR_GetLastError();
             strLoginCallBack = strLoginCallBack + ",错误号:" + code;
         }
     }
     /// <summary>
     /// 预览
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <param name="dwStreamType">码流类型:0主码流 1子码流 2第三码流</param>
     /// <returns></returns>
     public int Preview(string key, uint dwStreamType = 0)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];

         if (cameraDto.M_lUserID < 0)
         {
             //当前未登录
             _logger.LogInformation("未登录,不能进行预览");
             return -1;
         }

         if (cameraDto.M_lRealHandle >= 0)
         {
             _logger.LogInformation("当前正在预览,不可以再次进行预览!");
             return -1;
         }

         //当前未预览,开启预览

         CHCNetSDK.NET_DVR_PREVIEWINFO lpPreviewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();
         //lpPreviewInfo.hPlayWnd = (IntPtr)cameraDto.Order;//预览窗口id
         lpPreviewInfo.lChannel = 1;//预te览的设备通道
         lpPreviewInfo.dwStreamType = dwStreamType;//码流类型:0-主码流,1-子码流,2-码流3,3-码流4,以此类推
         lpPreviewInfo.dwLinkMode = 0;//连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP 
         lpPreviewInfo.bBlocked = true; //0- 非阻塞取流,1- 阻塞取流
         lpPreviewInfo.dwDisplayBufNum = 1; //播放库播放缓冲区最大缓冲帧数
         lpPreviewInfo.byProtoType = 0;
         lpPreviewInfo.byPreviewMode = 0;

         cameraDto.RealData ??= new CHCNetSDK.REALDATACALLBACK(RealDataCallBack);//预览实时流回调函数

         IntPtr pUser = new IntPtr();//用户数据

         //打开预览 Start live view 
         cameraDto.M_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(cameraDto.M_lUserID, ref lpPreviewInfo, null, pUser);
         if (cameraDto.M_lRealHandle < 0)
         {
             uint code = CHCNetSDK.NET_DVR_GetLastError();
             //预览失败,输出错误号
             _logger.LogInformation("预览失败, error code= " + code);
             return cameraDto.M_lRealHandle;
         }

         return cameraDto.M_lRealHandle;
     }
     /// <summary>
     /// 预览实时流回调函数
     /// </summary>
     /// <param name="lRealHandle"></param>
     /// <param name="dwDataType"></param>
     /// <param name="pBuffer"></param>
     /// <param name="dwBufSize"></param>
     /// <param name="pUser"></param>
     public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
     {
         if (dwBufSize > 0)
         {
             byte[] sData = new byte[dwBufSize];
             Marshal.Copy(pBuffer, sData, 0, (Int32)dwBufSize);

             string str = "实时流数据.ps";
             FileStream fs = new FileStream(str, FileMode.Create);
             int iLen = (int)dwBufSize;
             fs.Write(sData, 0, iLen);
             fs.Close();
         }
     }
     /// <summary>
     /// 开启录制预览图像
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <param name="timeStr">过车时间</param>
     /// <param name="videoPos">相机位置: 1左 2右 4顶</param>
     /// <param name="savePath">视频保存路径</param>
     /// <returns></returns>
     public bool StartVideoRecord(string key, string videoFileName, string videoFilePath)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];
         cameraDto.VideoFileName = videoFileName;
         cameraDto.VideoFilePath = videoFilePath;

         // 确保文件夹存在
         Directory.CreateDirectory(Path.GetDirectoryName(cameraDto.VideoFilePath));

         if (cameraDto.M_bRecord)
         {
             //当前正在录像,先停掉上个录像,在开始新的录制
             StopVedioRecord(key);

             _logger.LogInformation(key + "当前正在录像");
         }

         //强制I帧 Make a I frame
         CHCNetSDK.NET_DVR_MakeKeyFrame(cameraDto.M_lUserID, 1); //通道号 Channel number
         if (cameraDto.M_lRealHandle >= 0)
         {
             //开始录像 Start recording
             cameraDto.M_bRecord = CHCNetSDK.NET_DVR_SaveRealData_V30(cameraDto.M_lRealHandle, 2, cameraDto.VideoFilePath);
             if (!cameraDto.M_bRecord)
             {
                 //开始录像失败
                 uint code = CHCNetSDK.NET_DVR_GetLastError();
                 _logger.LogInformation(key + " 开始录像失败, error code= " + code);
                 return false;
             }
         }
         _logger.LogInformation(key + " 开始录像 ");
         return true;
     }
     /// <summary>
     /// 停止录像
     /// </summary>
     public (string videoName, string videoPath, List<VideoCamerFrameDto> videoFrames) StopVedioRecord(string key)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];

         if (cameraDto.M_bRecord)
         {
             //当前正在录像,停止录像 Stop recording
             string message = "";

             cameraDto.M_bRecord = CHCNetSDK.NET_DVR_StopSaveRealData(cameraDto.M_lRealHandle);
             if (!cameraDto.M_bRecord)
             {
                 //停止录像失败
                 uint code = CHCNetSDK.NET_DVR_GetLastError();
                 message = key + " 停止录像失败, error code= " + code;
             }
             else
             {
                 //停止录像成功
                 message = key + " 停止录像成功";
                 cameraDto.M_bRecord = false;
             }
             _logger.LogInformation(message);
         }

         return (cameraDto.VideoFileName, cameraDto.VideoFilePath, cameraDto.CamerFrameDtos);
     }

     /// <summary>
     /// 修改相机码流参数
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <param name="type">0主码流 1子码流 2第三码流 4曝光值</param>
     public void SetConfig(string key, int type, string xml)
     {
         if (string.IsNullOrEmpty(xml))
             return;

         HKCameraDto cameraDto = _hkCamareDic[key];
         E_HKCamareDWStreamType streamType = E_HKCamareDWStreamType.MainStream;
         string address = string.Empty;
         switch (type)
         {
             case 1:
                 address = "PUT /ISAPI/Streaming/channels/101";
                 streamType = E_HKCamareDWStreamType.MainStream;
                 break;
             case 2:
                 address = "PUT /ISAPI/Streaming/channels/102";
                 streamType = E_HKCamareDWStreamType.SubStream;
                 break;
             case 3:
                 address = "PUT /ISAPI/Streaming/channels/103";
                 streamType = E_HKCamareDWStreamType.ThirdStream;    
                 break;
             case 4:
                 address = "PUT /ISAPI/Image/channels/1/shutter";
                 streamType = E_HKCamareDWStreamType.Exposure;
                 break;

             default:
                 return;
         }


         // XML 配置
         var xmlInput = new CHCNetSDK.NET_DVR_XML_CONFIG_INPUT
         {
             dwSize = (uint)Marshal.SizeOf(typeof(CHCNetSDK.NET_DVR_XML_CONFIG_INPUT)),
             lpRequestUrl = Marshal.StringToHGlobalAnsi(address),
             dwRequestUrlLen = (uint)Encoding.ASCII.GetByteCount(address),
             lpInBuffer = IntPtr.Zero,
             dwInBufferSize = 0,
             dwRecvTimeOut = 5000,
         };

         var xmlOutput = new CHCNetSDK.NET_DVR_XML_CONFIG_OUTPUT
         {
             dwSize = (uint)Marshal.SizeOf(typeof(CHCNetSDK.NET_DVR_XML_CONFIG_OUTPUT)),
             lpOutBuffer = Marshal.AllocHGlobal(8 * 1024),
             dwOutBufferSize = 8 * 1024,
             lpStatusBuffer = Marshal.AllocHGlobal(1024),
             dwStatusSize = 1024
         };
         int inputDataLen = Encoding.UTF8.GetByteCount(xml);
         if (inputDataLen > 0)
         {
             byte[] inputXmlBytes = Encoding.UTF8.GetBytes(xml);
             GCHandle handle = GCHandle.Alloc(inputXmlBytes, GCHandleType.Pinned);
             xmlInput.lpInBuffer = handle.AddrOfPinnedObject();
             xmlInput.dwInBufferSize = (uint)inputDataLen;
         }
         else
         {
             xmlInput.lpInBuffer = IntPtr.Zero;
             xmlInput.dwInBufferSize = 0;
         }

         if (!CHCNetSDK.NET_DVR_STDXMLConfig(cameraDto.M_lUserID, ref xmlInput, ref xmlOutput))
         {
             _logger.LogInformation($"{key} NET_DVR_STDXMLConfig修改 {streamType.GetDescription()} 失败,错误号 {CHCNetSDK.NET_DVR_GetLastError()}");
         }
         else
         {
             var result = Marshal.PtrToStringAnsi(xmlOutput.lpOutBuffer);
             _logger.LogInformation($"{key} NET_DVR_STDXMLConfig修改 {streamType.GetDescription()} 成功");
         }
     }
     /// <summary>
     ///  设置设备时间【校时】
     /// </summary>
     /// <param name="key">相机ip</param>
     public void SetNvrTime(string key)
     {
         DateTime now = DateTimeUtil.Now();
         HKCameraDto cameraDto = _hkCamareDic[key];
         if (cameraDto.M_lUserID < 0)
             return;

         string msg = "相机:[" + cameraDto.IpAddress + "], ";
         try
         {
             cameraDto.StruTimeCfg.dwYear = (uint)now.Year;
             cameraDto.StruTimeCfg.dwMonth = (uint)now.Month;
             cameraDto.StruTimeCfg.dwDay = (uint)now.Day;
             cameraDto.StruTimeCfg.dwHour = (uint)now.Hour;
             cameraDto.StruTimeCfg.dwMinute = (uint)now.Minute;
             cameraDto.StruTimeCfg.dwSecond = (uint)now.Second;

             int nSize = Marshal.SizeOf(cameraDto.StruTimeCfg);
             IntPtr ptrTimeCfg = Marshal.AllocHGlobal(nSize);
             Marshal.StructureToPtr(cameraDto.StruTimeCfg, ptrTimeCfg, false);

             if (!CHCNetSDK.NET_DVR_SetDVRConfig(cameraDto.M_lUserID, CHCNetSDK.NET_DVR_SET_TIMECFG, -1, ptrTimeCfg, (uint)nSize))
             {
                 uint iLastErr = CHCNetSDK.NET_DVR_GetLastError();
                 msg += "设置时间失败,输出错误号:" + iLastErr;
             }
             else
             {
                 msg += "设置时间成功!" + now.ToString("s");
             }

             Marshal.FreeHGlobal(ptrTimeCfg);

             _logger.LogInformation(msg);
         }
         catch (Exception ex)
         {
             Utils.FileUtil.SaveErrLog(key + " 设置相机时间失败:" +ex.Message, ex.StackTrace);
         }
     }
     /// <summary>
     /// 计算视频帧数
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <param name="pows">顺位号</param>
     /// <param name="frame">视频帧数</param>
     public void SetVideoFrame(string key, int pows, int frame)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];

         if (pows == 0)
         {
             int count = cameraDto.CamerFrameDtos.Count;
             if (count > 0)
             {
                 pows = count;
             }
         }

         cameraDto.CamerFrameDtos ??= new List<VideoCamerFrameDto>();

         cameraDto.CamerFrameDtos.Add(new VideoCamerFrameDto
         {
             Order = pows + 1,
             Frame = frame
         });
     }
     /// <summary>
     /// 计算视频帧数
     /// </summary>
     /// <param name="key">相机ip</param>
     /// <param name="pows">顺位号</param>
     /// <param name="frame">视频帧数</param>
     public void ClearVideoFram(string key)
     {
         HKCameraDto cameraDto = _hkCamareDic[key];

         cameraDto.CamerFrameDtos.Clear();
     }
     /// <summary>
     /// 设置切片配置
     /// </summary>
     /// <param name="byNotSplitRecordFile">0-启用切片,1-不切片</param>
     /// <param name="sizeMB">切片文件大小 MB</param>
     /// <returns></returns>
     public bool SetPlayableSplitConfig(int byNotSplitRecordFile, int sizeMB)
     {
         NET_DVR_LOCAL_GENERAL_CFG config = new NET_DVR_LOCAL_GENERAL_CFG();

         // 启用文件切片
         config.byNotSplitRecordFile = (byte)byNotSplitRecordFile; // 0-启用切片,1-不切片

         // 设置合理的文件大小 (1GB)
         config.i64FileSize = 1024L * 1024L * sizeMB;

         IntPtr ptrConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config));
         Marshal.StructureToPtr(config, ptrConfig, false);

         bool result = CHCNetSDK.NET_DVR_SetSDKLocalCfg(17, ptrConfig);

         Marshal.FreeHGlobal(ptrConfig);
         return result;
     }
 }
 

4、项目运行\发布注意事项

需要把第一步下载过来的dll/so文件手动复制输出到项目目录,否则运行sdk海康sdk代码会报错找不到库文件,样例如下图:

image.png

5、完成以上四个步骤就可以随便发挥操作海康相机了