在上一篇博客:《如何使用海康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)
BOOL Csample_hk_cfgDlg::OnInitDialog()
{
// 初始化海康SDK资源...
NET_DVR_Init();
// 初始化网络、线程、套接字...
WORD wsVersion = MAKEWORD(2, 2);
WSADATA wsData = {0};
(void)::WSAStartup(wsVersion, &wsData);
}
// 点击“异步登录”按钮...
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;
}
// 获取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");
// 获取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;
}
// 获取图像参数 => 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设备进行校时操作 => 设置成跟电脑时间一致...
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;
}
// 设置设备异常消息回调接口函数...
if( !NET_DVR_SetExceptionCallBack_V30(0, NULL,
Csample_hk_cfgDlg::DeviceException, this) )
{
dwErr = NET_DVR_GetLastError();
MsgLogGM(dwErr);
break;
}
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;
}
}
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));
}
}
注意:在程序退出释放资源时,没有直接退出,因为,有可能还处在异步登录的等待回调过程中,必须等待异步回调返回之后才能退出,否则,会引起程序崩溃。
文章浏览阅读9.4k次,点赞8次,收藏71次。在pytorch中已经包含了许多的内置数据集,我们可以很简单的调用其内置的,但是在现实的过程之中我们往往会使用自己的数据集。这就使得读取自己的数据集并进行训练会有很大的问题。因此对于csv格式的数据集合,以下图为例每一份csv文件为一个样本,对应的标签数据也是使用csv格式进行存储。对于这种情况,我们可以使用重写dataset类来解决这个问题,利用迭代的方式依次读取对应的data和label。代码如下:class myDataSet(Dataset): def __i._pytorch读取csv数据集
文章浏览阅读2.6w次,点赞11次,收藏92次。Data envelopment analysis–BCC模型DEA--BCC模型Data envelopment analysis--BCC模型模型乘子型线性模型分式模型包络型基于输入向的模型基于产出向的模型BCC与CCR模型的区别BCC与CCR模型值的比较BCC有效BCC投影R代码模型先写出BCC模型的公式,继而进行详述。乘子型线性模型分式模型包络型基于输入向的模型基于产出向的模型BCC与CCR模型的区别第一个不同便是生产可能集基于的假设不同。CCR模型假设的规模报酬是不_bcc模型
文章浏览阅读7.6k次。在线的Web网站性能测试工具:1) Web Page Test2) Load Impact3) FreeSpeedTest4) Browser Mob5) Rapid Search Metrics6) Host Tracker7) Pingdom Tools8) Webo Software9) iWebtoo_web性能测试工具提供在线服务
文章浏览阅读1k次。本文由中山大学人机物智能融合实验室(HCP Lab)特约供稿。全球计算机视觉三大顶会之一CVPR 2019(IEEE Conference on Computer Visionand Pattern Recognition) 于 6月 16~20日 在美国洛杉矶如期举办。CVPR 作为计算机视觉三大顶级会议之一,一直以来都备受关注。被 CVPR 收录的论文更是代表了计算机视觉领域的最新发展...
文章浏览阅读4.7k次,点赞8次,收藏9次。安装又按不了->更新又不行->怎么办gcc程序“gcc”尚未安装。 您可以使用以下命令安装:sudo apt-get install gccweifc@ubuntu:~/demo$ sudo apt-get install gcc正在读取软件包列表… 完成正在分析软件包的依赖关系树正在读取状态信息… 完成现在没有可用的软件包 gcc,但是它被其它的软件包引用了。这可能..._没有可用的软件包 gcc-multilib,但是它被其它的软件包引用了。 这可能意味着这个
文章浏览阅读2.7k次。首先,到http://rxtx.qbang.org/wiki/index.php/Download下载相应zip包,由于发帖时2.2版本不稳定(里面是jar是2.1版本,但dll是2.2版本,造成版本冲突,报错:RXTX Version mismatch),建议使用2.1-7r2版本。请参见本博客另一篇博文点击打开链接,将zip中的jar安装到maven仓库里。在pom文件中加入引用。_rxtx version mismatch
文章浏览阅读2.3k次。效果图:上面的是原生Textview,第一行末尾数字整体换行了,下面是自定义Textview,第一行末尾数字分别在第一行和第二行展示布局中 MyTextview记得换成自己包名下的 <TextView android:layout_margin="25dp" android:id="@+id/test_tv" an..._android textview 数字直接换行了
文章浏览阅读7.8k次。社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ、人人网、开心网、新浪微博、搜狐微博、腾讯微博、淘宝、豆瓣、MSN、Google等社会化媒体账号登录该网站。前言在之前的Spring Social系列中,我们只是实现了使用服务提供商账号登录到业务系统中,但没有与业务系统中的账号进行关联。本章承接之前社交系列来实现社交账号与业务系统账号的绑定与解绑。Spring-_social connectionaddedredirecturl
文章浏览阅读2.6k次,点赞6次,收藏3次。#Caused by: java.lang.IllegalStateException: Cannot convert value of type ‘java.lang.String’ to required type ‘javax.sql.DataSource’ for property ‘dataSource’: no matching editors or conversion strategy found在Spring和mybatis的整合中出现了这个问题,找了很久,最后才发现是:在容器的注入过_caused by: java.lang.illegalstateexception: cannot convert value of type 'ja
文章浏览阅读2.2k次,点赞2次,收藏8次。普通安装1.拉取镜像,redis:4.0docker pull redis:4.02.创建redis容器名"redistest1",并开启持久化docker run -d -p 6379:6379 --name redistest1 redis:4.0 redis-server --appendonly yes参数说明:--appendonly yes:开启持久化挂载外部配置和数据安装1.创建目录和配置文件redis.confmkdir /dockermk_docker run -p 6379:6379 \ -v /root/docker/redis/redis.conf:/etc/redis/redis.
文章浏览阅读655次。单行函数 Function name(column | expression,[arg0,arg1,arg2….]) 单行函数的分类: 字符函数:接受字符串的输入并返回字符串的值 数值函数:接受数值的输入并返回数值 日期函数:对日期数据进行操作 转换函数:从一种数据类型转换成另一种类_193500大写
文章浏览阅读5.4k次,点赞4次,收藏17次。1.Android studio的安装百度搜索Android studio官网下载速度会比较慢,所以我们选择中文社区在这里选择要下载的版本,我选择的是Windows3.5.2版本下载完成之后双击安装,这里要注意,因为一般情况下我们都是用Java编程,所以安装的时候尽量选择和jdk同一个盘下。而且安装路径尽量不要出现中文,容易报错。然后一路next+finish。..._andriod端下载