来源: Visual C++ 编程词典 / 方案中心
在开发视频监控系统时,首先需要了解开发包的操作流程,才能够进行视频开发。以天敏 VC4000 为例,其基本开发思路如下:
视频服务器端首先 调用 VCAInitSdk 函数初始化开发包,然后调用 VCAGetDevNum 函数 获得系统中可以显示多少路视频,通过该数据值创建 相应的视频显示窗口,接着调用 VCARegVidCapCallBack 和 VCARegVidMpegCallBack 函数注册视频捕捉及压缩回调函数,然后调用 VCAOpenDevice 函数打开设备,调用 VCAStartVideoPreview 函数开始预览,调用 MTALoadLibrary 函数初始化网络包。最后在系统退出时调用 VCAUnInitSdk 函数释放开发包资源。
视频服务器效果如图 1 所示。
图 1 视频服务器效果图
客户端程序首先调用 MTACreateVideoDevice 函数创建视频显示窗口,调用 MTASetSplitMode 函数设置窗口切分模式。然后调用 MTALoadLibrary 函数初始化网络库,调用 MTANewCall 函数申请呼叫资源,接着调用 MTAMakeCall 函数请求媒体传输服务,调用 MTASetVideoOut 函数设置视频显示窗口,最后调用 MTAClearCall 函数释放网络包资源。
客户端程序效果如图 2 所示。
图 2 视频捕捉客户端
( 1 )创建一个基于对话框的应用程序,在对话框中添加按钮控件,如图 3 所示。
图 3 对话框设计
( 2 )将 Sa7134Capture.dll 、 MPG4c32.dll 、 MediaTransmit.dll 、 MediaTransmit.lib 、 MediaTransmit.lib 、 Sa7134Capture.h 和 MediaTransmit.h 文件添加到当前工程中。
( 3 )从 CStatic 类派生一个子类 CPreView ,作为视频显示的子窗口。在该类中添加如下成员变量:
BOOL m_Selected ; // 当前是否被选中
static int m_CurIndex ; // 预览窗口当前索引
int m_Index ; // 本窗口索引
BOOL m_ShowImage ; // 是否显示图像 , 即该路是否有信号
BOOL m_Dbled ; // 双击时窗口是否填充父窗口
PreState m_Stop ; // 预览状态
CBitmap m_Bitmap ; // 无信号是显示的位图资源
( 4 )处理 CPreView 类的 WM_PAINT 消息,当窗口关联的视频卡号有信号并处于选中状态,绘制绿色的边框,在窗口关联的视频卡号无信号时,绘制一幅位图表示当前无信号。
void CPreView::OnPaint ()
{
CPaintDC dc( this);
CRect rc ;
GetClientRect ( rc );
if (m_Index ==m_CurIndex )
m_Selected = TRUE;
else
m_Selected = FALSE;
if (m_Selected )
{
CPen pen( PS_SOLID,1,RGB(0,255,0));
dc.SelectObject ( &pen);
dc.Rectangle ( rc );
}
else
{
CPen pen( PS_SOLID,1,RGB(55,55,55));
dc.SelectObject ( &pen);
dc.Rectangle ( rc );
}
if (m_ShowImage )
{
CBrush brush (RGB( 255,0,255));
dc.SelectObject ( &brush);
rc.DeflateRect ( 1,1,1,1);
dc.FillRect ( rc,&brush );
}
else
{
CDC memDC ;
memDC.CreateCompatibleDC ( &dc);
memDC.SelectObject ( &m_Bitmap );
BITMAP bInfo ;
m_Bitmap.GetBitmap ( &bInfo );
int x = bInfo.bmWidth ;
int y = bInfo.bmHeight ;
dc.StretchBlt (1,1,rc.Width()-2,rc.Height()-2,&memDC,1,1,x,y,SRCCOPY);
memDC.DeleteDC ( );
}
}
( 5 )处理 CPreView 类的 WM_LBUTTONDOWN 消息,将当前窗口标记为选中状态(具有绿色的边框)。
void CPreView::OnLButtonDown (UINT nFlags , CPoint point)
{
int preIndex = m_CurIndex ;
if (m_CurIndex ==-1)
preIndex = m_Index ;
m_CurIndex = m_Index ;
((CPanel *)GetParent ())->RefreshWnd ( preIndex,m_Index );
CStatic::OnLButtonDown ( nFlags , point);
}
( 6 )处理 CPreView 类的 WM_SIZE 消息,在窗口大小改变时更新视频显示窗口。
void CPreView::OnSize (UINT nType , int cx , int cy)
{
if (m_ShowImage )
{
if (!m_Stop )
{
VCAUpdateOverlayWnd ( m_hWnd );
VCAUpdateVideoPreview ( m_Index,m_hWnd );
}
}
CStatic::OnSize ( nType , cx , cy);
}
( 7 )处理 CPreView 类的 WM_LBUTTONDBLCLK 消息,当用户双击某个窗口时,将窗口放大到父窗口的大小或者恢复窗口为原来的大小。
void CPreView::OnLButtonDblClk (UINT nFlags , CPoint point)
{
if (m_Stop ) // 在停止预览时禁止双击
return ;
m_Dbled = ! m_Dbled ;
// 记录原始区域大小
CRect rc,prc ;
GetClientRect ( rc );
this ->MapWindowPoints (GetParent (),rc );
GetParent ( )->GetClientRect (prc );
int div = ((CPanel *)GetParent ())->m_Div ;
prc.DeflateRect ( div,div,div,div );
if (m_Dbled )
{
((CPanel *)GetParent ())->ShowOnly ( m_Index );
SetWindowPos ( &wndTop,prc.left,prc.top,prc.Width (),
prc.Height ( ),SWP_SHOWWINDOW);
Invalidate( );
if (m_ShowImage )
{
VCAUpdateOverlayWnd ( m_hWnd );
VCAUpdateVideoPreview ( m_Index,m_hWnd );
}
}
else
{
((CPanel *)GetParent ())->ShowAll ( );
((CPanel *)GetParent ())->OnSize ( 0,0,0);
Invalidate( );
if (m_ShowImage )
{
VCAUpdateOverlayWnd ( m_hWnd );
VCAUpdateVideoPreview ( m_Index,m_hWnd );
}
}
CStatic::OnLButtonDblClk ( nFlags , point);
}
( 8 )创建一个对话框类 CPanel ,作为预览窗口的父窗口。修改对话框资源的风格为“ Child ”,边框为“ Thin ”。
( 9 )向 CPanel 类中添加如下成员变量:
CPreView * m_pList ; // 预览窗口
UINT m_Num ; // 记录预览窗口的数量
UINT m_UnitNum ; //m_Num 的开平方
UINT m_Div ; // 预览窗口的间隔
CRect m_PreRC ; // 所有预览窗口的显示区域
( 10 )向 CPanel 类中添加 CreatePreWnd 方法创建预览窗口。
BOOL CPanel::CreatePreWnd ( UINT uNum )
{
if (uNum ==0)
return FALSE;
m_UnitNum = uNum ;
m_Num = pow ( uNum,2);
m_pList = new CPreView [ m_Num ];
CRect rc ;
GetClientRect ( rc );
// 预览窗口的宽度
int width = (rc.Width ()-(uNum+1)*m_Div )/uNum ;
// 预览窗口的高度
int height = (rc.Height ()-(uNum+1)*m_Div )/uNum ;
for (int i =0; i <m_Num ; i ++)
{
int row = i/uNum+1;
int col = i % uNum+1;
int x = col *m_Div +(col-1)*width;
int y = row*m_Div +(row-1)*height;
CRect rect (x,y,x +width,y+height );
m_pList [i ].Create( "www",WS_CHILD|WS_VISIBLE|SS_BLACKFRAME|SS_NOTIFY,rect,this );
m_pList [i ].m_Index = i ;
}
return TRUE;
}
( 11 )向 CPanel 类中添加 RefreshWnd 方法刷新窗口。
void CPanel::RefreshWnd (int preIndex,int curIndex )
{
m_pList [ preIndex ].Invalidate();
m_pList [ curIndex ].Invalidate();
}
( 12 )向 CPanel 类中添加 ShowOnly 方法显示窗口。
void CPanel::ShowOnly (int Index)
{
for (int i =0; i <m_Num ; i ++)
{
if (i ==Index)
{
m_pList [i ].ShowWindow ( SW_SHOW);
}
else
{
m_pList [i ].ShowWindow ( SW_HIDE);
}
}
}
( 13 )处理 CPanel 类的 WM_SIZE 消息,在窗口大小改变时,调整预览窗口的大小。
void CPanel::OnSize (UINT nType , int cx , int cy)
{
CDialog::OnSize ( nType , cx , cy);
if (m_Num >0) // 创建了预览窗口
{
CRect rc ;
GetClientRect ( rc );
m_PreRC = rc ;
m_PreRC.DeflateRect ( m_Div,m_Div,m_Div,m_Div );
// 预览窗口的宽度
int width = (rc.Width ()-(m_UnitNum+1)*m_Div )/m_UnitNum ;
// 预览窗口的高度
int height = (rc.Height ()-(m_UnitNum+1)*m_Div )/m_UnitNum ;
for (int i =0; i <m_Num ; i ++)
{
int row = i / m_UnitNum+1;
int col = i % m_UnitNum+1;
int x = col *m_Div +(col-1)*width;
int y = row*m_Div +(row-1)*height;
CRect rect (x,y,x +width,y+height );
if (m_pList [i ].IsWindowVisible ())
{
if (!m_pList [i ].m_Dbled )
{
m_pList [i ].MoveWindow ( rect );
m_pList [i ].Invalidate( );
}
else
{
m_pList [i ].MoveWindow ( m_PreRC );
m_pList [i ].Invalidate( );
}
}
}
}
}
( 14 )在主对话框中添加如下成员变量。
int m_DevNum ; // 当前芯片数 , 也就是有多少路
BOOL m_bStopPreview ; // 是否停止预览
BOOL m_bStartCap ; // 是否开始捕捉
CPanel m_Frame ; // 预览窗口的父窗口
( 15 )在主对话框初始化时初始化 SDK 开发包。
BOOL CCaptureDlg::OnInitDialog ()
{
CDialog::OnInitDialog ( );
CButton * pBmpButton = (CButton *) GetDlgItem ( IDC_BMP);
pBmpButton ->SetCheck (1);
ASSERT( (IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT( IDM_ABOUTBOX < 0xF000);
CMenu * pSysMenu = GetSystemMenu ( FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu ;
strAboutMenu.LoadString ( IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty ())
{
pSysMenu ->AppendMenu (MF_SEPARATOR);
pSysMenu ->AppendMenu (MF_STRING, IDM_ABOUTBOX, strAboutMenu );
}
}
SetIcon ( m_hIcon , TRUE);
SetIcon ( m_hIcon , FALSE);
CString strTemp ;
m_Frame.Create ( IDD_PANEL_DIALOG,NULL);
CRect rc ;
GetWindowRect ( rc );
m_Frame.SetWindowPos ( &wndTop,0,0,rc.Width()-120,rc.Height()-10,SWP_SHOWWINDOW);
m_Frame.ShowWindow ( SW_SHOW);
// 初始化网络开发包
MTALoadLibrary ( 1001,WORK_AS_SERVER);
CButton * pVGAButton = (CButton *) GetDlgItem ( IDC_VGA);
CButton * pPCIButton = (CButton *) GetDlgItem ( IDC_PCI);
CString str = "temp";
GetPrivateProfileString (" 显卡设置 "," 类型 ","VGA"
,str.GetBuffer (0), 10, "./syssetting.ini");
BOOL ret;
if (str =="VGA")
{
pVGAButton ->SetCheck (1);
pPCIButton ->SetCheck (0);
// 初始化 SDK
ret = VCAInitSdk (m_Frame.m_hWnd,PCI_VIEDOMEMORY );
}
else
{
ret = VCAInitSdk (m_Frame.m_hWnd,PCI_MEMORY_VIDEOMEMORY );
pVGAButton ->SetCheck (0);
pPCIButton ->SetCheck (1);
}
if (ret)
{
// 获得几路视频
m_DevNum = VCAGetDevNum ();
m_Frame.CreatePreWnd ( (int )sqrt (m_DevNum ));
for (int i = 0; i <m_DevNum ; i ++)
{
VCARegVidCapCallBack ( i,CapCallBack );
VCARegVidMpegCallBack ( i,VCAPrcVidMpegCallBack );
VCAOpenDevice ( i,m_Frame.m_pList [i ].m_hWnd );
BOOL ret = VCAStartVideoPreview ( i );
m_Frame.UpdateAllPreView ( );
// VCAUpdateOverlayWnd ( m_Frame.GetSafeHwnd ());
// VCAUpdateVideoPreview ( i,m_Frame.m_pList [i ].m_hWnd );
}
m_bStopPreview = FALSE;
m_bStartCap = FALSE;
}
SetTimer ( 1,1000,NULL);
return TRUE;
}
( 16 )处理主对话框的 WM_TIMER 消息,判断某一路是否有视频信号。
void CCaptureDlg::OnTimer (UINT nIDEvent )
{
// 判断某一路是否有信号
eFieldFrequency frequency;
for (int i = 0; i < m_DevNum ; i ++)
{
VCAGetVidFieldFrq ( i,frequency );
if (frequency==FIELD_FREQ_0HZ) // 无信号
{
if (m_Frame.m_pList [i ].m_ShowImage == TRUE)
{
m_Frame.m_pList [i ].m_ShowImage = FALSE;
m_Frame.m_pList [i ].Invalidate( );
}
}
else
{
if (m_Frame.m_pList [i ].m_ShowImage ==FALSE)
{
m_Frame.m_pList [i ].m_ShowImage = TRUE;
m_Frame.m_pList [i ].Invalidate( );
VCAUpdateOverlayWnd ( m_Frame.m_pList [i ].m_hWnd );
VCAUpdateVideoPreview ( i,m_Frame.m_pList [i ].m_hWnd );
}
}
}
CDialog::OnTimer ( nIDEvent );
}
( 17 )处理主对话框的 WM_SIZE 消息,在对话框大小改变时调整视频显示窗口的父窗口,从而间接调整预览窗口。
void CCaptureDlg::OnSize (UINT nType , int cx , int cy)
{
CDialog::OnSize ( nType , cx , cy);
CRect rc,frc ;
GetClientRect ( rc );
m_Frame.GetClientRect ( frc );
m_Frame.SetWindowPos ( NULL,0,0,frc.Width(),rc.Height (),0);
m_Frame.ShowWindow ( SW_SHOW);
}
( 18 )处理“停止预览”按钮的单击事件,停止或开始视频预览。
void CCaptureDlg::OnStoppreview ()
{
int Index = m_Frame.IsDbled ();
if (!m_bStopPreview )
{
m_bStopPreview = ! m_bStopPreview ;
for (int i =0 ; i < m_DevNum ; i ++)
{
if (m_Frame.m_pList [i ].m_ShowImage )
{
m_Frame.m_pList [i ].m_Stop = psStop ;
VCAStopVideoPreview ( i );
VCAUpdateOverlayWnd ( m_Frame.m_hWnd );
m_Frame.m_pList [i ].Invalidate( );
}
}
m_StopPreview.SetWindowText (" 开始预览 ");
}
else
{
m_bStopPreview = ! m_bStopPreview ;
m_StopPreview.SetWindowText (" 停止预览 ");
for (int i =0 ; i < m_DevNum ; i ++)
{
if (m_Frame.m_pList [i ].m_ShowImage )
{
if (Index == -1)
{
VCAStartVideoPreview ( i );
VCAUpdateVideoPreview ( i,m_Frame.m_pList [i ].m_hWnd );
m_Frame.m_pList [i ].m_Stop = psPreview ;
}
else
{
m_Frame.m_pList [i ].m_Stop = psPreview ;
VCAStartVideoPreview ( Index);
VCAUpdateVideoPreview ( Index,m_Frame.m_pList [Index].m_hWnd );
}
m_Frame.m_pList [i ].Invalidate( );
}
}
}
}
( 19 )处理“网传”按钮的单击事件,开始或停止网络传输。
void CCaptureDlg::OnCapture ()
{
if (!m_bStartCap ) // 开始捕捉
{
m_bStartCap = ! m_bStartCap ;
m_Capture.SetWindowText (" 停止 ");
for (int i = 0 ; i < m_DevNum ; i ++)
{
if (m_Frame.m_pList [i ].m_ShowImage )
{
char data[10];
memset ( data,0,10);
itoa ( i,data,10);
char buffer[50]= "C://WW";
strcat ( buffer,data );
strcat ( buffer,".avi ");
VCASetVidCapSize ( i,240,320);
VCASetVidCapFrameRate ( i,25);
VCASetBitRate ( i,256);
BOOL ret = VCAStartVideoCapture ( i ,
CAP_ORIGIN_MPEG4_STREAM,MPEG4 _AVIFILE_CALLBACK ,buffer);
}
}
}
else
{
m_bStartCap = ! m_bStartCap ;
m_Capture.SetWindowText (" 网传 ");
for (int i = 0; i < m_DevNum ; i ++)
{
if (m_Frame.m_pList [i ].m_ShowImage )
{
VCAStopVideoCapture ( i );
}
}
}
}
( 20 )处理“快照”按钮的单击事件,截取当前预览图像,并存成文件。
void CCaptureDlg::OnSnapshot ()
{
if (CPreView::m_CurIndex != -1)
if (m_Frame.m_pList [CPreView::m_CurIndex ].m_ShowImage )
{
CButton * pBmpButton = (CButton *) GetDlgItem ( IDC_BMP);
if (pBmpButton ->GetCheck () != 0)
{
CFileDialog fDlg ( FALSE,"bmp","one",OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT," 位图 |*.bmp",this );
if (fDlg.DoModal ()==IDOK)
{
CString fName = fDlg.GetPathName ( );
VCASaveAsBmpFile ( CPreView::m_CurIndex,fName );
}
}
else
{
CFileDialog fDlg ( FALSE,"jpg","one",OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT,"JPG 文件 |*.JPG",this );
if (fDlg.DoModal ()==IDOK)
{
CString fName = fDlg.GetPathName ( );
VCASaveAsJpegFile ( CPreView::m_CurIndex,fName );
}
}
}
else
{
MessageBox (" 当前选择的图像不能预览 !"," 提示 ");
}
}
( 21 )处理“显卡类型”按钮的单击事件,设置显卡类型,将其存储在 INI 文件中。
void CCaptureDlg::OnSetting ()
{
CButton * pVGAButton = (CButton *) GetDlgItem ( IDC_VGA);
int sel = pVGAButton ->GetCheck ();
if (sel )
{
WritePrivateProfileString (" 显卡设置 "," 类型 ","VGA","./syssetting.ini");
}
else
{
WritePrivateProfileString (" 显卡设置 "," 类型 ","PCI","./syssetting.ini");
}
MessageBox (" 要使显卡设置生效 , 需要重新启动应用程序 !"," 提示 ");
}
( 22 )处理对话框的 WM_WINDOWPOSCHANGED 消息,在对话框位置改变时,更新视频预览窗口。
void CCaptureDlg::OnWindowPosChanged (WINDOWPOS FAR* lpwndpos )
{
CDialog::OnWindowPosChanged ( lpwndpos );
for (UINT i = 0 ; i < m_DevNum ; i ++)
{
if (m_Frame.m_pList [i ].m_ShowImage )
{
if (m_Frame.m_pList [i ].m_Stop ==psStop )
{
VCAStopVideoPreview ( i );
VCAUpdateOverlayWnd ( m_Frame.GetSafeHwnd ());
}
else if (m_Frame.m_pList [i ].m_Stop ==psPreview )
{
m_Frame.m_pList [i ].Invalidate( );
VCAUpdateOverlayWnd ( m_Frame.m_hWnd );
VCAUpdateVideoPreview ( i,m_Frame.m_pList [i ].m_hWnd );
}
}
}
}
( 1 )创建一个基于对话框的工程,向对话框中添加按钮控件。
( 2 )将 Sa7134Capture.dll 、 MPG4c32.dll 、 MediaTransmit.dll 、 MediaTransmit.lib 、 MediaTransmit.lib 、 Sa7134Capture.h 和 MediaTransmit.h 文件添加到当前工程中。
( 3 )创建一个对话框类 CServerInfo ,用于设置连接的服务器信息。对话框资源如图 4 所示。
图 4 连接服务器窗口
( 4 )在主对话框中定义如下成员变量:
int m_CardID ; // 卡号
int m_CurIndex ;
int m_BackID ;
CString m_ServerIP ; // 服务器 IP
int m_Port ; // 服务器端口
int m_Chanel ; // 视频通道
BOOL m_Captured ; // 是否开始捕捉
BOOL m_BackPlay ; // 是否进行视频回放
( 5 )在主对话框初始化时创建视频窗口。
CRect rc ;
m_Panel.GetClientRect ( rc );
MTACreateVideoDevice ( m_Panel.GetSafeHwnd (),m_hWnd,rc,320,240,2,FALSE);
MTASetSplitMode ( 1);
( 6 )处理“连接服务器”按钮的单击事件,开始连接服务器。
void CClientDlg::OnLinkServer ()
{
CServerInfo ServerDlg ;
if (ServerDlg.DoModal ()==IDOK)
{
m_ServerIP = ServerDlg.m_ServerIP ;
m_Port = ServerDlg.m_Port ;
m_Chanel = ServerDlg.m_Chanel-1;
MTASetMpeg4Version( MPEG4_V1);
if (!MTALoadLibrary (1008,WORK_AS_CLIENT))
{
MessageBox (" 加载网络库失败 !");
MTAFreeLibrary ( );
return ;
}
MTAClearCall ( m_CardID,FALSE );
CRect rc ;
m_Panel.GetClientRect ( rc );
MTACreateVideoDevice ( m_Panel.GetSafeHwnd (),m_hWnd,rc,240,320,2,FALSE);
MTASetSplitMode ( 1);
m_CurIndex = 0;
m_CardID = MTANewCall ( m_ServerIP.GetBuffer (0),m_Port,m_Chanel );
if (m_CardID ==-1)
{
MessageBox (" 连接服务器失败 !");
}
MTASetVideoOut ( m_CardID,0);
unsigned char data[] = "Admin";
MTAMakeCall ( m_CardID,REQ_VI_STREAM,FALSE,data,10,NULL,CmdRespond);
}
}
( 7 )处理“录像”按钮的单击事件,开始或停止录像。
void CClientDlg::OnCapture ()
{
if (!m_Captured )
{
CFileDialog fDlg ( FALSE,"avi","one",OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT,"AVI|*.avi",this );
if (fDlg.DoModal ()==IDOK)
{
m_Captured = TRUE;
CString file = fDlg.GetPathName ( );
MTAStartCapture ( m_CardID,file.GetBuffer (0),SAVE_VIDEO_DATA,NULL);
m_Capture.SetWindowText (" 停止录像 ");
}
}
else
{
m_Captured = FALSE;
m_Capture.SetWindowText (" 录像 ");
MTAStopCapture ( m_CardID );
}
}
( 8 )处理“视频回放”按钮的单击事件,回放录制的视频文件。
void CClientDlg::OnBackplay ()
{
if (!m_BackPlay )
{
CFileDialog fDlg ( TRUE,"","",OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT,"AVI|*.avi",this );
if (fDlg.DoModal ()==IDOK)
{
m_BackPlay = TRUE;
m_Play.SetWindowText (" 停止回放 ");
CString file = fDlg.GetPathName ( );
// 分配系统资源
m_BackID = MTANewPlayBack ( file);
//MTASetVideoOut ( m_BackID ,0);
// 打开回放文件
BOOL ret = MTAOpenFile ( m_BackID,file );
Sleep( 1000);
MTASetPlayPosition ( m_BackID,1);
// 开始播放
MTAStartPlay ( m_BackID,PLAY_VIDEO_DATA|PLAY_AUDIO_DATA );
MTASetVideoOut ( m_BackID ,0);
}
}
else
{
m_BackPlay = FALSE;
m_Play.SetWindowText (" 视频回放 ");
// 关闭打开的文件
MTACloseFile ( m_BackID );
// 释放回放资源
MTAFreePlayBack ( m_BackID );
}
}