时间:2022-06-15 09:17:39
摘 要:介绍了在Visual C++ 6.0开发环境下利用C语言编程实现视频监控客户端的制作,阐述了编程实现过程中所遇到的问题及解决方法,实现了IPC摄像机媒体流的获取及其在客户端的实时播放,并通过客户端多个窗口同时察看和录制文件。
关键词:IPC摄像机;视频监控;客户端;MFC
中图分类号:TP317.4 文献标识码:A 文章编号:16727800(2013)003012404
1 总体设计
1.1 用户界面
本文本着实用性、美观两个原则在VC++环境下使用MFC设计该界面。基于MFC的客户端界面的设计思路是:①添加各个控件并命名ID及根据实际情况关联变量;②添加初始化代码和消息响应函数。该客户端界面主要采取单文档视图,菜单栏与工具条结合属性控件标签页,主要包含三大模块:实时预览、本地回放以及电子地图。除了这些基本功能之外,还具备如下一些功能:从多个摄像机同时浏览和录制实时视频;支持多种记录模式: 连续、计划、报警和动态侦测;能够处理高帧速和大量的数据;对已记录事件的多种搜索功能,通过Web浏览器、客户端软件甚至PDA客户端进行远程访问;报警管理功能(声音报警、弹出式窗口或Email) 。整体效果见图1。
1.2 定义变量和函数
定义两个主要的结构体如下:
(1)记录设备相关信息的结构体:
typedef struct DeviceList
{
char szIp\[64\];//设备相关IP
int nPort;//设备连接端口号
char szType\[128\];//设备类型
char szUuid\[64\];//设备相关通用唯一识别码
}DeviceList;
(2)记录摄像机相关信息的结构体:
typedef struct CameraInfo
{
int nId;//每个摄像机对应的ID号
int nViewIndex;//每个摄像机对应的视图窗口
TCHAR tszIp\[MAX_TCHAR_LEN\];//对应IP
TCHAR tszOnvifPort\[MAX_TCHAR_LEN\];//对应端口号
TCHAR tszWebPort\[MAX_TCHAR_LEN\];//对应的WEB端口
TCHAR tszCodec\[MAX_TCHAR_LEN\];//对应的解压码
TCHAR tszStatus\[MAX_TCHAR_LEN\];//对应状态用于实时播放还是录制
TCHAR tszCompany\[MAX_TCHAR_LEN\];//对应公司名称
TCHAR tszUserName\[MAX_TCHAR_LEN\];//对应用户名
TCHAR tszPassword\[MAX_TCHAR_LEN\];//对应密码
TCHAR tszUuid\[MAX_TCHAR_LEN\];//对应设备通用唯一识别码
TCHAR tszDeviceType\[MAX_TCHAR_LEN\];//对应设备类型是onvif还是rtsp
TCHAR tszStreamMode\[MAX_TCHAR_LEN\];//对应码流类型
int nAlarmEnable;//是否启用报警
int nRecordEnable;是否启用录制
}CameraInfo;
2 应用程序主要函数设计
2.1 实时播放
在界面的设计上,为了突出本系统的主要功能,将播放模块作为其中一个大的模块,作为其中3个属性页之一显示在系统的主对话框。首先创建一个对话框,并命名ID为IDD-DIALOG-LIVE,依次创建一个子类CLIVEDIG,父类为属性页CResizablePage,该模块功能见图2。
该模块中实现的功能有:
(1)根据已经连接的网络摄像机IP地址搜索并添加设备,接受来自服务器的视频流,通过Live窗口将其拖拽至视图窗口进行实时播放。
(2)实现多路视频同时播放,播放的各个视频处于相互独立的状态,互不影响,实时显示不同设备发送的码流。
(3)实现实时播放视频中的播放、停止、截图、手动存储录像等功能,以及画面显示大小比例调整等。
下面将介绍各功能的具体实现:
(1)首先进行设备扫描,查找已连接的相关设备,将其添加至Live标签页左边所在树并显示出来。根据树状所显示的设备,将其添加至视图窗口进行实时播放,函数原型是:void CLiveDlg::AddToView(CameraInfo *pCameraInfo, CLiveViewDlg *pViewDlg),参数一为每个相关摄像机的信息,参数二为相关显示视图的信息。具体接口函数如下:
void CLiveDlg::AddToView(CameraInfo *pCameraInfo, CLiveViewDlg *pViewDlg)
{
//首先判断当前即将拖拽至视图窗口播放的摄像机是否已经在播放,如果有则不响应,保证其在原来的视图继续播放,这样可以防止同一个摄像机重复播放
if(IsViewExist(pYsCameraInfo->nId)) {//不重复Camera
if(pViewDlg->IsStop()){//如果已经停止播放,则发送播放消息
pViewDlg->SetStop(FALSE);//马上置于false pViewDlg->PostMessage(WM_USER_LIVE_DISPLAY);//发送播放消息
}
return;
}
//进行视图判断,如果即将显示的视图已经有摄像机在播放,则先将其停止
if(pViewDlg->GetViewId() > 0){//有camera在播放
pViewDlg->StopPlay();
}
//通过传入的摄像机ID号(每个摄像机的ID是唯一的)来查找数据库中各个摄像机对应的相关设备信息
DBDevice dbDevice = {0};
DBGetDevice(pCameraInfo->nId, &dbDevice);//通过数据库读取设备信息
pViewDlg->Init(tszUrl, tszName, _T(""), tszDeviceName);//进行一序列Live视图显示初始化
pViewDlg->SetViewId(pCameraInfo->nId);//设置当前显示摄像机ID
pViewDlg->StartPlay();//调用开始实时播放函数
pViewDlg->SetInView(TRUE); //把当前视图窗口设置为TRUE进行显示
}
(2)播放功能。创建一个工具条,命名该类名称为CLiveToolbar,用来实现播放、停止、截图、手动存储录像等功能,以及画面显示大小比例调整等。当用户点击视图所在窗口上的工具栏“播放”按钮时开始播放,“播放”按钮所在接口函数原型为:void CLiveToolbar::OnToolbarLivePlay()。
具体接口函数如下:
void CLiveToolbar::OnToolbarLivePlay()
{
// TODO: Add your command handler code here
if(!m_pViewDlg) return;//不存在相关摄像机
if(m_pViewDlg->IsStop())//当前相关摄像机状态为停止播放,则调用播放函数
m_pViewDlg->StartPlay();
}
(3)停止。当用户点击视图所在窗口上的工具栏“停止”按钮时,则停止当前播放的视频,播放画面要消失,并销毁整个播放链路,以方便用户再次点击播放按钮时,能正确建立新的播放链路。“停止”按钮所在接口函数原型为:void CLiveToolbar::OnToolbarLiveStop()。
具体接口函数如下:
void CLiveToolbar::OnToolbarLiveStop()
{
// TODO: Add your command handler code here
if(!m_pViewDlg) return; //不存在相关摄像机
if(!m_pViewDlg->IsStop())
m_pViewDlg->StopPlay();//当前相关摄像机状态为播放状态,则调用停止函数
m_pViewDlg->m_ipcCtrl.Invalidate(); //停止确认,防止闪屏
}
(4)截图。该功能向用户提供了随时截图并保存为本地硬盘图片文件的功能。用户点击视图所在窗口上的工具栏“停止”按钮时,即默认保存并弹出路径所在文件夹。截图功能函数原型为:
void CLiveToolbar::OnToolbarLiveSaveImage()。
具体接口函数如下:
void CLiveToolbar::OnToolbarLiveSaveImage()
{
// TODO: Add your command handler code here
if(!m_pViewDlg) return; //当前窗口不存在正在播放的摄像机
m_pViewDlg->SaveImage();//调用保存图像函数
//通过数据库保存图像并读取图像所在位置
//具体代码略去
//通过执行ShellExecuteA(NULL, NULL, szPath, NULL, NULL, SW_SHOW);弹出相应的文件夹
}
(5)手动录像。当用户点击视图所在窗口上的工具栏“手动录像”按钮时,则开始录制当前播放的视频,“手动录像”按钮所在接口函数原型为:void CLiveToolbar::OnToolbarLiveRecord()。
具体接口函数如下:
void CLiveToolbar::OnToolbarLiveRecord()
{
//只有对于当前视图存在正在播放的视频时,才能对其进行录像,并且对于正在录像的视图要做出相关提示,通过定义一个全局变量m_bRecord作为标志,首次按下为开始录像(m_bRecord =FALSE),再次按下相同按钮则为结束录像(m_bRecord=TRUE)
//主要代码如下
if(!m_pViewDlg) return; //当前窗口不存在正在播放的摄像机
if(!m_bRecord){//开始录像
m_pViewDlg->StartRecord();
m_bRecord = TRUE;//开始录像后则将m_bRecord置为TRUE
//通过导入另一张图片做出相关提示,作为开始录像标志
hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_TOOLBAR_LIVE2),
IMAGE_BITMAP,
0,0, // cx,cy
LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS );
}else{//结束录像,并做相关保存
m_pViewDlg->StopRecord();
m_bRecord = FALSE;// 结束录像后再次将m_bRecord置为FALSE
//重新导入图片
hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle()
MAKEINTRESOURCE(IDR_TOOLBAR_LIVE),
IMAGE_BITMAP,
0,0, // cx,cy
LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS );
}
}
2.2 本地回放
根据实际应用中的需要,可能需要对监控过程中某些情况的发生进行视频存储,用来作为事后所发生的情况进行实际调查取证,所以系统要实现对来自于不同设备的播放视频可以进行独立、可分辨存储录像和停止存储录像,本模块中实现对于所存储的视频文件进行独立的本地回放。回放部分作为其中三大标签页之一也是整个客户端的重要组成部分。同实时预览部分一样,创建一个新的对话框,命名ID为IDDDIALOGPLAY,并命名子类名称为CPlayDlg,同样继承属性页CResizablePage。本地回放预览见图3。
该部分首先通过搜索按钮查找已经录制完成的各个摄像机对应的文件,通过点击文件进行播放,同时还可以对正在播放的文件进行暂停、快进、快退处理,涉及的主要函数部分如下。
2.2.1 搜索处理
函数原型为void CPlayDlg::OnBtnSearch(),具体接口函数处理如下:
void CPlayDlg::OnBtnSearch()
{
//首先勾选树状选择所要查看的摄像机和与其对应的录制时间,并进行搜索,找出相对应的文件
TCHAR tszCameraName\[128\] = {0};//对应摄像机
TCHAR tszDir\[256\] = {0};//对应存储目录
TCHAR tszDate\[64\] = {0};//对应存储日期
SYSTEMTIME systemTime;//获取时间
UpdateData(TRUE);
HTREEITEM hRootItem = m_treeCamera.GetRootItem();
HTREEITEM hChildItem = m_treeCamera.GetChildItem(hRootItem);
//循环查找对应文件
while(hChildItem){
CameraInfo*pCameraInfo = (CameraInfo*)m_treeCamera.GetItemData(hChildItem);
//获取勾选摄像机对应的信息
if(pCameraInfo && m_treeCamera.GetCheck(hChildItem)) { _sntprintf(tszCameraName,sizeof(tszCameraName)-1,_T("%s%d"),CAMERA_NAME_PREFIX, pCameraInfo->nId);
int nType = m_combo.GetCurSel();
//通过所选择摄像机和对应日期以及录制类型查找对应的文件
FindPlayFiles(tszDir, tszDate, tszCameraName, nType);
}
hChildItem = m_treeCamera.GetNextSiblingItem(hChildItem);
}
UpdateData(FALSE);
}
2.2.2 文件播放处理
通过自定义一个消息WM_USER_PLAYBACK_CLICK来发送播放消息,所定义的消息如下:
ON_MESSAGE(WM_USER_PLAYBACK_CLICK, OnPlayFile)
afx_msg LRESULT OnPlayFile(WPARAM wParam, LPARAM lParam);
调用相应的播放函数OnPlayFile(PARAM wParam, LPARAM lParam),主要处理函数如下:
LRESULT CPlayDlg::OnPlayFile(WPARAM wParam, LPARAM lParam)
{
PlayFileInfo *pPlayFileInfo = (PlayFileInfo*)lParam;
//调用播放函数开始播放文件
m_pPlayViewDlg->StartPlay(pPlayFileInfo->tszFilePath, pPlayFileInfo->nSecondPos);//参数一为录制文件所在文件夹,参数二为该文件对应的录制时长
return 0;
}
2.2.3 快进部分处理
快进部分分为4步处理,点击快进键调用快进处理函数SetSpeedPlay(int nSpeed),一次为快进*1,同理两次快进*2,三次快进*3,四次快进*4,第五次为返回正常播放,具体接口函数如下:
void CPlayViewDlg::SetSpeedPlay(int nSpeed)
{
m_ipcCtrl.SetPlaySpeed(nSpeed);//通过该函数设置相应的快进速度处理
}
2.2.4 快退部分处理
同理,快退部分同样分为4步处理,点击快退键调用快进处理函数SetSpeedPlay(int nSpeed),一次为快退*1,两次快退*2,三次快退*3,四次快退*4,第五次为返回正常播放,函数原型同上。
2.3 电子地图
根据实际应用中的需要,可能对某个区域多个地点进行监控,这样就可以通过载入一张地图,在不同的位置添加不同的摄像机进行实时监控,并且各个部分可以进行预览,电子地图还可以进行缩小放大处理。电子地图部分作为其中三大标签页之一也是整个客户端的重要组成部分。同前面一样,通过创建一个新的对话框,命名ID为IDDDIALOGMAP,并命名子类名称为CMapDlg,同样继承属性页CResizablePage。电子地图预览见图4。
通过发送自定义的消息WM_USER_DRAGDROP_CAMERA 调用OnDragCamera()函数实现拖拽相应的摄像机到地图上显示,自定义消息如下:
ON_MESSAGE(WM_USER_DRAGDROP_CAMERA, OnDragCamera)
afx_msg LRESULT OnDragCamera(WPARAM wParam,LPARAM lParam);
函数具体处理如下:
LRESULT CMapDlg::OnDragCamera(WPARAM wParam, LPARAM lParam)
{
HTREEITEM hDragItem = (HTREEITEM)wParam;//树状中对应的所操作的摄像机
CPoint *pPoint = (CPoint*)lParam;
if(!IsDragInMap(*pPoint))return 0;//如果同一个摄像机已经添加则不再添加
CameraInfo*pCameraInfo = (CameraInfo*)m_treeCamera.GetItemData(hDragItem);
//通过调用该函数弹出对应摄像机实时播放的画面
m_pMapViewDlg->m_mapStatic.DragCamera(pCameraInfo, *pPoint);
return 0;
}
3 结语
本文提出了如何用vc++6.0实现视频监控客户端的编写,该设计方案已经编程实现,经过测试可以达到预期的效果。网络技术以及图像处理技术的飞速发展在扩大了视频监控系统应用范围的同时,也对其性能提出了更高的要求。整个使用过程中,客户端是否具有实用性也是一个很关键的因素。本文中搭建的视频监控客户端可以接收到流畅视频,从而保证了客户端的实用性,它具有良好的通用性,可以广泛应用于社区监控、交通监控、安防监控等大部分网络视频监控系统中。现有的网络视频监控系统可以满足用户的基本需求,但仍存在一些不足,随着无线网络的发展与成熟,在移动监控等性能中的应用将在以后逐渐完善。
参考文献:
\[1\] 杨大全,熊璐.数字化网络视频监控系统的设计与实现\[J\].计算机安全,2008(2).
\[2\] Jon Bates,Tim Tompkins.实用Visual C++6.0教程\[M\].北京:清华大学出版社,2006.
\[3\] 侯俊杰.深入浅出MFC\[M\].第2版.武汉:华中科技大学出版社,2001.
\[4\] (美) Stanley B.Lippman. 侯捷,译.Essential C++中文\[M\].武汉:华中科技大学出版社,2001.