1. 程式人生 > >【視訊開發】RTSP SERVER(基於live555)詳細設計

【視訊開發】RTSP SERVER(基於live555)詳細設計

/*

*本文基於LIVE555的嵌入式的RTSP流媒體伺服器一個設計文件,箇中細節現剖於此,有需者可參考指正,同時也方便後期自己查閱。(本版本是基於2011年的live555)

作者:[email protected]

*/

RTSP SERVER(基於live555)詳細設計

這個server的最終情況如下:

效能:D1資料時:

1.8路全開udp

  PID USER     PR  NI  VIRT RES  SHR S %CPU %MEM    TIME+ COMMAND           

 1175root      20   0 65712 23m 3112 R 29.0 34.5 285:13.05 dvrapp_SN6108 

2.8路全開tcp

  PID USER     PR  NI  VIRT RES  SHR S %CPU %MEM    TIME+ COMMAND           

 1175root      20   0 65612 23m 3112 R 31.0 34.4 289:03.86 dvrapp_SN6108

檔案:

靜態庫大小:Live555.a  1,585KB

檔案個數:  150左右

目錄

1.程式碼移植

live555程式碼移植到我司嵌入式平臺上。

1.1程式碼獲取

本次移植使用的版本是2011.12.23.

1.2檔案初步裁剪

Live555為跨平臺庫,本移移植旨在arm linux上執行,所以需先裁剪掉其它無關檔案。

a.刪除之前先生成用於linuxmakefile.進入live資料夾,執行./genMakefiles linux.此時生成了用於linuxMakefile.

b.刪除冗餘檔案和資料夾。

每個資料夾下只保留*.cpp,*.hh, *.c, *.h, Makefile.其餘全部刪除。

刪除資料夾:WindowsAudioInputDevicemediaServer

1.3修改Makefile

Makefile變數主要做以下修改:

C_COMPILER =              arm-hismall-linux-gcc

CPLUSPLUS_COMPILER =    arm-hismall-linux-g++

LINK =                    arm-hismall-linux-g++-o

LINK_OPTS =          -L.-lpthread  

C_FLAGS =              $(COMPILE_OPTS)     $(CFLAGSARM)

CPLUSPLUS_FLAGS =    $(COMPILE_OPTS)$(CFLAGSARM)

1.4live555生成的靜態庫連結到我司的可執行程式中

