2.4 SDK视频采集方案
在开发视频监控系统时,通常需要使用一些专用的监控卡。为了让用户能够进行二次开发,某些监控卡提供了SDK开发包,使用开发包提供的函数,用户可以根据需要自行设计监控软件,而不必依赖监控卡厂商提供的监控软件。本节将介绍有关监控卡SDK开发包相关知识。
2.4.1 监控卡选购分析
在开发视频监控系统时,需要选择一款适宜的监控卡。为了方便用户选购,下面列出选购监控卡时需要注意的几个方面:
l 具有SDK开发包
在购买监控卡时,一定要选购具有SDK开发包的监控卡。这样,开发人员可以方便地进行二次开发。否则,只有支持WDM驱动的监控卡才可以进行二次开发(使用Direct Show)。
l 监控卡的性能
购买的监控卡需要满足用户的需求。例如,监控卡的分辨率、高级的监控卡分辨率可以达到720*516,捕捉的画面接近DVD的质量。监控卡是否支持硬件压缩,支持硬件压缩的监控卡,用户不用编写算法进行软件压缩,这样,数据压缩的过程不经过CPU,能够提供系统的捕捉效率。此外,还需要考虑监控卡能够实现多少路视频捕捉、一台计算机可以同时安装几个监控卡等。
l 环境需求
对于不同厂家、不同类型的监控卡,其硬件要求通常是不同的。在监控卡的使用手册中,会有监控卡详细的环境需求描述,
多数监控卡对于计算机的硬件配置要求比较“苛刻”。尤其是对显卡、CPU、内存的要求。以天敏的VC4000为例,显卡要求支持DirectDraw和Overlay技术,显卡内存建议32MB以上。由于VC4000采用的是软压缩技术,因此对CPU的要求比较高。对于8路的视频需求,要求CPU为赛扬2.4G以上,对于16路的视频需求,要求CPU为赛扬2.8G以上,而对于24路(VC4000支持的视频需求上限)视频需求,要求CPU为P42.8G以上。至于内存的要求,也是随着视频需求的提高而提高,在8路和16路环境下,内存应在256M以上,在24路环境下,内存要求在512MB以上。
l 价格因素
高级的监控卡价格比较昂贵,性能比较突出。在购买监控卡时,需要从自身或用户的角度考虑,即要满足需求又要节约成本。
2.4.2 监控卡安装
在购买监控卡后,厂家会随同提供监控卡的驱动程序及产品说明书。用户首先需要仔细阅读产品说明书,将监控卡安装到主板上。监控卡多数都采用PCI插槽,例如,图2.33、图2.34分别显示的是天敏的VC4000监控卡和德加拉的监控卡。

图2.33 VC4000监控卡

图2.34 德加拉监控卡
下面以天敏的VC4000为例,介绍监控卡的安装过程。
(1)关闭计算机电源,打开机箱,将监控卡安装在一个空的PCI插槽上,如图2.35所示。

图2.35 安装监控卡
(2)从监控卡包装盒中取出螺丝,将监控卡固定在机箱上,如图2.36所示。
|

图2.36 固定监控卡
(3)将摄像头的信号线连接到监控卡上,如图2.37所示。

图2.37 连接信号线
至此,完成了监控卡的硬件安装,此外,还需要进行软件安装。安装监控卡使用的驱动程序、MPEG编码器、解码器等。具体步骤如下:
(1)安装DirectX 9.0或以上版本。许多监控卡都要求安装DirectX才能够使用监控卡。
(2)安装并注册MPEG编码器、解码器。
(3)将监控卡的安装盘放入光驱,会弹出如图2.38所示。

图2.38 监控卡驱动列表
(4)选择VC4000监控卡驱动,如图2.39所示。

