本文记录采用海康SDK二次开发视频监控客户端从0开始的学习知识点,供参考交流。海康视频监控SDK和示例程序可以从官网下载。
添加输出对话框的左键消息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 lineg_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->Invalidate(TRUE);g_pMainDlg->FullScreen(g_pMainDlg->m_struDeviceInfo.bFullScreen);}else{//single pic enlargeg_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 backplayGetWindowPlacement(&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 boxif (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);}}
进入OnMenuDeviceAdd()函数,其中在GENERALDEF_H头文件中定义了设备及设备拥有的通道两种结构体
typedef struct STRU_CHANNEL_INFO
{NET_DVR_DECODERCFG_V30 struDecodercfg; //通道的解码器信息.....
}
}CHANNEL_INFO,*pCHANNEL_INFO;typedef struct STRU_DEVICE_INFO{STRU_DEVICE_INFO *pNext; //自定义设备结构体,并且建立一个单链表的形式.....}LOCAL_DEVICE_INFO, *PLOCAL_DEVICE_INFO;
OnMenuDeviceAdd()函数中则主要创建这两种结构体的对象,然后再显示在控件当中。
触发NM_CLICK消息,取得点击位置判断设备和通道号。
根据标志位判断是否登陆,如果未登陆则登陆DoLoginEx,然后获取设备通道资源DoGetDeviceResoureCfg(),存入m_struDeviceInfo结构体中,接下来根据结构体的信息创建通道树。创建完毕之后进行客户端相应显示,改变标志位。
有按时间回放和按文件名回放两种方式。
初始化----查找文件---按文件名回放----开始回放----停止回放----注销设备
查找文件:
-----初始化设备------获取到设备登陆ID,lLoginID--建立查找文件的接口NET_DVR_FILECOND_V40 m_struFileCond ------初始化接口,设置查找文件类型时间等-----得到返回的文件列表ID,m_lFileHandle = NET_DVR_FindFile_V40(m_lLoginID, &m_struFileCond)------建立查找文件的结构体NET_DVR_FINDDATA_V40 m_struFindData -----清空list控件,开始查找线程,设置对话框状态CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(GetFileThread), this, 0, &dwThreadId);
void CDlgPlayBack::OnBnClickedBtnRemoteSearchList()
{// TODO: 在此添加控件通知处理程序代码UpdateData(TRUE);char szLan[128] = { 0 };if (m_lLoginID <0 || (g_pMainDlg->m_struDeviceInfo.lLoginID <= -1)){MessageBox("查询设备未登陆!"); return;}if (!InitDevice()){MessageBox("获取设备失败!");return;}if (!m_bSearching){memset(&m_struFileCond, 0, sizeof(NET_DVR_FILECOND_V40));m_iFileType = m_comboFileType.GetItemData(m_comboFileType.GetCurSel());m_struFileCond.dwFileType = m_iFileType;m_struFileCond.lChannel = g_pMainDlg->m_struDeviceInfo.struChanInfo[m_iChanIndex].iChanIndex;m_struFileCond.dwIsLocked = 0xFF;m_struFileCond.dwUseCardNo = 0;m_struFileCond.struStartTime.dwYear = (WORD)m_ctDateStart.GetYear();m_struFileCond.struStartTime.dwMOnth= (char)m_ctDateStart.GetMonth();m_struFileCond.struStartTime.dwDay = (char)m_ctDateStart.GetDay();m_struFileCond.struStartTime.dwHour = (char)m_ctTimeStart.GetHour();m_struFileCond.struStartTime.dwMinute = (char)m_ctTimeStart.GetMinute();m_struFileCond.struStartTime.dwSecOnd= (char)m_ctTimeStart.GetSecond();m_struFileCond.struStopTime.dwYear = (WORD)m_ctDateStop.GetYear();m_struFileCond.struStopTime.dwMOnth= (char)m_ctDateStop.GetMonth();m_struFileCond.struStopTime.dwDay = (char)m_ctDateStop.GetDay();m_struFileCond.struStopTime.dwHour = (char)m_ctTimeStop.GetHour();m_struFileCond.struStopTime.dwMinute = (char)m_ctTimeStop.GetMinute();m_struFileCond.struStopTime.dwSecOnd= (char)m_ctTimeStop.GetSecond();m_lFileHandle = NET_DVR_FindFile_V40(m_lLoginID, &m_struFileCond);if (m_lFileHandle <0){g_StringLanType(szLan, "获取文件列表失败!", "Fail to get file list");MessageBox(szLan);return;}m_listRemoteFile.DeleteAllItems();DWORD dwThreadId;if (m_hFileThread == NULL){m_hFileThread = CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(GetFileThread), this, 0, &dwThreadId);}if (m_hFileThread == NULL){g_StringLanType(szLan, "打开查找线程失败!", "Fail to open finding thread!");AfxMessageBox(szLan);return;}g_StringLanType(szLan, "停止查找", "Stop Searching");GetDlgItem(IDC_BTN_REMOTE_SEARCH_LIST)->SetWindowText(szLan);m_bSearching = TRUE;GetDlgItem(IDC_STATIC_REMOTE_SEARCH_STATE)->ShowWindow(SW_SHOW);}else{if (m_hFileThread){m_bQuit = TRUE;//TerminateThread(m_hFileThread, 0);}CloseHandle(m_hFileThread);m_hFileThread = NULL;NET_DVR_FindClose(m_lFileHandle);g_StringLanType(szLan, "查找", "Search");GetDlgItem(IDC_BTN_REMOTE_SEARCH_LIST)->SetWindowText(szLan);m_bSearching = FALSE;GetDlgItem(IDC_STATIC_REMOTE_SEARCH_STATE)->ShowWindow(SW_HIDE);m_iFileNum = 0;}}
GetFileThread查找线程:
查找下一个文件lRet = NET_DVR_FindNextFile_V40(g_pDlgPlayRemoteFile->m_lFileHandle, &struFileInfo);------判断查找结果lRet----NET_DVR_FILE_SUCCESS(成功)/NET_DVR_ISFINDING(等待)/((lRet == NET_DVR_NOMOREFILE) || (lRet == NET_DVR_FILE_NOFIND)(失败)-----查找完毕释放资源关闭线程NET_DVR_FindClose_V30(g_pDlgPlayRemoteFile->m_lFileHandle);
UINT GetFileThread(LPVOID pParam)
{LONG lRet = -1;NET_DVR_FINDDATA_V40 struFileInfo = { 0 };//memset(&struFileInfo, 0, sizeof(struFileInfo));//NET_DVR_FINDDATA_V40 struFileInfo;//memset(&struFileInfo, 0, sizeof(struFileInfo));CString csTmp;char m_szFileName[100];char szLan[128] = { 0 };while (!g_pDlgPlayRemoteFile->m_bQuit){lRet = NET_DVR_FindNextFile_V40(g_pDlgPlayRemoteFile->m_lFileHandle, &struFileInfo);//lRet = NET_DVR_FindNextFile_V50(g_pDlgPlayRemoteFile->m_lFileHandle, &struFileInfo);if (lRet == NET_DVR_FILE_SUCCESS){memcpy(&g_pDlgPlayRemoteFile->m_struFindData, &struFileInfo, sizeof(struFileInfo));strcpy(m_szFileName, struFileInfo.sFileName);g_pDlgPlayRemoteFile->m_listRemoteFile.InsertItem(g_pDlgPlayRemoteFile->m_iFileNum, m_szFileName, 0);if (struFileInfo.dwFileSize / 1024 == 0){csTmp.Format("%d", struFileInfo.dwFileSize);}else if (struFileInfo.dwFileSize / 1024 > 0 && struFileInfo.dwFileSize / (1024 * 1024) == 0){csTmp.Format("%dK", struFileInfo.dwFileSize / 1024);}else// if (){csTmp.Format("%dM", struFileInfo.dwFileSize / 1024 / 1024);//different from hard disk capacity, files need tranformation}//csTmp.Format("%d",struFileInfo.dwFileSize);g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 1, csTmp);//开始时间csTmp.Format("%04d%02d%02d%02d%02d%02d", struFileInfo.struStartTime.dwYear, \struFileInfo.struStartTime.dwMonth, struFileInfo.struStartTime.dwDay, \struFileInfo.struStartTime.dwHour, struFileInfo.struStartTime.dwMinute, \struFileInfo.struStartTime.dwSecond); g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 2, csTmp);//结束时间csTmp.Format("%04d%02d%02d%02d%02d%02d", struFileInfo.struStopTime.dwYear, struFileInfo.struStopTime.dwMonth, \struFileInfo.struStopTime.dwDay, struFileInfo.struStopTime.dwHour, \struFileInfo.struStopTime.dwMinute, struFileInfo.struStopTime.dwSecond); g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 3, csTmp);if (struFileInfo.byLocked == 1){g_StringLanType(szLan, "锁定", "Lock");csTmp.Format(szLan);}else if (struFileInfo.byLocked == 0){g_StringLanType(szLan, "未锁", "Unlock");csTmp.Format(szLan);}g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 4, csTmp);g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemData(g_pDlgPlayRemoteFile->m_iFileNum, struFileInfo.byLocked);csTmp.Format("%d", struFileInfo.dwFileIndex);g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 5, csTmp);csTmp.Format("%d", struFileInfo.byFileType);g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 6, csTmp);if (struFileInfo.byStreamType == 0){g_StringLanType(szLan, "主码流", "Main Stream");csTmp.Format(szLan);}else if (struFileInfo.byStreamType == 1){g_StringLanType(szLan, "子码流", "Sub Stream");csTmp.Format(szLan);}else if (struFileInfo.byStreamType == 2){g_StringLanType(szLan, "三码流", "Three Stream");csTmp.Format(szLan);}g_pDlgPlayRemoteFile->m_listRemoteFile.SetItemText(g_pDlgPlayRemoteFile->m_iFileNum, 7, csTmp);g_pDlgPlayRemoteFile->m_iFileNum++;}else{if (lRet == NET_DVR_ISFINDING){Sleep(5);continue;}if ((lRet == NET_DVR_NOMOREFILE) || (lRet == NET_DVR_FILE_NOFIND)){g_StringLanType(szLan, "查找", "Search");g_pDlgPlayRemoteFile->GetDlgItem(IDC_BTN_REMOTE_SEARCH_LIST)->SetWindowText(szLan);g_pDlgPlayRemoteFile->m_bSearching = FALSE;(g_pDlgPlayRemoteFile->GetDlgItem(IDC_STATIC_REMOTE_SEARCH_STATE))->ShowWindow(SW_HIDE);//g_pMainDlg->AddLog(g_pDlgRemoteFile->m_iDeviceIndex, OPERATION_SUCC_T, "NET_DVR_FindNextFile_V50 file num[%d]", g_pDlgRemoteFile->m_iFileNum);// if (g_pDlgRemoteFile->m_iFileNum > 0)// {// g_pMainDlg->AddLog(g_pDlgRemoteFile->m_iDeviceIndex, OPERATION_SUCC_T, "NET_DVR_FindNextFile_V30 file num[%d]", g_pDlgRemoteFile->m_iFileNum);// g_StringLanType(szLan, "获取文件列表结束,文件已经全部列出", "Finish to get file list, and documents have all been listed");// AfxMessageBox(szLan);// }// else// {// g_StringLanType(szLan, "获取文件列表结束,没有录像文件", "Finish to get file list, and There is no record file");// AfxMessageBox(szLan);// }g_pDlgPlayRemoteFile->m_iFileNum = 0;if (g_pDlgPlayRemoteFile->m_nPlayHandle == -1){//g_pDlgPlayRemoteFile->SetStopState();g_pDlgPlayRemoteFile->GetDlgItem(IDC_BTN_REMOTE_FILE_PLAY)->EnableWindow(FALSE);}break;}else{g_pDlgPlayRemoteFile->GetDlgItem(IDC_BTN_REMOTE_SEARCH_LIST)->SetWindowText("查找");g_pDlgPlayRemoteFile->m_bSearching = FALSE;(g_pDlgPlayRemoteFile->GetDlgItem(IDC_STATIC_REMOTE_SEARCH_STATE))->ShowWindow(SW_HIDE);//g_pMainDlg->AddLog(g_pDlgRemoteFile->m_iDeviceIndex, OPERATION_FAIL_T, "NET_DVR_FindNextFile_V50");g_StringLanType(szLan, "由于服务器忙,或网络故障,获取文件列表异常终止", "Since the server is busy, or network failure, abnormal termination of access to the file list");AfxMessageBox(szLan);g_pDlgPlayRemoteFile->m_iFileNum = 0;if (g_pDlgPlayRemoteFile->m_nPlayHandle == -1){//g_pDlgPlayRemoteFile->SetStopState();g_pDlgPlayRemoteFile->GetDlgItem(IDC_BTN_REMOTE_FILE_PLAY)->EnableWindow(FALSE);}break;}}}CloseHandle(g_pDlgPlayRemoteFile->m_hFileThread);g_pDlgPlayRemoteFile->m_hFileThread = NULL;NET_DVR_FindClose_V30(g_pDlgPlayRemoteFile->m_lFileHandle);g_pDlgPlayRemoteFile->m_lFileHandle = -1;g_pDlgPlayRemoteFile->m_bSearching = FALSE;return 0;
}
按文件名回放----开始回放:
判断选中的文件名----建立NET_DVR_PLAY_BY_NAME_PARA struPlayByName,播放接口----获取正放/倒放控制入口号,m_nPlayHandle = NET_DVR_PlayBackByName_V50(m_lLoginID, &struPlayByName)/NET_DVR_PlayBackReverseByName_V50(m_lLoginID, &struPlayByName); ----改变控件状态----开始播放NET_DVR_PlayBackControl(m_nPlayHandle, NET_DVR_PLAYSTART, NULL, NULL)-----设置进度条SetTimer(REMOTE_PLAY_STATE_TIMER, 1000, NULL);
void CDlgPlayBack::PlayBack()
{UpdateData(TRUE);CString csFileName;int iFileSelPos = 0;HWND hPlayWnd = m_staticPlayWnd.GetSafeHwnd();POSITION posItem = m_listRemoteFile.GetFirstSelectedItemPosition();char szLan[128] = { 0 };if (posItem == NULL){g_StringLanType(szLan, "请选择要播放的文件!", "Please select the file to play");AfxMessageBox(szLan);return;}iFileSelPos = m_listRemoteFile.GetNextSelectedItem(posItem);csFileName.Format("%s", m_listRemoteFile.GetItemText(iFileSelPos, 0));if (csFileName.IsEmpty()){return;}sprintf(m_szFileName, "%s", csFileName);// remoteplay_info.srcfilename=m_szFileName;if (m_nPlayHandle >= 0){if (NET_DVR_StopPlayBack(m_nPlayHandle)){//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_SUCC_T, "NET_DVR_StopPlayBack");}m_nPlayHandle = -1;Sleep(400);}NET_DVR_PLAY_BY_NAME_PARA struPlayByName = { 0 };memcpy(struPlayByName.szFileName, m_szFileName, 100);struPlayByName.hWnd = hPlayWnd;//正放倒放控制if (!((CButton *)GetDlgItem(IDC_RADIO_REVERSE))->GetCheck()){memcpy(m_szCurFileName, m_szFileName, 200);m_nPlayHandle = NET_DVR_PlayBackByName_V50(m_lLoginID, &struPlayByName);}else{memcpy(m_szCurFileName, m_szFileName, 200);m_nPlayHandle = NET_DVR_PlayBackReverseByName_V50(m_lLoginID, &struPlayByName);}if (m_nPlayHandle == -1){//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_FAIL_T, "NET_DVR_PlayBackByName[%s]", m_szFileName);char szLan[1024] = { 0 };g_StringLanType(szLan, "回放失败!", "Play Back Failed!");AfxMessageBox(szLan);return;}//改变播放控件状态GetDlgItem(IDC_BTN_REMOTE_FILE_PLAY)->SetWindowText("暂停");GetDlgItem(IDC_BTN_REMOTE_FILE_PLAY)->EnableWindow(TRUE);GetDlgItem(IDC_BTN_REMOTE_FILE_STOP)->EnableWindow(TRUE);//开始if (NET_DVR_PlayBackControl(m_nPlayHandle, NET_DVR_PLAYSTART, NULL, NULL))//NET_DVR_PlayBackControl(m_nPlayHandle, NET_DVR_PLAYSTART, m_iOffset, NULL){//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_SUCC_T, "NET_DVR_PLAYSTART offset[%d]", m_iOffset);}else{//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_FAIL_T, "NET_DVR_PLAYSTART offset[%d]", m_iOffset);char szLan[1024] = { 0 };NET_DVR_StopPlayBack(m_nPlayHandle);//SetStopState();GetDlgItem(IDC_BTN_REMOTE_FILE_PLAY)->SetWindowText("播放");m_nPlayHandle = -1;g_StringLanType(szLan, "回放失败!", "NET_DVR_PLAYSTART Failed!");AfxMessageBox(szLan);return;}//设置定时器控制进度条(先屏蔽进度条功能)m_bGetMaxTime = FALSE;SetTimer(REMOTE_PLAY_STATE_TIMER, 1000, NULL);
}
设置进度条:
用定时器实现,调用NET_DVR_PlayBackControl(m_nPlayHandle, NET_DVR_PLAYGETPOS, 0, &nPos)来获取进度,m_sliderPlayProgress.SetPos(nPos);设置进度条
void CDlgPlayRemoteTime::OnTimer(UINT_PTR nIDEvent)
{// TODO: 在此添加消息处理程序代码和/或调用默认值DWORD nPos;char szLan[128] = { 0 };// NET_DVR_TIME struOsdTime;if (nIDEvent == PLAYBYTIME_TIMER){if (m_nPlayHandle >= 0){if (NET_DVR_PlayBackControl(m_nPlayHandle, NET_DVR_PLAYGETPOS, 0, &nPos)){//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_SUCC_T, "NET_DVR_PLAYGETPOS pos[%d]", nPos);m_sliderPlayProgress.SetPos(nPos);}if (nPos > 100){//g_pMainDlg->AddLog(m_iDeviceIndex, OPERATION_FAIL_T, "NET_DVR_PLAYGETPOS %d, err = %d", nPos, NET_DVR_GetLastError());StopPlay();g_StringLanType(szLan, "由于网络原因或DVR忙,回放异常终止!", " Due to network reasons or DVR is busy, playback abnormal termination");AfxMessageBox(szLan);}if ((((CButton *)GetDlgItem(IDC_RIO_REVERSE))->GetCheck()) ? (nPos == 100) : (nPos == 0)){StopPlay();g_StringLanType(szLan, "按时间回放结束", "playback by time over");AfxMessageBox(szLan);}}}CDialogEx::OnTimer(nIDEvent);
}
结束回放:
调用NET_DVR_StopPlayBack(m_nPlayHandle);
当设备掉线或者断电时应该有异常判断并做出处理;这时应注销设备,释放资源,并可以设置重连等待时间。
通过查阅《网络设备SDK》发现有NET_DVR_SetExceptionCallBack_V30 等函数可以通过回调函数的方式来获取到不同类型的异常并处理。
NET_DVR_SetExceptionCallBack_V30(WM_NULL/*WM_MYCOMMAND*/, NULL/*this->m_hWnd*/, g_ExceptionCallBack, NULL);
NET_DVR_SetReconnect(5000, m_struDeviceInfo.bReconnect); //不调用的化系统也默认启动,重连时间为5S
回调函数g_ExceptionCallBack中可以获取到各种异常消息和设备通道ID,由于系统可以自动设置重连操作,所以在异常回调函数中可以根据需要做其他异常处理,一般情况各种异常都需要记录日志,这里当预览发生异常时关闭对应的输出对话框的输出,并改变对话框状态。
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{UNREFERENCED_PARAMETER(pUser);if (g_bExitDemo) //退出时不需要发送异常消息{return;}int i = 0;//int iDeviceIndex = -1;//CString sTemp;//char szTmpBuf[256] = {0};switch (dwType){case EXCEPTION_ALARM: //network exception while uploading alarmbreak;case EXCEPTION_PREVIEW: // exception while preview//g_pMainDlg->AddLog(iDeviceIndex, OPERATION_FAIL_T, "PREVIEW dev[%d]exception[%d]", iDeviceIndex, dwType);for (i = 0; i
ExceptionOut:return;
}
实现显示窗口边缘画线的效果:
由于将子对话框显示在主对话框控件上的相对位子,所以我们在主对话框上的控件上画不同颜色的线就可以实现看起来像选中了子对话框的线一样的效果;
代码:
void CDlgOutput::DrawOutputBorder()
{if (!IsWindowVisible()){return;}CPen *pOldPen = NULL;CPen pPen;CRect rc(0, 0, 0, 0);GetWindowRect(&rc); //获取到对话框在屏幕上的绝对坐标g_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->ScreenToClient(&rc); //将对话框的绝对坐标转化到控件IDC_STATIC_PLAY中的相对坐标if (g_pMainDlg->m_iCurWndIndex == m_iSubWndIndex){pPen.CreatePen(PS_SOLID, 2, RGB(0, 255, 0));//green}else{pPen.CreatePen(PS_SOLID, 2, RGB(125, 125, 116));}// rc.top-=1;// rc.left-=1;rc.right += OUTPUT_INTERVAL / 2;rc.bottom += OUTPUT_INTERVAL / 2;CDC *pDC = g_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->GetDC();ASSERT(pDC);pDC->SelectStockObject(NULL_BRUSH);pOldPen = pDC->SelectObject(&pPen);pDC->Rectangle(&rc);if (pOldPen){pDC->SelectObject(pOldPen);}ReleaseDC(pDC);
}
获取窗口当前大小rect,用于全屏恢复-----隐藏其他控件----将窗口移动到全屏(这里代码做了一个额外操作,及当前输出画面不是一个时先放大为一个画面,然后再次点击则全屏)
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 lineg_pMainDlg->GetDlgItem(IDC_STATIC_PLAY)->Invalidate(TRUE);g_pMainDlg->FullScreen(g_pMainDlg->m_struDeviceInfo.bFullScreen); //全屏/恢复}else{//single pic enlargeg_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);GetDlgItem(IDC_BUTTON_LOGIN)->ShowWindow(iShowStat);GetDlgItem(IDC_IPADDRESS_DEV)->ShowWindow(iShowStat);GetDlgItem(IDC_EDIT_PORT)->ShowWindow(iShowStat);GetDlgItem(IDC_EDIT_USER)->ShowWindow(iShowStat);GetDlgItem(IDC_EDIT_PWD)->ShowWindow(iShowStat);if (bFullScreen){//for full screen while backplayGetWindowPlacement(&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 boxif (bFullScreen){GetDlgItem(IDC_STATIC_PLAY)->MoveWindow(0, 0, g_iCurScreenWidth, g_iCurScreenHeight, true);}else{GetDlgItem(IDC_STATIC_PLAY)->MoveWindow(&m_rectPreviewBG, true);}PreviewReferShow(!bFullScreen);if (bFullScreen){g_dlgOutput[m_iCurWndIndex].MoveWindow(0, 0, g_iCurScreenWidth, g_iCurScreenHeight, TRUE);g_dlgOutput[m_iCurWndIndex].ShowWindow(SW_SHOW);}}
Declare all the global variables in ClientDemo.cpp, then add extern to them in ClientDemo.h.
if other files need to call these global variables, just include ClientDemo.h file?
把全局变量定义在cpp里然后在头文件中声明extern,这样符合头文件中不做定义的习惯。如果在头文件中定义,很容易出现包含相同头文件时,变量被重复定义的问题
用typedef定义了一种函数指针类型ChannelOpenProc,这种指针可以指向下面这种函数:
int _stdcall f(long, HANDLE*) //返回int 有一个long类型和一个HANDLE*类型的参数,_stdcall说明该函数参数是从右往左入栈的。于是可以这样使用topenPort类型:ChannelOpenProc pf1; //定义一个指针pf1=f; //指向f