海康威视视频监控客户端开发实践

本文记录采用海康SDK二次开发视频监控客户端从0开始的学习知识点,供参考交流。海康视频监控SDK和示例程序可以从官网下载。


目录

SDK关键点:

1.预览控制 

单画面播放流程:

多画面播放流程:

多画面播放时停止单通道画面流程:

录像开始/停止/保存:

2.画面分割

对话框创建:

对话框选择:

双击全屏:

 

3.设备管理

右击设备树:

单击设备树控件:

通道和通道号号相关说明:

直接登陆:

4.录像回放

按时间回放:

按文件名回放:

5.异常控制

6.报警

MFC客户端界面

1.预览控制

画面分割:

播放全屏:

Q&A:

设备和通道什么意思?

全局变量为什么定义在CPP中?

这段代码怎么理解typedef int (_stdcall *ChannelOpenProc)(long, HANDLE*);


 

SDK关键点:

1.预览控制 

NET_DVR_RealPlay_V40  实时预览;

NET_DVR_StopRealPlay  停止预览;

NET_DVR_SetRealDataCallBack  注册回调函数,捕获实时码流数据;

单画面播放流程:

创建通道树CreateDeviceTree--双击通道数,播放选中通道OnDblclkTreeChan(如果点击的是设备则跳出,如果点击的是具体通道号则调用双击播放函数)--双击播放DbPlayChannel--开始1路播放StartPlay

{

建立一个NET_DVR_CLIENTINFO预览参数结构体;

调用NET_DVR_RealPlay_V40 ;

获取播放返回状态;

设置标志位;

}

多画面播放流程:

采用对话框作为播放画面的窗口,在父类中保存当前使用的对话框序号,双击设备树播放选中通道DblPlayDevice(m_iCurDeviceIndex, m_iCurWndIndex); 取得当前正在播放的设备,如果设备正在播放则先关闭g_dlgOutput[i].StopPlay(),如果没有则开始播放StartPlayInsideDecode,关闭自身正在预览的通道,然后开始新通道的预览。

在多画面播放时,必须先选择确定的通道树,判断通道的播放状态,进行关闭或者显示,所以如果之前对设备所有通道进行播放,会没有确定的通道号,这时候不能单独播放,必须先停止全部播放功能。

流程:

1.双击设备树,获取通道或设备;

2.通道则获取当前选择焦点子对话框;设备则开始全部播放;

3.判断当前设备的当前通道是否正在播放,如果正在播放则停止播放;

4.没有播放则进入选择的对话框序号开始准备播放,先停止当前对话框的输出;

4.将通道参数传入子对话框开始1路播放StartPlay;

 

多画面播放时停止单通道画面流程:

当双击设备树播放选中通道正在播放时对其停止播放。StopPlayedChan(iDeviceIndex,iChanIndex);停止播放时遍历所有正在播放的通道,找到需要停止的通道。

 

录像开始/停止/保存:

创建路径,char m_chRecordPath[100];

NET_DVR_SaveRealData(m_lPlayHandle, chRecordPath);//开始并保存

NET_DVR_StopSaveRealData(m_lPlayHandle);//停止

 

2.画面分割

OnCbnSelchangeComboWndNum()  客户端demo中,通过该函数进行1-4-9....的画面分割;

demo中是通过CDlgOutput g_dlgOutput[MAX_OUTPUTS];//video output dialog 来支持设备的多通道播放的;

在ArrangeOutputs(int iNumber)查找关于对话框的位置显示;

对话框创建:

通过combo-box Control控件创建下拉列表控制m_iCurWndNum当前画面窗口数;创建改变对话框数量的消息,调用ArrangeOutputs(int iNumber)函数控制输出对话框在界面中的布局;

void ArrangeOutputs(int iNumber)

{
	if (iNumber == 0)
	{
		return;
	}
	int i = 0;

	int iSqrtNum = 0;//sqrt value of window number
	int iWidth = 0;//window width
	int iHeight = 0;//window height

	iSqrtNum = (int)sqrt((double)iNumber);

	for (i = 0; i < MAX_OUTPUTS; i++)
	{
		g_dlgOutput[i].ShowWindow(SW_HIDE);
	}

    /*m_rectPreviewBG在初始化话里用CRect STCrect;	
	GetDlgItem(IDC_STATIC_PLAY)->GetWindowRect(&STCrect);//获取控件的屏幕坐标
	g_pMainDlg->ScreenToClient(&STCrect);//转换为对话框上的客户坐标
	m_rectPreviewBG = STCrect; 初始化*/

	iWidth = (m_rectPreviewBG.Width() - OUTPUT_INTERVAL*(iSqrtNum - 1)) / iSqrtNum ;//a single pic width in partition
	iHeight = (m_rectPreviewBG.Height() - OUTPUT_INTERVAL*(iSqrtNum - 1)) / iSqrtNum;//a single pic height in partition
	int iPlayIndex = 0;
	

	for (i = 0; i < iNumber; i++)
	{
		iPlayIndex = i;

		g_dlgOutput[iPlayIndex].MoveWindow(m_rectPreviewBG.left + (i%iSqrtNum)*(iWidth + OUTPUT_INTERVAL), \
			m_rectPreviewBG.top + (i / iSqrtNum)*(iHeight + OUTPUT_INTERVAL), iWidth, iHeight, TRUE);
		g_dlgOutput[iPlayIndex].ShowWindow(SW_SHOW);
		g_dlgOutput[iPlayIndex].DrawOutputBorder();  //画边界有点问题
	}
}

