宋勇強(qiáng)
(山西大眾電子信息產(chǎn)業(yè)集團(tuán)有限公司,山西 太原 030024)
多路視頻實(shí)時(shí)傳輸往往對實(shí)時(shí)性有很高的要求,要在滿足實(shí)時(shí)性的同時(shí)保證視頻的碼率,不僅要求有完善的視頻編解碼功能,而且對網(wǎng)絡(luò)傳輸控制也要嚴(yán)格控制,還要考慮網(wǎng)絡(luò)中斷的異常處理,重連機(jī)制,雖然網(wǎng)上有很多類似的例子,但是要不就是不開源,修改困難,要不就是沒有結(jié)合項(xiàng)目,很難在實(shí)際中應(yīng)用。為了真正實(shí)現(xiàn)此功能,我以Microsoft Visual C++6.0作為編譯環(huán)境,用多線程的方式實(shí)現(xiàn)了對8路視頻的編解碼顯示處理。下面結(jié)合項(xiàng)目進(jìn)行詳細(xì)敘述。
本軟件主要功能為通過網(wǎng)口同時(shí)接收8路視頻,根據(jù)實(shí)際需求同時(shí)顯示其中3路視頻到三個顯示器,界面上方的狀態(tài)指示以及右側(cè)列表是通過串口接收操縱桿數(shù)據(jù)進(jìn)行相應(yīng)處理,顯示操縱桿的實(shí)時(shí)狀態(tài)和命令數(shù)據(jù),此文章不作為重點(diǎn),不再詳細(xì)描述,界面左下綠色矩形顯示經(jīng)過處理解碼后的視頻,軟件形成最終效果如圖1。
軟件通過在主對話框定義三個視頻對話框?qū)ο髮?shí)現(xiàn)不同顯示器的圖像顯示,網(wǎng)絡(luò)的初始化以及8路視頻接收線程函數(shù)都在主對話框中定義實(shí)現(xiàn),主對話框收到視頻數(shù)據(jù)后通過消息發(fā)送到相應(yīng)的視頻對話框,視頻對話框?qū)σ曨l數(shù)據(jù)進(jìn)行解碼處理并顯示。軟件組成圖如圖2 。
圖1 軟件界面效果圖
圖2 軟件組成圖
CMultipleVideoDlg類是軟件的主對話框類,主要功能包括對網(wǎng)絡(luò)的初始化,8個視頻處理線程的實(shí)現(xiàn)等。
2.1.1 初始化網(wǎng)絡(luò)
初始化網(wǎng)絡(luò)SOCKET功能,需要在CMultipleVideoApp的InitInstance()函數(shù)中添加
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}
然后通過WSAStartup(MAKEWORD(2,2),&WSAData))函數(shù)安裝SOCKET。這樣就可以正常使用網(wǎng)絡(luò)接收發(fā)送函數(shù)了。
2.1.2 接收處理線程的實(shí)現(xiàn)
主對話框通過AfxBeginThread(AFX_THREADPROC(&RecvData1),this)啟動8個線程來對8路視頻數(shù)據(jù)同時(shí)進(jìn)行接收及處理。線程是系統(tǒng)分配處理器時(shí)間資源的基本單位,是程序執(zhí)行流的最小單元,編程中往往把接收各種端口數(shù)據(jù)作為獨(dú)立的線程執(zhí)行,保障在軟件進(jìn)行其他操作時(shí)接收線程能夠?qū)崟r(shí)接收數(shù)據(jù)。在進(jìn)行網(wǎng)絡(luò)編程時(shí),我們常常見到同步(Sync)/異步(Async),阻塞(Block)/非阻塞(Unblock)四種程序調(diào)用方式。同步就是程序調(diào)用一個功能,該功能沒有結(jié)束前,程序死等結(jié)果。異步就是程序調(diào)用一個功能,不需要知道該功能結(jié)果,該功能有結(jié)果后通知程序(回調(diào)通知)。阻塞就是調(diào)用函數(shù),函數(shù)沒有接收完數(shù)據(jù)或者沒有得到結(jié)果之前,程序不會返回。非阻塞就是調(diào)用函數(shù),函數(shù)立即返回,通過select通知調(diào)用者[1]。本軟件因?yàn)橐瑫r(shí)接收8路視頻,如果某一路視頻斷開連接或數(shù)據(jù)錯誤,必須保證其他視頻正常接收處理,所以必須采用異步非阻塞模式。當(dāng)把網(wǎng)絡(luò)SOCKET設(shè)置為非阻塞模式時(shí),相當(dāng)于通知系統(tǒng)當(dāng)數(shù)據(jù)沒有準(zhǔn)備好的時(shí)候不要讓線程睡眠,而是應(yīng)立即返回一個錯誤代碼;當(dāng)數(shù)據(jù)準(zhǔn)備就緒時(shí),返回成功指示,應(yīng)用程序開始處理數(shù)據(jù)。處理線程間通信有臨界區(qū)、互斥、信號量、事件等方法,本軟件采用了利用事件對象的方法處理進(jìn)程異步,一個事件對象有兩種狀態(tài):信號態(tài)與非信號態(tài)。線程能監(jiān)視于信號態(tài)的事件,以便在適當(dāng)?shù)臅r(shí)間完成對事件的操作。8個線程對應(yīng)的事件都在初始時(shí)候建立,默認(rèn)狀態(tài)為非信號態(tài),然后在線程內(nèi)部判斷時(shí)間狀態(tài),當(dāng)需要把某一路視頻顯示到視頻界面時(shí),利用SetEvent()把事件置為信號態(tài),平時(shí)線程處于掛起狀態(tài),當(dāng)事件變?yōu)樾盘枒B(tài)時(shí),線程繼續(xù)執(zhí)行。接收線程采用非阻塞模式,如果網(wǎng)絡(luò)端口暫時(shí)無數(shù)據(jù)則稍后再試,如果數(shù)據(jù)錯誤,則斷開SOCKET,間隔時(shí)間進(jìn)行重連,如果對方SOCKET正常結(jié)束,則正常退出,線程掛起。線程處理流程如圖3。
圖3 線程處理流程圖
2.1.3 網(wǎng)絡(luò)連接函數(shù)的實(shí)現(xiàn)
初始化SOCKET網(wǎng)絡(luò)連接函數(shù)是程序一開始和出現(xiàn)網(wǎng)絡(luò)故障或數(shù)據(jù)錯誤時(shí)進(jìn)行的操作,,因?yàn)榻邮站€程中經(jīng)常需要重新初始化SOCKET進(jìn)行重練,為了避免線程卡死,連接函數(shù)必須采用無阻塞的方式,當(dāng)無法連接或連接錯誤時(shí)程序需要直接返回錯誤信息,軟件通過對錯誤信息的判斷進(jìn)行重連或其他操作,不影響主程序的執(zhí)行,網(wǎng)絡(luò)連接函數(shù)的流程如圖4。
圖4 網(wǎng)絡(luò)連接流程圖
CMulVideo類共有三個對象,分別對應(yīng)3個視頻顯示對話框,本類的主要功能是接收視頻數(shù)據(jù)進(jìn)行解碼并顯示。CMulVideo類接收線程收到正確視頻數(shù)據(jù)后發(fā)送的消息進(jìn)行解碼處理。然后以40 ms每幀進(jìn)行顯示。軟件編解碼采用了H264格式,通過timeSetEvent()函數(shù)(VC提供的精確定時(shí)器,精確到毫秒級)來控制顯示視頻的周期。顯示圖像時(shí)沒有采用DIRECTX,而是采用了YUV轉(zhuǎn)RGB,然后調(diào)用MFC系統(tǒng)繪圖函數(shù)實(shí)現(xiàn)圖像的顯示,其目的是為了方便以后在視頻上進(jìn)行繪圖,畫準(zhǔn)心等操作,而且DIRECTX對多顯示的處理比較繁瑣,需要獲取顯卡驅(qū)動,而RGB只需要擴(kuò)展坐標(biāo)顯示就可以實(shí)現(xiàn)三個顯示器顯示不同視頻的功能。
本文結(jié)合實(shí)際項(xiàng)目講解了多線程處理多路視頻的方法,尤其是對非阻塞方式的網(wǎng)絡(luò)連接、接收處理數(shù)據(jù)以及多線程同步,數(shù)據(jù)交互進(jìn)行了詳細(xì)描述,對視頻編解碼以及精確定時(shí)也有一定的闡述,而且此項(xiàng)目經(jīng)過驗(yàn)證,在實(shí)際中有了良好的應(yīng)用。是多線程編程和視頻處理很好地參考資料。