趙宇峰
(鄭州威科姆科技股份有限公司,河南 鄭州 450001)
責(zé)任編輯:任健男
三網(wǎng)融合作為國家級(jí)的長期戰(zhàn)略,目前已在全國多個(gè)省份進(jìn)行試點(diǎn),數(shù)字機(jī)頂盒是三網(wǎng)融合的著力點(diǎn),隨著嵌入式芯片技術(shù)的飛速發(fā)展,其性能越來越高、功能越來越復(fù)雜,所能承載的業(yè)務(wù)越來越多樣化。而且隨著視頻編碼功能開始在嵌入式芯片上逐漸普及,以往只能在PC上實(shí)現(xiàn)的實(shí)時(shí)視頻通信業(yè)務(wù)在機(jī)頂盒上實(shí)現(xiàn)也成為可能。本文提出一種基于海思HI3716C平臺(tái)的實(shí)時(shí)視頻通信的實(shí)現(xiàn)方法,該方法采用USB攝像頭+數(shù)字機(jī)頂盒+電視機(jī)的模式,應(yīng)用場(chǎng)景示意圖如圖1所示。USB攝像頭實(shí)現(xiàn)視頻原始數(shù)據(jù)的采集,機(jī)頂盒完成視頻數(shù)據(jù)的發(fā)送、接收以及編解碼,電視機(jī)作為顯示終端進(jìn)行視頻圖像的顯示,管理服務(wù)器用來對(duì)在線用戶進(jìn)行管理。
圖1 實(shí)時(shí)視頻通信應(yīng)用場(chǎng)景示意圖
機(jī)頂盒終端以海思HI3716C為主芯片。海思HI3716C芯片采用ARM Cortex A9架構(gòu)的處理器,頻率達(dá)到1 GHz,支持 H.264,MPEG-4,H.263 視頻編碼,最大分辨力可達(dá)720×576@25 f/s,視頻編碼提供動(dòng)態(tài)碼率和固定碼率模式,支持1路語音編碼。終端的硬件總體結(jié)構(gòu)如圖2所示。
圖2 機(jī)頂盒終端硬件總體結(jié)構(gòu)
終端的軟件部分使用嵌入式Linux操作系統(tǒng)和yaffs2文件系統(tǒng),并以此為基礎(chǔ)開發(fā)了視頻點(diǎn)播、網(wǎng)絡(luò)直播、本地磁盤音視頻播放、圖片瀏覽等多種業(yè)務(wù),本文實(shí)現(xiàn)的實(shí)時(shí)視頻通信是其眾多業(yè)務(wù)中的一種,本文也只對(duì)該業(yè)務(wù)的實(shí)現(xiàn)方法做重點(diǎn)描述。圖3是實(shí)時(shí)視頻通信業(yè)務(wù)的軟件框架,其中的UI是與用戶之間的接口,負(fù)責(zé)接收用戶指令以及呈現(xiàn)給用戶指令處理結(jié)果;指令處理模塊負(fù)責(zé)處理來自UI的指令和來自網(wǎng)絡(luò)的消息;網(wǎng)絡(luò)交互模塊用于同其他用戶和管理服務(wù)器進(jìn)行交互;采集編碼發(fā)送模塊完成視頻數(shù)據(jù)的采集、編碼和發(fā)送;接收解碼顯示模塊進(jìn)行視頻數(shù)據(jù)的接解碼及顯示。視頻實(shí)時(shí)通信的重點(diǎn)在視頻數(shù)據(jù)的采集、編解碼和發(fā)送,因此本文也主要圍繞這幾點(diǎn)進(jìn)行闡述。
圖3 視頻實(shí)時(shí)通信業(yè)務(wù)軟件框架
攝像頭設(shè)備是視頻數(shù)據(jù)來源,要進(jìn)行視頻通信,首先要使機(jī)頂盒能夠支持USB攝像頭設(shè)備。目前主流的USB攝像頭一般都符合UVC(USB Video Class)規(guī)范,Linux從2.6.26開始將UVC驅(qū)動(dòng)納入內(nèi)核原生包,只要在配置內(nèi)核時(shí)選擇了UVC選項(xiàng),嵌入式Linux系統(tǒng)就能很方便地支持符合UVC規(guī)范的USB攝像頭設(shè)備,另外Linux UVC驅(qū)動(dòng)支持V4L2應(yīng)用編程接口[1]。本文使用的Linux版本為2.6.35,其源碼中已經(jīng)包含的UVC驅(qū)動(dòng),內(nèi)核編譯完成后,在 linux-2.6.35/drivers/media/video/uvc 目錄下,生成的有uvcvideo.ko,在機(jī)頂盒啟動(dòng)時(shí)加載該驅(qū)動(dòng),在/dev目錄下生成video0設(shè)備節(jié)點(diǎn)。
在Linux系統(tǒng)進(jìn)行視頻采集基本都是用V4L2編程接口,V4L2(Video for Linux 2)是Linux內(nèi)核中視頻設(shè)備統(tǒng)一的驅(qū)動(dòng)接口,它采用流水線的方式,在打開視頻設(shè)備/dev/video0之后,剩余的操作完全通過ioctl系統(tǒng)調(diào)用向驅(qū)動(dòng)發(fā)送控制命令的方式來完成[2],所使用到的控制命令按照調(diào)用順序如下:
1)控制命令VIDIOC_QUERYCAP,用于查詢視頻設(shè)備的功能,如本文中使用的USB攝像頭設(shè)備應(yīng)該具有V4L2_CAP_VIDEO_CAPTURE功能。
2)控制命令VIDIOC_S_FMT,用來設(shè)置視頻設(shè)備的視頻數(shù)據(jù)格式,如視頻圖像數(shù)據(jù)的寬、高和像素格式等。
3)控制命令VIDIOC_REQBUFS,向V4L2驅(qū)動(dòng)請(qǐng)求分配視頻緩沖區(qū),申請(qǐng)多個(gè)視頻緩沖區(qū)可以提高捕獲圖像的效率,一般不超過5個(gè)。
4)控制命令VIDIOC_QUERYBUF,用于查詢?cè)谏弦徊街蟹峙涞囊曨l緩沖區(qū)的相關(guān)信息,如緩沖區(qū)在內(nèi)核中的偏移地址、長度等信息。因?yàn)橥ㄟ^VIDIOC_REQBUFS申請(qǐng)到的緩沖區(qū)在內(nèi)核空間,用戶空間不能直接訪問,所以一般會(huì)在通過VIDIOC_QUERYBUF命令獲得緩沖區(qū)相關(guān)信息后調(diào)用mmap內(nèi)存映射函數(shù)將內(nèi)核空間地址映射為用戶空間地址,之后應(yīng)用程序就可以直接訪問用戶空間地址來獲取捕獲到的視頻圖像。
5)控制命令VIDIOC_QBUF,將一個(gè)空閑的緩沖區(qū)放入視頻緩沖區(qū)輸入隊(duì)列中,在啟動(dòng)視頻采集后,捕獲到的視頻數(shù)據(jù)會(huì)被保存到此視頻緩沖區(qū),在保存有一幀視頻數(shù)據(jù)后,這個(gè)緩沖區(qū)會(huì)被放入輸出隊(duì)列,在調(diào)用后面的VIDIOC_DQBUF命令時(shí)就能獲取到該緩沖區(qū)。
6)控制命令VIDIOC_STREAMON,用于啟動(dòng)視頻采集。
7)控制命令VIDIOC_DQBUF,從視頻緩沖區(qū)的輸出隊(duì)列中取得一個(gè)已經(jīng)保存有一幀視頻數(shù)據(jù)的視頻緩沖區(qū),因?yàn)橹耙呀?jīng)調(diào)用mmap進(jìn)行過地址映射,所以應(yīng)用程序可以直接從用戶空間地址來讀取該視頻數(shù)據(jù),然后對(duì)視頻數(shù)據(jù)做進(jìn)一步的處理。處理完數(shù)據(jù)之后需要調(diào)用VIDIOC_QBUF,將該緩沖區(qū)仍舊放入輸入隊(duì)列中,進(jìn)行下一幀視頻數(shù)據(jù)的獲取。
在攝像頭捕獲到視頻數(shù)據(jù)之后,為便于數(shù)據(jù)的遠(yuǎn)程傳輸,需要對(duì)數(shù)據(jù)進(jìn)行壓縮編碼。海思HI3716C平臺(tái)支持H.264,H.263和MPEG-4編碼方式,考慮到后續(xù)工作中可能有機(jī)頂盒與手機(jī)終端視頻互同的需求,所以選擇在手機(jī)終端視頻解碼支持較好的H.263編碼。為便于應(yīng)用程序的編寫,海思平臺(tái)在應(yīng)用層中抽象出VI和VENC模塊,其中VI模塊負(fù)責(zé)將待編碼的視頻數(shù)據(jù)輸送給VENC模塊,VENC模塊負(fù)責(zé)對(duì)輸入的視頻數(shù)據(jù)進(jìn)行壓縮編碼,在海思SDK中提供的有2個(gè)模塊的應(yīng)用層API,這些API在實(shí)現(xiàn)視頻編碼時(shí)的調(diào)用關(guān)系如圖4所示。首先要調(diào)用相應(yīng)的接口完成VI模塊和VENC通道的創(chuàng)建,在調(diào)用HI_UNF_VENC_CreateChn接口時(shí)可以指定視頻碼率,然后調(diào)用HI_UNF_VENC_AttachInput接口將VI模塊和VENC通道進(jìn)行綁定,緊接著啟動(dòng)圖像采集線程并調(diào)用接口HI_UNF_VENC_Start,在圖像采集線程中將V4L2輸出隊(duì)列中的一個(gè)視頻緩沖區(qū)內(nèi)的數(shù)據(jù)拷貝到VI模塊的幀緩沖區(qū),VENC模塊對(duì)這些數(shù)據(jù)進(jìn)行壓縮編碼后輸出已編碼數(shù)據(jù)。
圖4 視頻編碼接口調(diào)用關(guān)系示意圖
經(jīng)過壓縮編碼的數(shù)據(jù)需要通過網(wǎng)絡(luò)傳輸給遠(yuǎn)端用戶,視頻傳輸對(duì)實(shí)時(shí)性、同步性要求很高,但是可以容忍數(shù)據(jù)丟失和出錯(cuò),因此所使用的傳輸協(xié)議可以是無可靠性保證的,但必須是實(shí)時(shí)高效的。UDP是面向無連接數(shù)據(jù)傳輸協(xié)議,由于其不提供數(shù)據(jù)的可靠性保證,所以傳輸時(shí)延較短,非常符合視頻傳輸?shù)囊?。另外家庭用戶終端多處于NAT設(shè)備之后,使用內(nèi)網(wǎng)IP地址,這種情況下2個(gè)機(jī)頂盒進(jìn)行互聯(lián)互通需要進(jìn)行NAT穿透,UDP穿透NAT的技術(shù)相對(duì)成熟。鑒于以上原因,在此選擇UDP協(xié)議作為視頻傳輸協(xié)議。
因?yàn)镮P協(xié)議最大傳輸單元(MTU)為1500 byte,如果UDP數(shù)據(jù)包長度過長,超過了MTU的長度,在IP層就會(huì)對(duì)其進(jìn)行重新分組,這樣會(huì)大大降低傳輸?shù)男?,而且出錯(cuò)概率大大增加,所以最好將視頻數(shù)據(jù)經(jīng)過分組打包之后再進(jìn)行UDP傳輸,以保證在傳輸時(shí)不會(huì)造成IP碎片。本文借鑒RTP協(xié)議中RTP分組頭的格式[3],使用其中的M(Marker)、PT(Payload Type)和 SN(Sequence Number)字段,其中M字段占用1 bit,用來表示幀結(jié)束,PT字段占用7 bit,用來表示數(shù)據(jù)編碼類型(按照RTP協(xié)議,H.263對(duì)應(yīng)的類型為34),SN字段占用16 bit,用來計(jì)數(shù),每發(fā)送1個(gè)數(shù)據(jù)包該字段就加1。M,PT和SN字段的總長度占3 byte,UDP的首部是8 byte,IP協(xié)議的首部是20 byte,這樣每個(gè)UDP數(shù)據(jù)包中承載的靜負(fù)荷的最大長度是1469。對(duì)于長度大于1469的視頻幀數(shù)據(jù)要被打包成多個(gè)分組進(jìn)行傳輸。比如一個(gè)長度為5000 byte的視頻幀就要分為4個(gè)分組進(jìn)行傳輸,如圖5所示。
圖5 視頻數(shù)據(jù)分組封裝示意圖
在接收到遠(yuǎn)端用戶發(fā)送過來的視頻數(shù)據(jù)之后,需要對(duì)數(shù)據(jù)進(jìn)行解碼顯示。與視頻編碼相類似,海思平臺(tái)在應(yīng)用層抽象出了AVPlay和VO模塊,AVPlay模塊用于音視頻的解碼和同步,VO模塊負(fù)責(zé)視頻的顯示。運(yùn)用模塊的API可以很方便地實(shí)現(xiàn)視頻解碼和顯示,調(diào)用關(guān)系示意圖如圖6所示。
圖6 視頻解碼接口調(diào)用關(guān)系示意圖
完成了AVPlay模塊和VO模塊的創(chuàng)建后需要調(diào)用HI_UNF_VO_AttachWindow接口將兩者綁定,同時(shí)要調(diào)用HI_UNF_VO_SetWindowEnable接口將VO中創(chuàng)建的窗口使能,否則視頻是不會(huì)顯示的。然后調(diào)用HI_UNF_AVPLAY_Start接口并啟動(dòng)數(shù)據(jù)接收線程,在線程中接收到來自遠(yuǎn)端用戶的視頻數(shù)據(jù)包之后,需要組裝成幀放入AVPlay幀緩沖區(qū)。此后只要AVPlay緩沖區(qū)中有數(shù)據(jù)就會(huì)進(jìn)行視頻數(shù)據(jù)的解碼。
本節(jié)以用戶A和用戶B要進(jìn)行視頻通信為例來描述一下數(shù)據(jù)的交互過程。
當(dāng)用戶啟動(dòng)機(jī)頂盒后,需要向管理服務(wù)器發(fā)送登錄信息,管理服務(wù)器對(duì)用戶信息進(jìn)行記錄,用戶信息包括用戶名、所使用的本地IP、端口號(hào)以及是否接有USB攝像頭設(shè)備等,同時(shí)服務(wù)器能獲取到它們登錄管理服務(wù)器所使用的公網(wǎng)IP和端口號(hào),也作為用戶信息進(jìn)行保存。當(dāng)用戶需要查看在線用戶信息時(shí),向管理服務(wù)器發(fā)送獲取在線用戶信息的請(qǐng)求,服務(wù)器將當(dāng)前在線的所有用戶的信息返回給用戶。
當(dāng)用戶A希望和用戶B進(jìn)行視頻通信時(shí),首先要判斷對(duì)方是否和自己在同一局域網(wǎng),如果是在同一局域網(wǎng),用戶A可以直接向用戶B的本地IP和端口號(hào)發(fā)送請(qǐng)求,用戶B收到請(qǐng)求后返回給用戶A確認(rèn)信息,同時(shí)啟動(dòng)視頻編解碼,用戶A在收到用戶B的確認(rèn)信息后也啟動(dòng)視頻編解碼,兩用戶的實(shí)時(shí)視頻通信就此建立。如果用戶A和用戶B不在同一局域網(wǎng),則需要進(jìn)行UDP穿透NAT[4],用戶A向管理服務(wù)器發(fā)送穿透請(qǐng)求,管理服務(wù)器收到之后向用戶B發(fā)送穿透NAT指令,用戶B向用戶A的公網(wǎng)IP和端口方向隨便發(fā)送一些數(shù)據(jù)即完成了NAT穿透,用戶A在等待一段時(shí)間之后向用戶B的公網(wǎng)IP和端口發(fā)送視頻通信請(qǐng)求,此時(shí)該請(qǐng)求會(huì)被用戶B成功接收,之后兩者可以正常進(jìn)行通信。此時(shí)管理服務(wù)器起到了輔助UDP穿透的作用。圖7所示是用戶A和用戶B不在同一局域網(wǎng)的情況。
本文在嵌入式Linux機(jī)頂盒上實(shí)現(xiàn)了實(shí)時(shí)視頻通信功能,在局域網(wǎng)內(nèi),能較通暢地進(jìn)行碼率為1 Mbit/s的實(shí)時(shí)視頻通信,在公網(wǎng)上則需要將碼率限制在500 kbit/s以下。本文只是在初步實(shí)現(xiàn)了實(shí)時(shí)視頻通信,驗(yàn)證了其在嵌入式終端實(shí)現(xiàn)的可行性,在后續(xù)的工作中仍需要對(duì)業(yè)務(wù)功能進(jìn)行不斷優(yōu)化,首先要添加對(duì)音頻的支持,其次要增加根據(jù)網(wǎng)絡(luò)狀況進(jìn)行編碼碼率動(dòng)態(tài)調(diào)整功能的支持,另外還要考慮機(jī)頂盒與其他類型終端的實(shí)時(shí)視頻通信。
圖7 不同局域網(wǎng)內(nèi)兩終端視頻通信建立過程
[1]唐人財(cái),劉連浩.基于嵌入式Linux遠(yuǎn)程圖像監(jiān)控系統(tǒng)的設(shè)計(jì)[J].計(jì)算機(jī)與現(xiàn)代化,2010(11):31-38.
[2]劉升,趙晶晶,范秀麗.基于V4L2的嵌入式視頻監(jiān)控系統(tǒng)[J].微計(jì)算機(jī)應(yīng)用,2011,32(1):37-42.
[3]陳慶余,朱光喜.一種MPEG-4 over RTP的實(shí)現(xiàn)方法[J].電視技術(shù),2001,25(6):27-29.
[4]劉昊東,陳天煌.基于P2P網(wǎng)絡(luò)中UDP穿透NAT技術(shù)的研究[J].計(jì)算機(jī)與數(shù)字工程,2009,37(12):112-114.