郝 朝,劉升護(hù)
(中國飛行試驗(yàn)研究院,陜西 西安 710089)
飛行試驗(yàn)是驗(yàn)證航空產(chǎn)品設(shè)計(jì)指標(biāo)要求和檢驗(yàn)航空產(chǎn)品質(zhì)量改進(jìn)提高航空產(chǎn)品性能進(jìn)行航空新理論和航空新技術(shù)研究的重要手段。在飛行試驗(yàn)中,機(jī)載視頻影像能以最為直觀與準(zhǔn)確的方式描述飛機(jī)內(nèi)部各獨(dú)立子系統(tǒng)的健康狀態(tài),為地面飛行指揮人員和試飛工程師提供及時(shí)豐富的信息,對保障飛行安全、提高試飛效率有不可替代的作用[1]。
目前機(jī)載抽引的視頻是以PCM流[1]的形式進(jìn)行遙測下傳。地面遙測天線接收到射頻信號后通過PCM接收機(jī)進(jìn)行解調(diào),視頻服務(wù)器接收視頻數(shù)據(jù)并進(jìn)行分路提取處理,然后以UDP組播[2-3]的形式發(fā)送給客戶端進(jìn)行監(jiān)控顯示。
基于以上分析,提出基于FFMPEG[4-6]和SDL[7-8]的遙測視頻實(shí)時(shí)解析方案??蛻舳私邮辗?wù)器發(fā)送的多路視頻數(shù)據(jù)包,經(jīng)過解包、拼幀,將一幀完整的視頻數(shù)據(jù)送給FFMPEG進(jìn)行解碼,得到Y(jié)UV[9-10]圖像數(shù)據(jù),然后采用SDL進(jìn)行顯示。
PCM(pulse-code moduliation)稱為脈沖編碼調(diào)制。PCM數(shù)據(jù)傳輸以其抗干擾能力強(qiáng)、數(shù)據(jù)帶寬大等特點(diǎn),廣泛應(yīng)用于航空試驗(yàn)領(lǐng)域。PCM數(shù)據(jù)中一個(gè)完整的幀稱為全幀,每一個(gè)全幀可以包含一個(gè)或者若干個(gè)子幀。數(shù)據(jù)中的每個(gè)子幀由同步字和數(shù)據(jù)字組成。目前飛行試驗(yàn)中,應(yīng)用的機(jī)載視頻采集器有MiniR700和通用采集器兩種。
不同的采集器對應(yīng)的PCM幀視頻數(shù)據(jù)存放格式也不相同。對于MiniR700視頻采集器,PCM幀視頻格式如圖1所示。
圖1 MiniR700采集器視頻PCM幀格式
假設(shè)該飛機(jī)一共有三路視頻(V1、V2、V3)進(jìn)行遙測下傳,則每一路視頻均是以TS流[11-12]的形式存儲(chǔ)在PCM幀結(jié)構(gòu)中,每一子幀中各路視頻數(shù)據(jù)交替出現(xiàn)。為了保證PCM帶寬,數(shù)據(jù)中會(huì)存在填充字(FADE)的現(xiàn)象。服務(wù)器進(jìn)行處理時(shí),首先通過ID字將每個(gè)子幀拼成一個(gè)完整的PCM全幀,然后分別提取每一路的視頻數(shù)據(jù),最后通過UDP組播的形式進(jìn)行發(fā)送,發(fā)送數(shù)據(jù)包格式為【第幾路】【該路視頻數(shù)據(jù)】。
對于通用采集器,PCM幀視頻格式如圖2所示。服務(wù)器進(jìn)行處理時(shí),只需要將接收到的數(shù)據(jù)包以組播的形式進(jìn)行轉(zhuǎn)發(fā)即可。
圖2 通用采集器視頻PCM幀格式
通用采集器視頻PCM幀只有一個(gè)子幀,幀長512個(gè)字。一個(gè)完整的視頻畫面數(shù)據(jù)需要拆分為上百個(gè)PCM幀,每個(gè)PCM幀內(nèi)的圖像數(shù)據(jù)定義如表1所示。
客戶端通過UDP組播接收服務(wù)器發(fā)送的視頻數(shù)據(jù)包。UDP組播初始化流程為:采用WSAStartup的初始化Winsock;創(chuàng)建套接字;采用bind綁定端口號;采用IP_ADD_MEMBERSHIP加入組播組;采用非阻塞的異步套接字WSAAsyncSelect()實(shí)現(xiàn)網(wǎng)絡(luò)接收,對網(wǎng)絡(luò)事件采用基于消息的異步存取策略,能夠方便地處理網(wǎng)絡(luò)通信。當(dāng)接收到服務(wù)器視頻數(shù)據(jù)包時(shí),會(huì)觸發(fā)FD_READ消息。接收到每路視頻數(shù)據(jù)包后,存入相應(yīng)的FIFO隊(duì)列。
表1 影像數(shù)據(jù)幀頭格式
對于MiniR700采集器的視頻PCM幀,首先需要剔除填充字,然后對TS流進(jìn)行解封裝,拼成完整的一幀視頻數(shù)據(jù)。TS流為188字節(jié)的固定包長度,好處是便于找到幀的起始位置,易于從包丟失中恢復(fù),適合于有誤碼的環(huán)境。TS流格式如圖3所示。包頭為4個(gè)字節(jié),負(fù)載為184個(gè)字節(jié)。
圖3 TS流格式
同步字節(jié)固定為0x47,占1個(gè)字節(jié),該字段是MPEG-2 TS傳送包標(biāo)識(shí)符。PID占13位,表示傳送包的有效凈荷中的數(shù)據(jù)類型。根據(jù)PID將TS上從不同ES(elementary stream,視頻基本流)來的TS包區(qū)分開,以重建原來的ES。為了還原視頻數(shù)據(jù),還需要傳輸節(jié)目隨帶信息及解釋有關(guān)TS特定結(jié)構(gòu)的信息(元數(shù)據(jù)),即節(jié)目特定信息(program specific information,PSI),用于說明:1個(gè)視頻是由多少個(gè)ES組成的;1個(gè)視頻是由哪些個(gè)ES組成的;在哪些個(gè)PID情況下,1個(gè)相應(yīng)的解碼器能找到TS中的各個(gè)數(shù)據(jù)包。為了重建原來的ES,就要追蹤從不同ES來的TS包及其PID。因此,一些映射結(jié)構(gòu)(mapping mechanism),如節(jié)目源結(jié)合表(PAT,PID=0)和節(jié)目源映射表(PMT),會(huì)以打包的形式存在于TS上,即借助于PSI傳輸一串描述了各種ES的表格來實(shí)現(xiàn)。有了PAT及PMT這兩種表,就可以根據(jù)PID將TS上從不同ES來的TS包區(qū)分開。首先從PID=0的PAT上找出帶有PMT的那個(gè)節(jié)目源,然后從所選擇的PMT中找到組成該節(jié)目源的各個(gè)ES的PID。將TS流還原成一幀完整視頻數(shù)據(jù)后存入解碼緩沖區(qū)。
對于通用采集器視頻PCM幀,接收到每路視頻數(shù)據(jù)包后,將多個(gè)視頻數(shù)據(jù)包依據(jù)有效數(shù)據(jù)長度、影像幀長度拼成完成一幀視頻后送給解碼器解碼。拼幀流程如圖4所示。首先從接收緩沖區(qū)中取出一個(gè)視頻數(shù)據(jù)包,如果當(dāng)前數(shù)據(jù)幀ID不等于上一數(shù)據(jù)幀ID,說明為新的一幀視頻,將Frame.length置0,將Frame.ID置為新的視頻幀ID。否則說明為同一視頻幀。將有效視頻數(shù)據(jù)進(jìn)行提取放入解碼緩沖區(qū),如果Frame.length等于該影像幀的總長度,則說明已拼成一幀完整視頻,送給解碼器進(jìn)行解碼。
圖4 拼幀流程
為了保證在視頻解碼的同時(shí)不丟失數(shù)據(jù)包,采用多線程并發(fā)[13-14]與多緩沖區(qū)[15]機(jī)制,針對數(shù)據(jù)接收、數(shù)據(jù)包解析、視頻解碼與顯示分別開辟單獨(dú)的線程。設(shè)置兩個(gè)緩沖區(qū):數(shù)據(jù)接收緩沖區(qū)與解碼緩沖區(qū)。數(shù)據(jù)接收線程接收到遙測視頻數(shù)據(jù)包后將數(shù)據(jù)包存入接收緩沖區(qū)。數(shù)據(jù)包解析線程從緩沖區(qū)中提取數(shù)據(jù)包進(jìn)行解包與組幀,將一幀完整視頻數(shù)據(jù)存入解碼緩沖區(qū)。視頻解碼線程從緩沖區(qū)中提取一幀視頻進(jìn)行解碼并顯示。各線程之間采用互斥量進(jìn)行同步操作。
FFMPEG是一個(gè)開源且跨平臺(tái)的音視頻流方案,具備高可移植性和編解碼質(zhì)量,為音視頻轉(zhuǎn)換、解碼以及流化提供了完整的解決方案。libavcodec包含全部FFMPEG音頻/視頻編解碼庫,相關(guān)數(shù)據(jù)結(jié)構(gòu)包括AVFormatContext、AVCodecContext、AVCodec、AVPacket、AVFrame 與AVPicture等。
FFMPEG解碼視頻流的流程如下:
(1)對FFMPEG進(jìn)行初始化。
av_register_all()
avformat_network_init();
(2)對pFormatCtx進(jìn)行初始化設(shè)置,包括width、height。
(3)找到對應(yīng)格式的視頻解碼器。
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
(4)打開對應(yīng)格式的視頻解碼器。
avcodec_open2(pCodecCtx, pCodec,NULL);
(5)對一幀視頻數(shù)據(jù)采用相應(yīng)的解碼器進(jìn)行解碼。
avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
在程序運(yùn)行時(shí)讀取配置文件,執(zhí)行步驟1~4對FFMPEG進(jìn)行設(shè)置,然后循環(huán)從解碼緩沖區(qū)中提取一幀視頻數(shù)據(jù),調(diào)用步驟5進(jìn)行解碼得到Y(jié)UV數(shù)據(jù),向圖像顯示子線程發(fā)送消息。
SDL(simple directmedia layer)是一套基于C語言的跨平臺(tái)多媒體開發(fā)庫,提供了多種控制音視頻輸入與輸出的函數(shù)。SDL顯示YUV數(shù)據(jù)流程如下:
(1)對SDL進(jìn)行初始化。
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
(2)利用控件創(chuàng)建SDL顯示窗口。
screen=SDL_CreateWindowForm(m_hwnd);
(3)基于窗口創(chuàng)建渲染器。
sdlRenderer=SDL_CreateRenderer(screen,-1,0);
(4)創(chuàng)建紋理用于顯示YUV數(shù)據(jù)。
sdlTexture=SDL_CreateTexture(sdlRenderer,SDL_PIXELFORMAT_IYUV,1,width,height);
(5)設(shè)置紋理的像素?cái)?shù)據(jù)。
SDL_UpdateTexture(sdlTexture,NULL,pFrameYUV→data[0],pFrameYUV→linesize[0]);
(6)將紋理數(shù)據(jù)復(fù)制給渲染目標(biāo)。
SDL_RenderCopy(sdlRenderer,sdlTexture,NULL,NULL);
(7)顯示畫面。
SDL_RenderPresent(sdlRenderer)。
執(zhí)行步驟1~4實(shí)現(xiàn)SDL初始化顯示設(shè)置,接收到解碼線程發(fā)送的消息,調(diào)用步驟5~7即可實(shí)現(xiàn)對YUV圖像的顯示。
軟件界面如圖5所示。選擇飛機(jī)與服務(wù)器,點(diǎn)擊開始按鈕,通過組播接收服務(wù)器視頻數(shù)據(jù)包。應(yīng)用效果表明,該軟件能夠?qū)崿F(xiàn)多路遙測視頻的解碼與顯示,時(shí)延滿足實(shí)時(shí)監(jiān)控需求。
圖5 實(shí)際應(yīng)用效果
為了解決機(jī)載多路視頻實(shí)時(shí)監(jiān)控的問題,設(shè)計(jì)了基于FFMPEG和SDL的遙測視頻實(shí)時(shí)解析軟件??蛻舳送ㄟ^UDP組播接收服務(wù)器發(fā)送的PCM視頻數(shù)據(jù)包,通過解包并拼成完整的一幀視頻數(shù)據(jù),送給FFMPEG進(jìn)行解碼并通過SDL進(jìn)行顯示。該軟件已經(jīng)應(yīng)用于多個(gè)型號試飛視頻實(shí)時(shí)監(jiān)控中,正確性和可靠性得到驗(yàn)證,為保障型號試飛的高效順利進(jìn)行發(fā)揮了重要作用。