本文记录采用海康SDK二次开发视频监控客户端从0开始的学习知识点,供参考交流。海康视频监控SDK和示例程序可以从官网下载。
目录
这段代码怎么理解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