摘 要:針對在研的視頻數(shù)字化設(shè)備開發(fā)了一種視頻流量監(jiān)測系統(tǒng),。該系統(tǒng)采用微軟MFC的API技術(shù)設(shè)計,,通過通信接口接收流量信息,并經(jīng)信號處理后實時顯示視頻設(shè)備的流量信息,。應(yīng)用結(jié)果表明,,該監(jiān)測系統(tǒng)運(yùn)行穩(wěn)定可靠,監(jiān)測精度高,,具有一定的實用性和推廣價值,。
關(guān)鍵詞: 視頻設(shè)備; 視頻流量,; MFC,; 監(jiān)測
Visual C++是美國微軟公司推出的目前使用極為廣泛的可視化編程環(huán)境,它具有以下特點:(1)Developer Studio 由一套集成工具構(gòu)成,,用于開發(fā)Win32環(huán)境下運(yùn)行的應(yīng)用程序,;(2)提供了具有功能強(qiáng)大的向?qū)Чぞ?如MFC AppWizard、ClassWizard,、MFC ActiveX Control Wizard, ISAPI Extension Wizard,、 ATL COM AppWizard和Custom AppWizard) 簡化Win32應(yīng)用程序開發(fā);(3)MFC類庫支持多線程應(yīng)用程序開發(fā),; (4)具有Windows Socket和MAPI支持,,可與網(wǎng)絡(luò)和E-mail鏈接;(5)MFC類庫封裝Win32 Internet,、ActiveX,、ISAPI等功能使編程更容易。
隨著Visual C++的廣泛應(yīng)用,,人們歸納出使用Visual C++編寫Windows應(yīng)用程序的三種方法:(1)直接調(diào)用Win32 API應(yīng)用程序編程接口函數(shù),,大量程序代碼須由用戶自己編寫;(2)使用MFC類庫和ATL(活動模板庫)直接編寫應(yīng)用程序,,相對方便容易,;(3)既使用MFC 類庫和ATL,也使用向?qū)?Wizards)編寫。第三種方法是最方便簡單的,。它首先用MFC AppWizard,、MFC ActiveX ControlWizard、ISAPI Extension Wizard和ATL COM AppWizard生成基本框架結(jié)構(gòu),用ClassWizard建立應(yīng)用程序類,、消息處理和數(shù)據(jù)處理或定義控件屬性,、事件、方法,,將要求的功能源碼添加到相應(yīng)類中,。本文將研究利用Visual C++的MFC類等API庫實現(xiàn)視頻數(shù)字化設(shè)備流量監(jiān)測軟件的設(shè)計[1]。
1 視頻流量監(jiān)測軟件設(shè)計
為了驗證設(shè)計的視頻數(shù)字化設(shè)備性能,,需對視頻流量進(jìn)行監(jiān)測,,筆者采用Visual C++集成開發(fā)環(huán)境對其監(jiān)測軟件進(jìn)行開發(fā)。該視頻數(shù)字化設(shè)備監(jiān)測軟件要求能夠同時測試和顯示8路視頻流量信息和狀態(tài),。為實現(xiàn)8路視頻流量的接收,,則需要在網(wǎng)絡(luò)通信組件中添加組播方式。通過設(shè)置定時功能對某一段時間輸出的流量進(jìn)行累加統(tǒng)計并定時顯示從而驗證視頻數(shù)字化設(shè)備能否達(dá)到性能指標(biāo)要求,。
1.1 面向?qū)ο蠹夹g(shù)和方法的應(yīng)用
首先定義一個項目的工程名,,然后在定義的工程中根據(jù)Visual C++的功能向?qū)Чぞ呱晒こ桃曨悺⒐こ涛臋n類,、工程主框類及其成員函數(shù),。在這些對象的具體實現(xiàn)中,大部分都直接使用了底層的Windows API函數(shù),,與其他使用第三方控件和接口實現(xiàn)的方式相比,,使用更靈活、功能更強(qiáng)大,、運(yùn)行效率更高[2],。在這些類中分別構(gòu)造定義網(wǎng)絡(luò)通信函數(shù)、組播方式處理程序,、數(shù)據(jù)處理函數(shù),、缺省文件讀取函數(shù)、查詢判別函數(shù),、視類定時顯示函數(shù)等關(guān)鍵函數(shù),。通過這些函數(shù)實現(xiàn)網(wǎng)絡(luò)通信和數(shù)據(jù)處理以及定時流量和狀態(tài)顯示等功能。圖1是視頻監(jiān)測系統(tǒng)的幾個封裝類示意圖,。
1.2 視頻監(jiān)測軟件架構(gòu)方法
本系統(tǒng)運(yùn)用MFC類庫豐富的Windows API性能,,首先從CAsyncSocket類中派生構(gòu)造CNewSocket網(wǎng)絡(luò)通信子類,在CNewSocket子類的一個成員函數(shù)OnReceive(int nErrorCode)里調(diào)用MFC類庫中的ReceiveFrom( )函數(shù)對視頻設(shè)備流量進(jìn)行接收,。利用CObject類派生的CGrouptestObject表達(dá)和封裝測試對象的數(shù)據(jù)內(nèi)容,,用派生的CGrouptestMain類表達(dá)標(biāo)題內(nèi)容,利用由CDocument派生的CGrouptestDoc表達(dá)和封裝數(shù)據(jù)處理內(nèi)容,,用CView派生的GrouptestView表達(dá)和封裝測試對象的多種外觀和視覺側(cè)面,,從而實現(xiàn)數(shù)據(jù)的管理與表達(dá)分開,,使程序結(jié)構(gòu)更清晰,開發(fā)效率更高,。
在Visual C++ 的Main/Document/View結(jié)構(gòu)中有:SDI(單文檔界面)和MDI(多文檔界面)方式,,VC++的Application Framework會根據(jù)用戶選擇自動產(chǎn)生代碼框架,使用非常方便[3],。其中SDI只支持一種文檔結(jié)構(gòu),,一個SDI程序一次只能處理一個文檔對象;MDI接口可支持多種文檔,,并可同時處理多個文檔對象,。本文采用了MDI結(jié)構(gòu),以便對監(jiān)測對象進(jìn)行擴(kuò)展,。具體對象間的通信和連接關(guān)系如圖2所示,。
1.3 網(wǎng)絡(luò)通信的建立
在CNewSocket類中網(wǎng)絡(luò)通信函數(shù)NewSocket.cpp定義了網(wǎng)絡(luò)通信接收的方式,。在CNewSocket類中的接收函數(shù)OnReceive( )內(nèi)容為:
void CNewSocket::OnReceive(int nErrorCode)
{ char szRecv[MAXSIZE],;while(TRUE) {
NumRead=ReceiveFrom(szRecv,MAXSIZE,
SenderAddr,SenderPort);
if(NumRead==SOCKET_ERROR) break,; }
((CGrouptestDoc*)pParent)->ReadDataProc(),;
CAsyncSocket::OnReceive(nErrorCode) ; }
其中ReceiveFrom( )接收的網(wǎng)絡(luò)流量(字節(jié)),,再轉(zhuǎn)移給數(shù)據(jù)處理函數(shù)ReadDataProc()處理,。SenderAddr是發(fā)送地址,SenderPort是發(fā)送端口,,NumRead是接收的字節(jié)數(shù)大小,,ReadDataProc()則是位于文檔類中的數(shù)據(jù)處理函數(shù),它用于處理和顯示接收的數(shù)據(jù),。
為接收多個視頻設(shè)備流量,,需加入組播方式。在組播方式加入后需要獲取主機(jī)地址,。其中GetHostIPAddress()是取主機(jī)地址函數(shù),。
LPSTR GetHostIPAddress(void)
{if(gethostname(MyName,80)==SOCKET_ERROR)
return NULL; //獲得主機(jī)名
if(!(thisHost=gethostbyname(MyName)))
return NULL,;memset((void*)&in,sizeof(in),0) ,;
in.s_addr=*((unsigned long*)thisHost->h_addr_list[0]);
if(!(ptr=inet_ntoa(in)))
return NULL,;
IP =new char[strlen(ptr)-1] ,;
strcpy(IP,ptr);
return IP,;} //得到主機(jī)IP地址
gethostname()函數(shù)是取本地主機(jī)的名字,,gethostbyname()函數(shù)則是獲取本地主機(jī)名字對應(yīng)的結(jié)構(gòu)指針。
在CGrouptestDoc文檔類的成員函數(shù)OnNewDocument()中加入組播地址:
BOOL CGrouptestDoc::OnNewDocument(){
mipadd=GetHostIPAddress(); //取主機(jī)地址
if(!mipadd) { AfxMessageBox("取主機(jī)地址失敗"),;
return FALSE,; }
if(!m_pNamePort->Create(LOCALPORT,SOCK_DGRAM,
FD_READ,mipadd)) {
AfxMessageBox("建立套接口失敗");return -1,;}
for(int i=0,;i<8;i++) {
mreq.imr_multiaddr.s_addr=
inet_addr(mon[i].ipdest) ,;
mreq.imr_interface.s_addr=INADDR_ANY;
if(m_pNamePort->SetSockOpt(
IP_ADD_MEMBERSHIP,(char*)&mreq, sizeof(mreq),
IPPROTO_IP)<0)
{ return -1,;}} //加入組播地址
return TRUE;}
該成員函數(shù)中由函數(shù)GetHostIPAddress( )獲得主機(jī)IP地址,,再由函數(shù)Create( )建立套接口,,設(shè)定多目的地址和端口地址后通過函數(shù)SetSockOpt( )加入組播地址。從而獲得8組視頻流量顯示,。
1.4 數(shù)據(jù)處理
數(shù)據(jù)處理函數(shù)是對接收的8個視頻網(wǎng)絡(luò)設(shè)備端口視頻流量進(jìn)行分組統(tǒng)計,。在CGrouptestDoc文檔類中建立數(shù)據(jù)處理成員函數(shù):
void CGrouptestDoc::ReadDataProc()
{ if(NumRead>0) {
i= iLookup(SenderPort,SenderAddr);
if (i !=0xff) { //匹配則執(zhí)行處理
switch(SenderPort) {
case LOCALPORT2: //源端口1
if(SenderAddr==ipaddrbuf[0])
{totle1=totle1+NumRead,; //流量計數(shù)累加
totle=totle1,; } ……;break,;
case LOCALPORT1:……,; break;}
mon[i].totle=totle,;mon[i].ipstr=SenderAddr,;
mon[i].flag=1;} } } //置統(tǒng)計數(shù)據(jù)標(biāo)志
該項目文檔的成員函數(shù)ReadDataProc()先通過iLookup()函數(shù)確定視頻流量在界面上源地址和目標(biāo)地址所應(yīng)該對應(yīng)的位置,。然后累加統(tǒng)計每路流量并在規(guī)定的時間動態(tài)地顯示所接入的各路視頻設(shè)備的流量,。最多可以顯示8路視頻流量。
視頻流量監(jiān)測開始時首先要賦給源網(wǎng)絡(luò)地址和目標(biāo)網(wǎng)絡(luò)地址,。通過初始化時讀取配置文件函數(shù)ReadIniFile()設(shè)定網(wǎng)絡(luò)源地址和網(wǎng)絡(luò)目標(biāo)地址,。此函數(shù)先讀取配置文件,再通過Ini_IP_TcConvert()函數(shù)變換為判別的4組 ipaddrbuf字符對應(yīng)的目的網(wǎng)絡(luò)地址,。iLookup()是查詢判別分組函數(shù),,它通過設(shè)定不同源端口確定幾個分組次序,在此不予分析,。
1.5 視頻流量顯示
視頻流量顯示是在項目視類函數(shù)中實現(xiàn)的,。在視類初始化更新函數(shù)OnInitialUpdate()中啟動定時器[4]。SetTimer()是定時器控制函數(shù),,能使視頻流量在其界面上每秒定時顯示,。
void CGrouptestView::OnInitialUpdate(){
SetTimer(ID_CONT_TOL,1000,NULL) ,;}
通過位于CGrouptestView視類中的定時器函數(shù)顯示每秒更新的視頻流量值:
Void CGrouptestView::OnTimer(UINT nIDEvent) {
t=CTime::GetCurrentTime(); //獲得當(dāng)前時間
timestr=t.Format("%c"),; //窗口顯示
m_source.SetWindowText(timestr) ;
TRACE(timestr+"\n");SetEvent(hzero); pDoc=Get-
Document(),;
if(nIDEvent==1) { pDoc->ReadDataProc();
for (i=0,;i<8,;i++){ if (mon[i].flag) {
//判標(biāo)志位,如果零則顯示置空
mon[i].flag=0,; totletmp[i]=(float)
(8*mon[i].totle/(1.0*1000*1000)) ,;
tolstr[i].Format("%2.3f Mbps",totletmp[i]) ;
switch(i) { case 0:
//網(wǎng)絡(luò)地址和流量顯示
m_tol0.SetWindowText(tolstr[i]),;
m_dest0.SetWindowText(mon[i].ipdest),;
m_ip0.SetWindowText(mon[i].ipstr);
if(totletmp[i]>0.1) //流量極下限
m_status0.SetIcon(h_green) ,; //正常
else {m_status0.SetIcon(h_red),;
//故障m_tol0.SetWindowText
("0.000 Mbps");}
break,;……} }else { mon[i].totle=0,;
switch(i){ case 0:
//顯示清空流量值m_tol0.SetWindow-
Text("0.000 Mbps"),;
m_status0.SetIcon(h_red) ,;
break;……,;break,;} } } }
UpdateData(FALSE); //顯示更新
for(int k=0,;k<8,;k++) { //數(shù)據(jù)清零
mon[k].totle=0;totle1=0,; //計數(shù)清零
┉┉ } CFormView::OnTimer(nIDEvent),;}
在界面框架頂端添加標(biāo)題和版本顯示[5]:
BOOL CGrouptestApp::InitInstance()
{ m_pMainWnd->ShowWindow(SW_SHOW) ;
m_pMainWnd->SetWindowText(
"XX視頻流量監(jiān)測系統(tǒng)"),;
m_pMainWnd->UpdateWindow();return TRUE;}
視頻流量監(jiān)測系統(tǒng)軟件實際運(yùn)行圖如圖3所示,。
通過實際Visual C++的開發(fā)使用,發(fā)現(xiàn)它的MFC類庫包含豐富的功能,,在滿足面向?qū)ο蟮拈_發(fā)應(yīng)用中由于包含了大量已封裝好的各個類和成員函數(shù),,因此對于串口通信、網(wǎng)絡(luò)通信,、定時發(fā)生器和界面顯示等都有對應(yīng)的類和函數(shù),,這樣極大地減少了源程序代碼的長度,,使程序的開發(fā)工作量大幅減少,從而提高了程序的質(zhì)量,。用于監(jiān)測視頻數(shù)字化設(shè)備流量的軟件采用Visual C++開發(fā)時廣泛采用了上述的MFC類和成員函數(shù),,在實際運(yùn)行過程中穩(wěn)定可靠,各個功能得到驗證,。
參考文獻(xiàn)
[1] GORLEN K E. An object-oriented class library for C++programs[C]. Software Pratice and Experience,1987,17(12): 899-922.
[2] HALBERT D C, BRIEN P D. Using types and inheritance in object-oriented languages[C]. European Conference on Object-oriented Programming, 1987:20-31.
[3] SHOPIRO J E. An example of multiple inheritance in C++: a model of the iostream library[J]. SIGPLAN Notices,1989, 24(12):32-36.
[4] CHUNG C M,, LEE M C. Object-oriented programming software metrics[J]. International Journal of Mini and Microcomputers, 1994,16(1):7-15.
[5] HEWITT J A. Techical service in 1983[J]. Library Resource Services,1984,28(3):205-218.