图2.39 VC4000驱动程序
(5)依次选择“安装驱动程序”、“安装SDK开发包”、“安装应用程序 客户端 服务器端”。
(6)重新启动计算机,完成软件的安装。
2.4.3 系统部署方案
根据视频监控系统的作用范围不同,系统部署方式可以分为局域网部署和广域网部署。在局域网部署方式中,视频服务器与客户机处于同一个网络内,视频服务器和客户机的IP,可以根据需要自行设置,客户机可以通过视频服务器的IP和端口号来连接服务器。局域网部署方式结构图如图2.40所示。

图2.40 局域网部署方式结构图
在广域网部署方式下,客户机与视频服务器之间的通信是通过Internet实现的,因此视频服务器可能存在两种类型的IP地址,即静态IP和动态IP。如果视频服务器采用静态IP,则客户端可以通过IP地址和端口号来访问视频服务器。视频服务器静态IP结构图如图2.41所示。
图2.41 视频服务器静态IP结构图
如果视频服务器采用动态IP,即IP地址由ADSL拨号而获得,那么对于远程访问的客户机来说,是不可能随时知道视频服务器的IP地址的。此时,可以通过域名解析系统来实现。首先申请一个域名,可以是付费的或免费的。然后客户端通过域名和端口号来访问视频服务器。视频服务器动态IP结构图如图2.42所示。

图2.42 视频服务器动态IP结构图
这里有一点需要说明,通常ADSL拨号是由路由器完成的,而不是视频服务器,因此视频服务器没有直接处于公网上,需要通过路由器建立端口映射来实现数据的传输。
2.4.4 开发包分析
在购买具有SDK开发包的监控卡时,会附带有监控卡的使用手册,其中包含有SDK开发包的详细说明。下面以天敏VC4000监控卡为例,介绍其主要函数及开发流程。
天敏VC4000监控卡的SDK主要由Sa7134Capture.dll、MediaTransmit.dll、MPG4c32.dll、Sa7134Capture.lib、MediaTransmit.lib、Sa7134Capture.h和MediaTransmit.h等几个文件组成。在使用开发包提供的函数前,需要将这些文件添加到当前工程中。天敏VC4000监控卡的SDK包含了近百个函数,下面介绍开发视频监控经常使用的函数。
1.VCAInitSdk
该函数用于初始化开发包。在使用SDK开发包中的函数前,首先需要调用该函数进行初始化。VCAInitSdk函数语法如下:
BOOL WINAPI VCAInitSdk( HWND hWndMain, DISPLAYTRANSTYPE eDispTransType =
PCI_VIEDOMEMORY, BOOL bInitAudDev = FALSE );
参数说明:
hWndMain:表示视频显示多路小窗口的父窗口。
eDispTransType:表示显示类型。
bInitAudDev:表示是否初始化音频设备。
2.VCAUnInitSdk
该函数用于释放调用VCAInitSdk函数分配的系统资源,通常在程序结束时调用该函数。VCAUnInitSdk函数原型如下:
void WINAPI VCAUnInitSdk()
3.VCAGetDevNum
该函数用于获得监控卡中芯片的数量。通常,监控卡支持多少路视频,将会存在多少个芯片。VCAGetDevNum函数语法如下:
LONG WINAPI VCAGetDevNum()
返回值:表示系统中安装监控卡上的芯片数量。
4.VCARegVidCapCallBack
该函数用于注册视频捕捉的回调函数。如果用户在进行视频捕捉时执行某些动作,可以调用该函数注册一个回调函数。VCARegVidCapCallBack函数语法如下:
BOOL WINAPI VCARegVidCapCallBack( DWORD dwCard, PrcVidCapCallBack ppCall )
参数说明:
dwCard:表示视频捕捉的卡号。
ppCall:表示回调函数指针,其定义如下:
typedef void (CALLBACK *PrcVidCapCallBack)( DWORD dwCard, BYTE *pbuff, DWORD dwSize )
其中,dwCard表示视频捕捉的卡号,pbuff表示视频数据,dwSize表示视频数据的大小。
5.VCARegVidMpegCallBack
该函数用于注册视频MPEG数据压缩的回调函数。VCARegVidMpegCallBack函数语法如下:
BOOL WINAPI VCARegVidMpegCallBack( DWORD dwCard, PrcVidMpegCallBack ppCall )
参数说明:
dwCard:表示视频捕捉的卡号。
ppCall:表示回调函数指针,其定义如下:
typedef void (CALLBACK *PrcVidMpegCallBack)
( DWORD dwCard, BYTE *pbuff, DWORD dwSize, BOOL isKeyFrm )
其中, dwCard表示视频捕捉的卡号,pbuff表示视频MPEG数据,dwSize表示视频数据的大小。isKeyFrm表示是否使用关键帧。
6.VCAOpenDevice
该函数用于打开视频预览窗口。语法如下:
BOOL WINAPI VCAOpenDevice( DWORD dwCard,HWND hPreviewWnd )
参数说明:
dwCard:表示视频捕捉的卡号。
hPreviewWnd:表示视频预览窗口句柄。
7.VCAStartVideoPreview
该函数用于打开视频预览窗口。语法如下:
BOOL WINAPI VCAStartVideoPreview( DWORD dwCard )
参数说明:
dwCard:表示预览的视频卡号。
8.VCAStopVideoPreview
该函数用于停止视频预览。语法如下:
BOOL WINAPI VCAStopVideoPreview( DWORD dwCard )
参数说明:
dwCard:表示停止预览的视频卡号。
9.VCAUpdateOverlayWnd
该函数用于更新视频预览窗口。当预览窗口的父窗口大小或位置改变时,需要调用该函数进行调整。VCAUpdateOverlayWnd函数语法如下:
BOOL WINAPI VCAUpdateOverlayWnd(HWND hOverlayWnd)
参数说明:
hOverlayWnd:表示预览窗口的父窗口
10.VCAUpdateVideoPreview
该函数用于更新视频预览窗口。当预览窗口的大小和位置需要调整时调用该函数。通常,在调用该函数前,需要调用VCAUpdateOverlayWnd函数。VCAUpdateVideoPreview语法如下:
BOOL WINAPI VCAUpdateVideoPreview( DWORD dwCard, HWND hPreviewWnd )
参数说明:
dwCard:表示视频卡号。
hPreviewWnd:表示视频预览窗口。
11.VCASetVidCapSize
该函数用于设置视频捕捉的大小。语法如下:
BOOL WINAPI VCASetVidCapSize( DWORD dwCard, DWORD dwWidth, DWORD dwHeight )
参数说明:
dwCard:表示视频卡号。
dwWidth:视频捕捉图像的宽度,建议为16的整数倍。
dwHeight:视频捕捉图像的高度,建议为16的整数倍。
12.VCASetVidCapFrameRate
该函数用于设置视频捕捉帧率。语法如下:
BOOL WINAPI VCASetVidCapFrameRate( DWORD dwCard, DWORD dwFrameRate,
BOOL bFrameRateReduction = FALSE )
参数说明:
dwCard:表示视频卡号。
dwFrameRate:表示设置的捕捉帧率,PCL信号最大帧率为25,NTSC最大帧率为30。
bFrameRateReduction:该参数是保留的,未被使用。
13.VCASetBitRate
该函数用于设置MPEG压缩的位率。语法如下:
BOOL WINAPI VCASetBitRate( DWORD dwCard, DWORD dwBitRate)
参数说明:
dwCard:表示视频卡号。
dwBitRate:表示MPEG的压缩位率。范围在56KBPS~10MBPS之间。
14.VCAStartVideoCapture
该函数用于开始视频捕捉。语法如下:
BOOL WINAPI VCAStartVideoCapture( DWORD dwCard,
APMODEL enCapMode, MP4MODEL enMp4Mode, LPCTSTR lpFileName );
参数说明:
dwCard:表示视频卡号。
enCapMode:表示视频捕捉模式。
enMp4Mode:表示MPEG压缩模式,只有在enCapMode参数为CAP_MPEG4_STREAM时,该参数才起作用。
lpFileName:表示视频捕捉的文件名称。
15.VCAStopVideoCapture
该函数用于停止视频捕捉。语法如下:
BOOL WINAPI VCAStopVideoCapture( DWORD dwCard )
参数说明:
dwCard:表示视频卡号。
16.MTALoadLibrary
该函数用于初始化网络开发包。语法如下:
MEDIATRANSMIT_API BOOL MTALoadLibrary(WORD usLocalPort, WORD usRole);
参数说明:
usLocalPort:表示本地端口号。
usRole:表示角色,为WORK_AS_SERVER,表示初始服务器端。为WORK_AS_CLIENT,表示初始化客户端。为WORK_AS_SUPPLY,表示初始化代理端。
17.MTAWriteVideo
该函数用于向客户端传递视频数据。语法如下:
MEDIATRANSMIT_API BOOL MTAWriteVideo(BYTE nCardNo, BYTE *pData,
LONG lSize, BOOL bIFrm)
参数说明:
nCardNo:表示欲传递数据的视频卡号。
pData:表示传递的视频数据。
lSize:表示视频数据的大小。
bIFrm:表示是否为I帧。
18.MTACreateVideoDevice
该函数用于创建视频显示窗口。语法如下:
MEDIATRANSMIT_API int MTACreateVideoDevice(HWND hParentWnd, HWND hNotifyWnd,
RECT rect, int nWidth, int nHeight, int nSpace, BOOL bUseOverlay);
参数说明:
hParentWnd:表示视频显示窗口的父窗口句柄。
hNotifyWnd:表示视频显示通知消息接收的窗口句柄。
19.MTASetSplitMode
该函数用于设置视频显示窗口的切分模式。语法如下:
MEDIATRANSMIT_API int MTASetSplitMode(int nMode);
参数说明:
nMode:表示视频窗口的显示模式,取值范围1到4。
20.MTANewCall
该函数用于申请呼叫资源。在访问远程视频服务器时,需要调用该函数进行连接。MTANewCall函数语法如下:
MEDIATRANSMIT_API int MTANewCall(char *pRemoteIp, WORD usRemotePort,
BYTE biCardNo);
参数说明:
pRemoteIp:表示远程视频服务器的IP地址。
usRemotePort:表示远程视频服务器的端口号。
biCardNo:表示视频服务器的视频卡号。
返回值:表示本地的呼叫号。
21.MTASetVideoOut
该函数用于将呼叫的视频信号显示在视频显示窗口的指定位置。语法如下:
MEDIATRANSMIT_API BOOL MTASetVideoOut(int nCallID,int nIndex);
参数说明:
nCallID:表示呼叫号,通常为MTANewCall函数的返回值。
nIndex:表示显示位置相对的索引号,索引从0开始,最大值为15。
22.MTAMakeCall
该函数用于请求媒体传输服务。语法如下:
MEDIATRANSMIT_API BOOL MTAMakeCall(int nCallID, BYTE biReqType, BOOL bWANCall,
BYTE* lpSndData, BYTE biSndSize, HANDLE hEventNotify, vCmdRespond evCallNotify)
参数说明:
nCallID:表示呼叫号,通常为MTANewCall函数的返回值。
biReqType:表示请求类型,为REQ_AV_STREAM,表示请求音频、视频传输,为REQ_VI_STREAM,表示请求视频传输,为REQ_AU_STREAM,表示请求音频传输。
23.MTANewPlayBack
该函数用于视频回放时分配系统资源。语法如下:
MEDIATRANSMIT_API int MTANewPlayBack(LPCTSTR lpFileName)
参数说明:
lpFileName:表示初始化回放文件名,可以为NULL。
返回值:如果函数执行成功,返回值为回放ID,如果函数执行失败,返回值为-1。
24.MTAOpenFile
该函数用于打开回放文件。语法如下:
MEDIATRANSMIT_API BOOL MTAOpenFile(int nPlayBackID,LPCTSTR lpFileName)
参数说明:
nPlayBackID:表示视频回放资源ID。
lpFileName:表示回放文件名。
25.MTAStartPlay
该函数表示在当前位置开始播放文件。语法如下:
MEDIATRANSMIT_API BOOL MTAStartPlay(int nPlayBackID,DWORD dwMode)
参数说明:
nPlayBackID:表示回放资源ID。
dwMode:表示回放模式,为PLAY_VIDEO_DATA,表示回放视频数据。为PLAY_AUDIO_DATA,表示回放音频数据。
26.MTACloseFile
该函数用于关闭回放文件。语法如下:
MEDIATRANSMIT_API void MTACloseFile(int nPlayBackID)
参数说明:
nPlayBackID:表示回放资源ID。
27.MTAFreePlayBack
该函数用于释放回放系统资源。语法如下:
MEDIATRANSMIT_API void MTAFreePlayBack(int nPlayBackId)
参数说明:
nPlayBackId:表示欲释放的回放资源ID。
28.MTASetWndPos
该函数用于设置视频显示窗口的位置。语法如下:
MEDIATRANSMIT_API BOOL MTASetWndPos(RECT rc);
参数说明:
rc:表示视频窗口新的位置。
29.MTAPageDown
该函数用于向下翻页,直到当前显示模式下的最后一页。语法如下:
MEDIATRANSMIT_API int MTAPageDown();
返回值:如果函数执行成功,返回值为前一次的页号,否则返回小于1的错误码。
30.MTAPageUp
该函数用于向上翻页,直到当前显示模式下的第一页。语法如下:
MEDIATRANSMIT_API int MTAPageUp();
返回值:如果函数执行成功,返回值为前一次的页号,否则返回小于1的错误码。
2.4.5 视频开发设计方案
在开发视频监控系统时,首先需要了解开发包的操作流程,才能够进行视频开发。以天敏VC4000为例,其基本开发思路如下:
视频服务器端首先调用VCAInitSdk函数初始化开发包,然后调用VCAGetDevNum函数 获得系统中可以显示多少路视频,通过该数据值创建相应的视频显示窗口,接着调用VCARegVidCapCallBack和VCARegVidMpegCallBack函数注册视频捕捉及压缩回调函数,然后调用VCAOpenDevice函数打开设备,调用VCAStartVideoPreview函数开始预览,调用MTALoadLibrary函数初始化网络包。最后在系统退出时调用VCAUnInitSdk函数释放开发包资源。
视频服务器效果如图2.43所示。

