如何使用海康SDK实现网络摄像机(IPC)自动配置【源码】【监控】【录播】【NVR】_海康摄像头切换anr将自动重连ipc_haoyifans的博客-程序员秘密

技术标签: 录播  配置  采集端  监控  NVR  海康  IPC  

前言:

    在上一篇博客:《如何使用海康SDK实现异步登录网络摄像机(IPC)》,我们实现了完全的IPC异步登录,今天我们要完成的是:实现IPC的自动配置,能够自动设定主码流和子码流,能获取通道的音视频压缩格式,只让H264+AAC通过,为下一步实现无插件直播播放做准备(要实现无插件直播播放,只能选择H264+AAC配置)。

    本示例还能获取IPC的RTSP端口,便于正确的构造rtsp地址;还能设置IPC的镜像模式;还能设置IPC的OSD信息,设定显示时间在画面中的位置;还能同步IPC的时钟与本地PC保持一致;还能设置IPC的预览画面窗口,实现IPC图像的本地回放;

    在开始写代码之前,我们先要下载好一份海康SDK,里面有详细的开发文档和丰富的示例程序;

    下载地址 => http://www.hikvision.com/cn/download_61.html

    我们示例代码使用的是 设备网络SDK_Win32,版本为 V5.2.5.25

源码下载:

    CSDN: https://download.csdn.net/download/haoyitech/10289344

源码说明:

    开发工具:下载后,请用 VS2010 打开。

    基本原理:异步登录IPC成功之后,直接调用SDK提供的接口对IPC进行配置,主要是为下一步实现无插件直播播放做准备,所有的配置功能都集中在 Csample_hk_cfgDlg::OnDeviceLoginSuccess(WPARAM wParam, LPARAM lParam) 当中。

    海康SDK里面有很多的相关动态库,我们进行了部分筛选,只留下我们需要的,文件结构说明:

编译结果存放位置    => sample_hk_login\bin
海康SDK核心库       => sample_hk_login\bin\HCCore.dll
海康SDK网络库       => sample_hk_login\bin\HCNetSDK.dll
海康SDK播放D3D库    => sample_hk_login\bin\D3DX9_43.dll
海康SDK播放控件库   => sample_hk_login\bin\PlayCtrl.dll
海康SDK音频渲染库   => sample_hk_login\bin\AudioRender.dll
海康SDK播放辅助库   => sample_hk_login\bin\SuperRender.dll
海康SDK画面预览库   => sample_hk_login\bin\HCNetSDKCom\HCPreview.dll
海康SDK设备配置库   => sample_hk_login\bin\HCNetSDKCom\HCCoreDevCfg.dll
海康SDK通用配置库   => sample_hk_login\bin\HCNetSDKCom\HCGeneralCfgMgr.dll
浩一科技代码辅助库  => sample_hk_login\common

关键代码:(详见 Csample_hk_loginDlg)

1、初始化过程:

BOOL Csample_hk_cfgDlg::OnInitDialog()
{
	// 初始化海康SDK资源...
	NET_DVR_Init();

	// 初始化网络、线程、套接字...
	WORD	wsVersion = MAKEWORD(2, 2);
	WSADATA	wsData	  = {0};
	(void)::WSAStartup(wsVersion, &wsData);
}

2、发起异步登录:

