内容简单介绍:
海康官方只提供.netframework的版本库sdk只支持windows,我这里需要实现.netcore跨平台调用录制视频,所以才有了以下操作!!! 保姆教程如下:
1、 先去海康开放平台下载自己需要的系统库文件 (windows系统下载c#的dll即可,linux系统下载c++的so文件)如下图
linux库文件
windows库文件
2、 修改海康官网提供的sdk.cs文件内容改为适配windows和linux,样例代码如下:
注意:我这只是截图提供了万分之一的一小部分,需要把所有的方法都按如图适配成图中格式,如有需要可联系作者本人获取源文件!!
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代码会报错找不到库文件,样例如下图: