热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

本文记录采用海康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);}}

 


3.设备管理


右击设备树:

进入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消息,取得点击位置判断设备和通道号。


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


  • 预览、回放、通道相关参数配置等接口,通过通道号区分需要访问的设备视频通道,我司设备有DVR、混合型DVR、NVR、网络摄像机等,通道有两种类型:模拟通道和IP通道(数字通道)。DVR视频输入接模拟摄像机,其视音频通道称为模拟通道;网络摄像机通道也是模拟通道;混合型DVR、NVR、CVR设备等设备支持网络摄像机、编码器接入,对应通道称为IP通道(或者数字通道),配置相关参数时需调用IP接入配置参数来进行资源的获取和重新分配。
  • 客户端通过注册设备(NET_DVR_Login_V40)返回的设备信息NET_DVR_DEVICEINFO_V30获取模拟通道个数(byChanNum)、模拟通道起始通道号(byStartChan)和设备支持的最大IP通道数(byIPChanNum+ byHighDChanNum*256)、数字通道起始通道号(byStartDChan)。
  • 从byStartChan到byStartChan+byChanNum-1对应为模拟通道的通道号,IP通道的通道号为byStartDChan到byStartDChan+ (byIPChanNum + byHighDChanNum*256) -1。DVR只有模拟通道,NVR只有IP通道,混合型DVR同时支持模拟通道和IP通道。
  • 如果设备支持IP通道个数大于0,则可以通过远程参数配置接口NET_DVR_GetDVRConfig(配置命令:NET_DVR_GET_IPPARACFG_V40)可以获取得到设备详细的IP资源信息(NET_DVR_IPPARACFG_V40),包括模拟通道是否禁用(byAnalogChanEnable)、IP通道个数(dwDChanNum)、IP通道起始通道号(dwStartDChan)、IP通道取流模式、IP通道有效状态和在线状态等。通过远程参数配置接口NET_DVR_SetDVRConfig(配置命令:NET_DVR_SET_IPPARACFG_V40)可对设备进行IP资源配置,包括添加、修改、删除IP通道等。
  • 混合型DVR、NVR或CVR的IP报警输入和报警输出的通道是在音视频IP通道资源分配好后,由设备自动分配的。如果要对IP报警参数进行配置,首先通过命令NET_DVR_GET_IPALARMINCFG_V40和NET_DVR_GET_IPALARMOUTCFG_V40获取IP报警输入资源(NET_DVR_IPALARMINCFG_V40)和IP报警输出资源(NET_DVR_IPALARMOUTCFG_V40),相关接口:NET_DVR_GetDVRConfig。然后通过命令NET_DVR_SET_ALARMINCFG_V40可以配置报警数相关参数(NET_DVR_ALARMINCFG_V40),包括报警输入名称、报警器类型、布防时间、联动方式等,通过命令NET_DVR_SET_ALARMOUTCFG_V30可以配置报警输出相关参数(NET_DVR_ALARMOUTCFG_V30),比如报警输出名称、布防时间、输出报警延时等,相关接口:NET_DVR_GetDVRConfigNET_DVR_SetDVRConfig
  • 目前SDK私有协议对接时64路以下的NVR的IP通道号是从33开始的,64路以及以上的NVR的IP通道从1开始,起始通道号具体取值可以参考上述说明通过登录接口返回的设备信息获取。但是V5.1.5.2版本以及之后版本新增了NET_DVR_STDXMLConfig透传ISAPI协议的功能(ISAPI协议是基于标准HTTP协议的),该协议URL和XML、JSON数据里面通道号不区分模拟还是数字通道,起始通道号都是1,按顺序递增。因此,不同接口对接存在通道号不一致的问题,需要调用接口NET_DVR_SDKChannelToISAPI(V5.2.7.45以及之后版本支持)进行转换。

直接登陆:

根据标志位判断是否登陆,如果未登陆则登陆DoLoginEx,然后获取设备通道资源DoGetDeviceResoureCfg(),存入m_struDeviceInfo结构体中,接下来根据结构体的信息创建通道树。创建完毕之后进行客户端相应显示,改变标志位。


4.录像回放

有按时间回放和按文件名回放两种方式。


按时间回放:


按文件名回放:

初始化----查找文件---按文件名回放----开始回放----停止回放----注销设备

查找文件:

-----初始化设备------获取到设备登陆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);


5.异常控制

当设备掉线或者断电时应该有异常判断并做出处理;这时应注销设备,释放资源,并可以设置重连等待时间。

通过查阅《网络设备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 ChangePlayBtnState();goto ExceptionOut;}}break;default://g_pMainDlg->AddLog(iDeviceIndex, OPERATION_FAIL_T, "else exception dev[%d]exception[0x%x]", iDeviceIndex, dwType);break;}
ExceptionOut:return;
}

6.报警

 




MFC客户端界面


1.预览控制


画面分割:

实现显示窗口边缘画线的效果:

由于将子对话框显示在主对话框控件上的相对位子,所以我们在主对话框上的控件上画不同颜色的线就可以实现看起来像选中了子对话框的线一样的效果;

代码:

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);}}

 


  • 深入解析MFChttps://www.cnblogs.com/qq78292959/archive/2010/05/20/2077050.html ;
  • MFC消息机制:https://www.cnblogs.com/findumars/p/3948427.html;



Q&A:


设备和通道什么意思?

 


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

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 int (_stdcall *ChannelOpenProc)(long, HANDLE*);

用typedef定义了一种函数指针类型ChannelOpenProc,这种指针可以指向下面这种函数:
int _stdcall f(long, HANDLE*)    //返回int 有一个long类型和一个HANDLE*类型的参数,_stdcall说明该函数参数是从右往左入栈的。于是可以这样使用topenPort类型:ChannelOpenProc pf1; //定义一个指针pf1=f; //指向f


推荐阅读
  • 第一步:PyQt4Designer设计程序界面该部分设计类同VisvalStudio内的设计,改下各部件的objectName!设计 ... [详细]
  • 转载:MFC 获得各类指针、句柄的方法
    2019独角兽企业重金招聘Python工程师标准转载:最近有些人在问MFC编程一些要点,有一些句柄的获取、指针的获取是 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 如何在HTML中获取鼠标的当前位置
    本文介绍了在HTML中获取鼠标当前位置的三种方法,分别是相对于屏幕的位置、相对于窗口的位置以及考虑了页面滚动因素的位置。通过这些方法可以准确获取鼠标的坐标信息。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • ExcelApp#启动excel程序ExcelAppCreateOleObject(“Excel.Application”);#加载文件但不显示文件内容(true表 ... [详细]
  • 九、删除用户 并提交代码到git仓库
    (1)弹框询问用户是否删除数据 ... [详细]
author-avatar
啊哈哈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有