// 点击“异步登录”按钮...
void Csample_hk_cfgDlg::OnBnClickedButtonAsync()
{
	if( m_HKLoginID > 0 ) {
		TRACE("=== 当前已经处于登录状态 ===\n");
		return;
	}
	ASSERT( m_HKLoginID <= 0 );
	CString strAddress("192.168.1.65");
	CString strUser("admin");
	CString strPass("admin123");
	int nCmdPort = 8000;
	DWORD dwErr = this->doDeviceLogin(strAddress, nCmdPort, strUser, strPass);
}
// 执行IPC登录操作...
DWORD Csample_hk_cfgDlg::doDeviceLogin(LPCTSTR lpIPAddr, int nCmdPort, LPCTSTR lpUser, LPCTSTR lpPass)
{
	// 将摄像机的错误标志复位...
	m_dwHKErrCode = NET_DVR_NOERROR;
	// 登录之前,先释放资源,保存通知窗口...
	DWORD dwErr = GM_NoErr;
	this->ClearResource();
	// 异步方式登录DVR设备...
	NET_DVR_DEVICEINFO_V40  dvrDevV40 = {0};
	NET_DVR_USER_LOGIN_INFO dvrLoginInfo = {0};
	dvrLoginInfo.cbLoginResult = Csample_hk_cfgDlg::DeviceLoginResult;
	strcpy(dvrLoginInfo.sDeviceAddress, lpIPAddr);
	strcpy(dvrLoginInfo.sUserName, lpUser);
	strcpy(dvrLoginInfo.sPassword, lpPass);
	dvrLoginInfo.bUseAsynLogin = 1;
	dvrLoginInfo.wPort = nCmdPort;
	dvrLoginInfo.pUser = this;
	// 调用异步接口函数...
	if( NET_DVR_Login_V40(&dvrLoginInfo, &dvrDevV40) < 0 ) {
		dwErr = NET_DVR_GetLastError();
		MsgLogGM(dwErr);
	}
	// 设置正在异步登录中标志...
	m_HKLoginIng = true;
	// 如果调用失败,清除所有资源...
	if( dwErr != GM_NoErr ) {
		TRACE("=== 登录失败,错误号:%lu ===\n", dwErr);
		this->ClearResource();
		return dwErr;
	}
	// 打印正在登录状态...
	TRACE("=== 正在异步登录... ===\n");
	return GM_NoErr;
}

3、异步登录成功,进行IPC配置:(Csample_hk_cfgDlg::OnDeviceLoginSuccess)

获取RTSP端口:

// 获取IPC的rtsp端口号...
NET_DVR_RTSPCFG dvrRtsp = {0};
if( !NET_DVR_GetRtspConfig(m_HKLoginID, 0, &dvrRtsp, sizeof(NET_DVR_RTSPCFG)) ) {
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}
// 调用正确,打印rtsp端口...
TRACE("\nRTSP端口:%lu\n", dvrRtsp.wPort);

判断音视频压缩格式:

// 获取压缩配置参数信息 => 包含了 主码流 和 子码流 ...
NET_DVR_COMPRESSIONCFG_V30 dvrCompressCfg = {0};
if( !NET_DVR_GetDVRConfig(m_HKLoginID, NET_DVR_GET_COMPRESSCFG_V30, nDvrStartChan, 
                        &dvrCompressCfg, sizeof(dvrCompressCfg), &dwReturn) )
{	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}
// 判断主码流或子码流的视频类型是否正确 => 复合流
if( dvrCompressCfg.struNormHighRecordPara.byStreamType != 1 ||
	dvrCompressCfg.struNetPara.byStreamType != 1 ) {
	dwErr = GM_DVR_VType_Err;
	MsgLogGM(dwErr);
	break;
}
// 判断主码流或子码流视频编码类型是否正确 => H264
if( dvrCompressCfg.struNormHighRecordPara.byVideoEncType != NET_DVR_ENCODER_H264 ||
	dvrCompressCfg.struNetPara.byVideoEncType != NET_DVR_ENCODER_H264 ) {
	dwErr = GM_DVR_VEnc_Err;
	MsgLogGM(dwErr);
	break;
}
TRACE("=== RTSP - Video is Encoder H264 ===\n");
// 判断主码流或子码流音频编码类型是否正确 => AAC
// 音频格式不对,只是报错,但不退出,RTSP会话会自动丢弃音频...
if( dvrCompressCfg.struNormHighRecordPara.byAudioEncType != AUDIOTALKTYPE_AAC ||
	dvrCompressCfg.struNetPara.byAudioEncType != AUDIOTALKTYPE_AAC ) {
	MsgLogGM(GM_DVR_AEnc_Err);
	//dwErr = GM_DVR_AEnc_Err;
	//MsgLogGM(dwErr);
	//break;
}
TRACE("=== RTSP - Audio is Encoder AAC ===\n");