图2.43 视频服务器效果图
客户端程序首先调用MTACreateVideoDevice函数创建视频显示窗口,调用MTASetSplitMode函数设置窗口切分模式。然后调用MTALoadLibrary函数初始化网络库,调用MTANewCall函数申请呼叫资源,接着调用MTAMakeCall函数请求媒体传输服务,调用MTASetVideoOut函数设置视频显示窗口,最后调用MTAClearCall函数释放网络包资源。
客户端程序效果如图2.44所示。

图2.44 视频捕捉客户端
下面以一个具体实例介绍视频监控程序的设计方法。服务器端程序设计步骤如下:
实例位置:光盘\mr\2\2.4\2.4.5\01
(1)创建一个基于对话框的应用程序,在对话框中添加按钮控件,如图2.45所示。

图2.45 对话框设计
(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);
}
}
}
}
客户端程序设计步骤如下:
实例位置:光盘\mr\2\2.4\2.4.5\02
(1)创建一个基于对话框的工程,向对话框中添加按钮控件。
(2)将Sa7134Capture.dll、MPG4c32.dll、MediaTransmit.dll、MediaTransmit.lib、MediaTransmit.lib、Sa7134Capture.h和MediaTransmit.h文件添加到当前工程中。
(3)创建一个对话框类CServerInfo,用于设置连接的服务器信息。对话框资源如图2.46所示。

图2.46 连接服务器窗口
(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);
}
}