如:dvrapp_sn6108

       裁剪到此時的Live555編譯時會生成所有的庫,不可能將所有庫鏈進可執行程式中。我司只用到了視訊:H264,音訊 G711.a.其餘不用。故只需將這幾個有關庫連結進即可。

       我司連結靜態庫到可執行程式的做法是,先成一個xx.a,然後在生成可執行程式時,連結所有.a檔案。所以我們這裡只需將需要的.o檔案裝進live555.a即可。

       具體做法:在live555資料夾下新建資料夾liveLib用於存放其它檔案生成的.a. 此資料夾用於生成live555.a最終庫。(將所有.a先打散成.o,再合成一個live555.a.每個資料夾下在生成.a時都拷貝一份.aliveLib中。最後將生成的live555.a拷貝到master\LIB\ARM\SN6108中。可執行程式連結時在此資料夾下可找到live555.a

2.功能新增

       Live555原始碼的功能要用到我司具體專案中還需做一定的修改,不是拿來就能用的。

原本對於開源專案,尤其是c++專案,最好不要改動原有的類,所以修改應該是繼承父類,在子類中修改。這樣有利於程式碼的升級,和維護。但是由於考慮到c++的繼承的層數以及虛擬函式的執行時繫結對效能的影響,以及編譯出文件的大小,所以本修改中只對一部分類作了繼承,另一些直接在原有類中新增新方法。

2.1 資料的輸入

       資料從ringbufferserver模組的傳輸,使用了通知機制,即當ringbuf有資料時,資料發出通道,告知某一通道有資料可用,則server在需要的時候會來這個通道來取資料。

spacer.gif     資料輸入框圖

通知機制主要由以下函式實現。

void signalNewFrameData(int mediaType, int chanel,int trans_mode,int buf_len)

       signalNewFrameData為一個全域性函式,可被外部執行緒呼叫(注意,整個live555是一個單執行緒程式)。signalnewFrameData會呼叫virtual void triggerEvent(EventTriggerId eventTriggerId, void*clientData = NULL), 這個函式共兩個引數,一個是觸發的事件id,另一個是此id對應的事件處理函式所在的類例項指標,這裡具體是各個輸入的videoSourceaudioSource類例項指標。

       資料的寫入ringbuffer由以下函式實現:

       write_unicast_data_live(只用於單播),每當一個數據到來後,先判斷是音訊還是視訊,然後再裝入各自對應的ringbuffer,接著呼叫 signalnewFrameData通知相應的server. 通知時刻落在server剛好需要資料的時刻區間間的概率較小,大部分情況是(經實驗證明了的)server正在處理其它資料或已經取完資料,正在等下次取資料時刻的到來(此時可能正停留在Eventloop 裡的sigleStepselect)。所以通知後都會把事件記入一個bitmask型別的變數fTriggersAwaitingHandling(最多可累計掛入32個待處理事件)然後在select結束後,再處理每個TriggerNum所對應的事件(呼叫Source中的deliverFrame將資料向後傳送)。處理完一個事件,則將fTriggersAwaitingHandling中對應的bitmask位清0,singleStep每一次迴圈中TriggerEvent只處理一個事件(如有未處理完事件,等下一迴圈再處理)

2.2實時視訊(H264)的輸入和輸出

       Live555提供的示例裡面有直接讀檔案的類和使用方法,但沒有實時輸入的類及其實現。

       Live555中資料流基本路線是:

       SourceàFilter1àFilter2…àSink

       Filter可能有多個,也可能一個也沒有。對於h264filter有兩個,對於音訊g711.a,實現中則沒有Filter.

       本設計具體實現視訊實時輸入方法如下:      

1.視訊輸入

       live555中,輸入為Source類,輸出為 Sink類。中間處理環節類稱為Filter.

SNDeviceSource類繼承於FramedSource,用於實時輸入h264視訊。該類的實現參考了DeviceSource

SNDeviceSource::deliverFrame()中實現資料的輸入。Memmove 將資料拷貝到fTo.(最後優化後已經改為傳指標了,沒有了記憶體拷貝).

2 .Filter

       對於H264視訊Source不是直接到Sink, 而是經過如下:

       SNDeviceSourceàH264VideoStreamDiscreteFrameràH264FUAFragmenteràH264VideoRTPSink

       中間兩個類稱為Filter,是對視訊資料的進一步處理。

       H264VideoStreamDiscreteFramer繼承於H264VideoStreamFramer, H264VideoStreamFramer主要是提取sps, ppsH264VideoStreamDiscreteFramer 主要是用於輸入 離散的NAL單元,H264FUAFragmenter主要是對H264nal進行分片打包,根據rfc3984FU-A規則進行分片打包。

3 .視訊輸出

       H264VideoRTPSink為原有類實現了h264 rtp包的輸出.

       視訊的打包和傳送操作都在H264VideoRTPSink的父類multiFramedRTPSink.其打包傳送的流程如下:

sendNextàbuildandSendPacketàpackFrameàgetnextFrameààafterGettingFrameàsendPacketIfnessaryàscheduleDelayedTask àsendNextà。。。

       buildandSendPacket會將rtp包頭打好(timestamps and sequence num先預留,等取到幀資料後再填充)。

       packFrame即將幀資料往rtp包頭後面掛。所以其任務就是要取得幀資料,所以呼叫getnextFrame來從上一遊來獲取幀資料。對於h264來說,它的上一級由2.12可看出是H264FUAFragmenter, 這個類會將幀資料分好片(<1448位元組), 交給multiFramedRTP Sink。好,獲取到幀資料後,就是afterGettingFrame了,這裡面主要做一些檢查工作(檢查資料是否正確,buffer是否溢位等各種檢查)和補充工作(填充前面所說的timestampsrtp sequence number)。之後便是sendPakcetIfnessary, 這裡面會使用tcpudp將資料傳送出去。傳送完後,需將下一次任務準備一下,即呼叫scheduleDelayedTasksendNext放入延遲佇列中,等