设置主码流和子码流的大小:

// 从配置文件中读取并设置主码流大小和子码流大小 => 自定义码流...
dvrCompressCfg.struNormHighRecordPara.dwVideoBitrate = 1024 * 1024;
dvrCompressCfg.struNormHighRecordPara.dwVideoBitrate |= 0x80000000;
dvrCompressCfg.struNetPara.dwVideoBitrate = 500 * 1024;
dvrCompressCfg.struNetPara.dwVideoBitrate |= 0x80000000;
// 设置 主码流 和 子码流 的配置参数信息...
if( !NET_DVR_SetDVRConfig(m_HKLoginID, NET_DVR_SET_COMPRESSCFG_V30, nDvrStartChan, &dvrCompressCfg, sizeof(dvrCompressCfg)) ) {
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}
TRACE("=== 主码流:1Mbps,子码流:500kbps ===\n");

设置IPC的镜像画面模式:

// 获取DVR设备的前端参数...
NET_DVR_CAMERAPARAMCFG dvrCCDParam = {0};
if( !NET_DVR_GetDVRConfig(m_HKLoginID, NET_DVR_GET_CCDPARAMCFG, nDvrStartChan, 
                          &dvrCCDParam, sizeof(dvrCCDParam), &dwReturn) )
{
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}
// 对镜像模式进行处理 => 镜像:0 关闭;1 左右;2 上下;3 中间
BOOL bOpenMirror = true;
dvrCCDParam.byMirror = (bOpenMirror ? 3 : 0);
if( !NET_DVR_SetDVRConfig(m_HKLoginID, NET_DVR_SET_CCDPARAMCFG, nDvrStartChan,
                          &dvrCCDParam, sizeof(dvrCCDParam)) )
{
	dwErr = NET_DVR_GetLastError(); // 注意这个错误号:NET_DVR_NETWORK_ERRORDATA
	MsgLogGM(dwErr);
	break;
}

设置IPC的OSD信息:

// 获取图像参数 => OSD | 坐标 | 日期 | 星期 | 字体 | 属性
NET_DVR_PICCFG_V40 dvrPicV40 = {0};
if( !NET_DVR_GetDVRConfig(m_HKLoginID, NET_DVR_GET_PICCFG_V40, nDvrStartChan,
                          &dvrPicV40, sizeof(dvrPicV40), &dwReturn) )
{
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}
// 从通道配置文件中获取是否开启OSD...
BOOL bOpenOSD = true;
// 设置图像格式 => OSD | 坐标 | 日期 | 星期 | 字体 | 属性
//strcpy((char*)dvrPicV40.sChanName, "Camera"); // 通道名称...
//dvrPicV40.dwVideoFormat = 2; // 视频制式:0- 不支持,1- NTSC,2- PAL 
//dvrPicV40.dwShowChanName = 0; // 预览的图象上是否显示通道名称:0-不显示,1-显示(区域大小704*576) 
//dvrPicV40.wShowNameTopLeftX = 200; // 通道名称显示位置的x坐标
//dvrPicV40.wShowNameTopLeftY = 100; // 通道名称显示位置的y坐标
//dvrPicV40.dwEnableHide = 1; // 是否启动隐私遮蔽:0-否,1-是
dvrPicV40.dwShowOsd = bOpenOSD; // 预览的图象上是否显示OSD:0-不显示,1-显示(区域大小704*576)
dvrPicV40.wOSDTopLeftX = 300; // OSD的x坐标
dvrPicV40.wOSDTopLeftY = 20; // OSD的y坐标
dvrPicV40.byOSDType = 2; // OSD类型(年月日格式) 0-XXXX-XX-XX 年月日; 1-XX-XX-XXXX 月日年; 2-XXXX年XX月XX日; 3-XX月XX日XXXX年; 4-XX-XX-XXXX 日月年; 5-XX日XX月XXXX年; 6-xx/xx/xxxx 月/日/年; 7-xxxx/xx/xx 年/月/日; 8-xx/xx/xxxx 日/月/年
dvrPicV40.byDispWeek = 0; // 是否显示星期:0-不显示,1-显示
dvrPicV40.byOSDAttrib = 2; // OSD属性(透明/闪烁):1-透明,闪烁;2-透明,不闪烁;3-闪烁,不透明;4-不透明,不闪烁
dvrPicV40.byHourOSDType = 0; // 小时制:0表示24小时制,1表示12小时制或am/pm 
dvrPicV40.byFontSize = 0xFF; // 字体大小:0- 16*16(中)/8*16(英),1- 32*32(中)/16*32(英),2- 64*64(中)/32*64(英),3- 48*48(中)/24*48(英),4- 24*24(中)/12*24(英),5- 96*96(中)/48*96(英),0xff- 自适应
dvrPicV40.byOSDColorType = 0; // OSD颜色模式:0- 默认(黑白),1-自定义(颜色见struOsdColor)
dvrPicV40.struOsdColor.byRed = 255;
dvrPicV40.struOsdColor.byGreen = 0;
dvrPicV40.struOsdColor.byBlue = 0;
dvrPicV40.byAlignment = 0; // 对齐方式:0- 自适应,1- 右对齐,2- 左对齐
if( !NET_DVR_SetDVRConfig(m_HKLoginID, NET_DVR_SET_PICCFG_V40, nDvrStartChan,
                          &dvrPicV40, sizeof(dvrPicV40)) )
{
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}

设置IPC的同步时间:

// 对IPC设备进行校时操作 => 设置成跟电脑时间一致...
NET_DVR_TIME dvrTime = {0};
CTime curTime = CTime::GetCurrentTime();
dvrTime.dwYear = curTime.GetYear();
dvrTime.dwMonth = curTime.GetMonth();
dvrTime.dwDay = curTime.GetDay();
dvrTime.dwHour = curTime.GetHour();
dvrTime.dwMinute = curTime.GetMinute();
dvrTime.dwSecond = curTime.GetSecond();
if( !NET_DVR_SetDVRConfig(m_HKLoginID, NET_DVR_SET_TIMECFG, 0,
                          &dvrTime, sizeof(dvrTime)) )
{
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}

设置IPC的异常消息回调函数:

// 设置设备异常消息回调接口函数...
if( !NET_DVR_SetExceptionCallBack_V30(0, NULL, 
        Csample_hk_cfgDlg::DeviceException, this) )
{
	dwErr = NET_DVR_GetLastError();
	MsgLogGM(dwErr);
	break;
}

设置IPC的图像预览窗口:

BOOL bPreview = true;
// 配置了可以预览画面才显示...
if( bPreview ) {
	// 准备显示预览画面需要的参数...
	CWnd * lpPreview = this->GetDlgItem(IDC_PREVIEW);
	NET_DVR_CLIENTINFO dvrClientInfo = {0};
	dvrClientInfo.hPlayWnd     = lpPreview->m_hWnd;
	dvrClientInfo.lChannel     = nDvrStartChan;
	dvrClientInfo.lLinkMode    = 0;
	dvrClientInfo.sMultiCastIP = NULL;
	// 调用实时预览接口...
	m_HKPlayID = NET_DVR_RealPlay_V30(m_HKLoginID, &dvrClientInfo,
                                    NULL, NULL, TRUE);
	if( m_HKPlayID < 0 ) {
		dwErr = NET_DVR_GetLastError();
		MsgLogGM(dwErr);
		break;
	}
}

4、程序关闭,释放资源:

Csample_hk_cfgDlg::~Csample_hk_cfgDlg()
{
	// 释放海康SDK资源...
	NET_DVR_Cleanup();
	// 阻塞等待退出...
	this->WaitForExit();
	// 注销登录...
	this->ClearResource();
}
// 等待异步登录退出 => 使用互斥不起作用...
void Csample_hk_cfgDlg::WaitForExit()
{
	m_bIsExiting = true;
	while( m_HKLoginIng ) {
		::Sleep(5);
	}
	ASSERT( !m_HKLoginIng );
}
// 释放建立资源...
void Csample_hk_cfgDlg::ClearResource()
{
	// 释放正在录像资源,实时预览资源...
	if( m_HKPlayID >= 0 ) {
		NET_DVR_StopRealPlay(m_HKPlayID);
		m_HKPlayID = -1;
	}
	// 释放登录资源...
	if( m_HKLoginID >= 0 ) {
		NET_DVR_Logout_V30(m_HKLoginID);
		m_HKLoginID = -1;
		memset(&m_HKDeviceInfo, 0, sizeof(m_HKDeviceInfo));
	}
}
注意:在程序退出释放资源时,没有直接退出,因为,有可能还处在异步登录的等待回调过程中,必须等待异步回调返回之后才能退出,否则,会引起程序崩溃。

更多信息:

************************************************************
 * 浩一科技,提供云监控、云录播的全平台无插件解决方案。
 * 支持按需直播,多点布控,分布式海量存储,动态扩容;
 * 支持微信扫码登录,全平台帐号统一,关联微信小程序;
 * 支持多种数据输入:摄像头IPC、rtmp、rtsp、MP4文件;
 * 支持全实时、全动态、全网页管理,网页前后台兼容IE8;
 * 支持多终端无插件自适应播放,flvjs/hls/rtmp自动适配;
************************************************************
 * 官方网站 => https://myhaoyi.com
 * 技术博客 => http://blog.csdn.net/haoyitech
 * 开源代码 => https://github.com/HaoYiTech/
************************************************************

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/haoyitech/article/details/79574696

智能推荐

2021-01-01_计算折扣小明过年_qq_51365920的博客-程序员秘密

python练习题1.小明过年得到1500元压岁钱,在商场看中一个书包,正好商场做活动打八五折,从键盘输入书包的价格,输出折扣后书包的价格以及小明买了书包后剩下的钱。money = 1500bao = float(input(‘请输入书包的价格:’))0.85sheng = money-baoprint(“折扣后书包的价格:”,bao)print(“小明买书包后剩下的钱:”,sheng)2.计算1~100的和i,sum = 1,0while i &lt; 101: sum += i i +=

Java 的枚举类型:枚举的线程安全性及序列化问题_帅性而为1号的博客-程序员秘密

写在前面:Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。本文将深入分析枚举的源码,看一看枚举是怎么实现的,是如何保证线程安全的,以及为什么用枚举实现的单例是最佳方式。枚举是如何保证线程安全的要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enu...

Subversion的安装和使用_dajuezhao的博客-程序员秘密

一、环境 1、操作系统:Windows XP 2、JDK:JDK 1.6.0 二、下载和安装 相关软件已经相应的网址: 软件 网址 备注 apache_2.2.14-win32-x86-openssl-0.9.8k.msi http://archive.a

Redis总结二 - 测试案例_redis测试用例_calangan的博客-程序员秘密

搭建springboot项目application.ymlserver: port: 9001spring: redis: host: 127.0.0.1 port: 6379 jedis: pool: max-wait: 30000 #连接池最大阻塞等待时间,使用负值表示没有限制 max-active: 100 #连接池最大连接数,使用负值表示没有限制 max-idle: 20 #连接池中的最大空闲连接

用Python 操作 Excel,这篇文章别错过了!(超全总结)_python反置excel_平静愉悦的博客-程序员秘密