(对话框有个很重要的属性需要更改,如果不改为Child,将不能弹出到主对话框上的对应位置)属性改为Child以后,对话框会跟随主对话框移动。三种属性之间的区别:https://blog.csdn.net/dafenqie/article/details/53836150

对话框选择:

添加输出对话框的左键消息OnLButtonDown;点击之后改变当前选中对话框;并且判断声音播放的选择;

添加输出对话框左键双击消息OnLButtonDblClk;切换焦点对话框--是否在播放状态(是的话可以放大缩小)--如果在全屏则还原--如果当前界面输出窗口之后一个则全屏--如果当前界面有多个输出窗口则调整为一个;

双击全屏:

在Output对话框中添加双击响应函数,首先判断是否处于播放状态,如果不是则不做动作直接返回,然后判断最大化标志,如果已经最大化则还原,输出对话框调整为原来的个数。如果没有最大化则先调整输出对话框为单独的一个,然后再次双击则全屏。

全屏FullScreen()的时候先保存当前的对话框位置,然后GetDlgItem(IDC_STATIC_PLAY)->MoveWindow(0, 0, g_iCurScreenWidth, g_iCurScreenHeight, true);将控件移动到全屏大小。如果已经全屏则调用之前保存的对话框位置将其缩小。

void CDlgOutput::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	ChangeCurWinCfg();

	if (m_lPlayHandle < 0 && !g_pMainDlg->m_struDeviceInfo.bEnlarged)
	{
		return;
	}

	if (g_pMainDlg->m_struDeviceInfo.bFullScreen)
	{
		g_pMainDlg->m_struDeviceInfo.bEnlarged = FALSE;
		g_pMainDlg->m_struDeviceInfo.bFullScreen = FALSE;
		g_pMainDlg->GetDlgItem(IDC_COMBO_WNDNUM)->EnableWindow(TRUE);
		g_pMainDlg->FullScreen(g_pMainDlg->m_struDeviceInfo.bFullScreen);
		g_pMainDlg->ArrangeOutputs(g_pMainDlg->m_iCurWndNum);
		g_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->Invalidate(TRUE);
		return;
	}

	g_pMainDlg->GetDlgItem(IDC_COMBO_WNDNUM)->EnableWindow(FALSE);

	if (g_pMainDlg->m_struDeviceInfo.bEnlarged || 1 == g_pMainDlg->m_iCurWndNum)
	{
		g_pMainDlg->m_struDeviceInfo.bFullScreen = TRUE;

		//brush chosen line
		g_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->Invalidate(TRUE);
		g_pMainDlg->FullScreen(g_pMainDlg->m_struDeviceInfo.bFullScreen);
	}
	else
	{//single pic enlarge
		g_pMainDlg->m_struDeviceInfo.bEnlarged = TRUE;
		g_pMainDlg->ArrangeOutputs(1);
	}

	CDialogEx::OnLButtonDblClk(nFlags, point);
}

void CVideoSClientDlg::FullScreen(BOOL bFullScreen)
{
	int iShowStat = bFullScreen ? SW_HIDE : SW_SHOW;

	m_treeDevList.ShowWindow(iShowStat);

	if (bFullScreen)
	{
		//for full screen while backplay
		GetWindowPlacement(&m_struOldWndpl);

		CRect rectWholeDlg;//entire client(including title bar)
		CRect rectClient;//client area(not including title bar)
		CRect rectFullScreen;
		GetWindowRect(&rectWholeDlg);
		RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery, &rectClient);
		ClientToScreen(&rectClient);

		rectFullScreen.left = rectWholeDlg.left - rectClient.left;
		rectFullScreen.top = rectWholeDlg.top - rectClient.top;
		rectFullScreen.right = rectWholeDlg.right + g_iCurScreenWidth - rectClient.right;
		rectFullScreen.bottom = rectWholeDlg.bottom + g_iCurScreenHeight - rectClient.bottom;
		//enter into full screen;
		WINDOWPLACEMENT struWndpl;
		struWndpl.length = sizeof(WINDOWPLACEMENT);
		struWndpl.flags = 0;
		struWndpl.showCmd = SW_SHOWNORMAL;
		struWndpl.rcNormalPosition = rectFullScreen;
		SetWindowPlacement(&struWndpl);
	}
	else
	{
		SetWindowPlacement(&m_struOldWndpl);
	}

	//refresh backgroud box
	if (bFullScreen)
	{
		GetDlgItem(IDC_STATIC_PLAY)->MoveWindow(0, 0, g_iCurScreenWidth, g_iCurScreenHeight, true);
	}
	else
	{
		GetDlgItem(IDC_STATIC_PLAY)->MoveWindow(&m_rectPreviewBG, true);
	}
	PreviewReferShow(!bFullScreen);
	GetDlgItem(IDC_STATIC_PLAY)->ShowWindow(SW_SHOW);

	if (bFullScreen)
	{
		g_dlgOutput[m_iCurWndIndex].MoveWindow(0, 0, g_iCurScreenWidth, g_iCurScreenHeight, TRUE);
		g_dlgOutput[m_iCurWndIndex].ShowWindow(SW_SHOW);
	}

}

 

3.设备管理

右击设备树:

进入OnMenuDeviceAdd()函数,其中在GENERALDEF_H头文件中定义了设备及设备拥有的通道两种结构体

typedef struct STRU_CHANNEL_INFO
{

NE
  • 2
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值