在之前的办公自动化系列文章中,我已经对Python操作Excel的几个常用库openpyxl、xlrd/xlwt、xlwings、xlsxwriter等进行了详细的讲解。为了进一步带大家了解各个库的异同,从而在不同场景下可以灵活使用,本文将横向比较7个可以操作 Excel 文件的常用模块,在比较各模块常用操作的同时进行巩固学习!首先让我们来整体把握下不同库的特点“ xlrd、xlwt、xlutils各自的功能都有局限性,但三者互为补充,覆盖了Excel文件尤其是.xls文件的操作。...

2.3 Go语言从入门到精通:数据类型_true' (type bool) cannot be represented by the typ_xcbeyond的博客-程序员秘密

文章目录1、基本数据类型1.1 布尔型1.2 数值型1.2.1 整型1.2.2 浮点型1.2.3 复数1.3 字符串型2、派生数据类型2.1 指针2.2 数组2.3 结构体2.4 通道(channel)2.5 切片(slice)2.6 函数2.7 接口(interface)2.8 Map3、其他3.1 数据类型转换3.2 类型别名4、小结Go 语言是一种静态类型的编程语言,在 Go 编程语言中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才

随便推点

Spring Cloud 入门教程(七): 消息总线(Spring Cloud Bus)(Greenwich.RELEASE)_chichachu6829的博客-程序员秘密

参考网址:https://blog.csdn.net/forezp/article/details/81041062,由于此文中作者基于git和rabbitMq,为了适应内网我改造为基于mysql和kafka 一、准备工作 1、安装kafka 参考这个:kafka在windows下的安装...

【转】php 的对应处理java的Base64.encodeBase64String(DigestUtils.md5Hex(params+appkey+timestamp).getBytes());_shinikm的博客-程序员秘密

之前是没用过java的,由于要对接一个java提供的接口,接口示例是这么写的Base64.encodeBase64String(DigestUtils.md5Hex(params+appkey+timestamp).getBytes())看一下,应该就是将参数可key进行md5加密,然后在去byte数组,然后将这个数组进行base64加密。但是php的方法跟java是不一样的,无奈我想到的是按...

gradle构建的项目结构详解_gradle项目架构_NPException的博客-程序员秘密

整个工程目录如如下关键文件build.gradle:文件包含项目构建所使用的脚本。如:plugins { id 'org.springframework.boot' version '2.1.2.RELEASE' id 'java'}//允许引入该工程去使用的一些插件apply plugin: 'io.spring.dependency-management...

Docker国内官方镜像地址_docker image官方地址_HaiTian-Jackie的博客-程序员秘密

docker国内官方镜像地址docker默认使用国外官方网站镜像,速度比较慢,甚至无法连接状态。国内镜像地址比较多,但是大都比较坑爹。所以我个人偏好使用docker国内官方镜像地址。如果安装官方安装docker后,配置镜像地址如下;vim /etc/docker/daemon.json{&amp;amp;amp;amp;quot;registry-mirrors&amp;amp;amp;amp;quot;:[&amp;amp;amp;amp;quot;https://registry.do

MNIST数据集的简单读取(搬砖)_读取mnist数据集_搞深学的程序员的博客-程序员秘密

今天看到了很多读取数据集的代码,思想就一个吧了解数据集存储结构,然后设计函数读取。https://www.jianshu.com/p/e7c286530ab9看了这位老哥的代码,稍微改一下,加了个标注,自己总结一下。1.mnist数据集很好下载,这里不贴链接了。下载后是这样的2.然后解压,这里包括60000张训练数据,10000张测试数据3.里面的具体结构是这样的4.前16个字节是描述信息,后面是数据信息,一张图片分成2825个字节存储,如测试数据集有10000张图片,前8个字节是描述信息,

Guava学习总结_+YUAN的博客-程序员秘密

什么是Guava Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、...

推荐文章

热门文章

相关标签