1. 程式人生 > >RTSP協議,這個寫的不錯,賺了

RTSP協議,這個寫的不錯,賺了

1. RTSP連線的建立過程

      RTSPServer類用於構建一個RTSP伺服器,該類同時在其內部定義了一個RTSPClientSession類,用於處理單獨的客戶會話。

      首先建立RTSP伺服器(具體實現類是DynamicRTSPServer),在建立過程中,先建立Socket(ourSocket)在TCP的554埠進行監聽,然後把連線處理函式控制代碼(RTSPServer::incomingConnectionHandler)和socket控制代碼傳給任務排程器(taskScheduler)。

      任務排程器把socket控制代碼放入後面select呼叫中用到的socket控制代碼集(fReadSet)中,同時將socket控制代碼和incomingConnectionHandler控制代碼關聯起來。接著,主程式開始進入任務排程器的主迴圈(doEventLoop

),在主迴圈中呼叫系統函式select阻塞,等待網路連線。

      當RTSP客戶端輸入(rtsp://192.168.0.1/1.mpg)連線伺服器時,select返回對應的socket,進而根據前面儲存的對應關係,可找到對應處理函式控制代碼,這裡就是前面提到的incomingConnectionHandler了。在incomingConnectionHandler中建立了RTSPClientSession,開始對這個客戶端的會話進行處理。


2. DESCRIBE請求訊息處理過程

      RTSP伺服器收到客戶端的DESCRIBE請求後,根據請求URL(rtsp://192.168.0.1/1.mpg),找到對應的流媒體資源,返回響應訊息。live555中的ServerMediaSession

類用來處理會話中描述,它包含多個(音訊或視訊)的子會話描述(ServerMediaSubsession)。

      RTSP伺服器收到客戶端的連線請求,建立了RTSPClientSession類,處理單獨的客戶會話。在建立RTSPClientSession的過程中,將新建立的socket控制代碼(clientSocket)和RTSP請求處理函式控制代碼RTSPClientSession::incomingRequestHandler傳給任務排程器,由任務排程器對兩者進行一對一關聯

      當客戶端發出RTSP請求後,伺服器主迴圈中的select呼叫返回,根據socket控制代碼找到對應的incomingRequestHandler,開始訊息處理。先進行訊息的解析

,如果發現請求是DESCRIBE則進入handleCmd_DESCRIBE函式。根據客戶端請求URL的字尾(如1.mpg),呼叫成員函式DynamicRTSPServer::lookupServerMediaSession查詢對應的流媒體資訊ServerMediaSession。如果ServerMediaSession不存在,但是本地存在1.mpg檔案,則建立一個新的ServerMediaSession。在建立ServerMediaSession過程中,根據檔案字尾.mpg,建立媒體MPEG-1or2的解複用器(MPEG1or2FileServerDemux)。再由MPEG1or2FileServerDemux建立一個子會話描述MPEG1or2DemuxedServerMediaSubsession。最後由ServerMediaSession完成組裝響應訊息中的SDP資訊(SDP組裝過程見下面的描述),然後將響應訊息發給客戶端,完成一次訊息互動。

SDP訊息組裝過程:

      ServerMediaSession負責產生會話公共描述資訊,子會話描述由MPEG1or2DemuxedServerMediaSubsession產生。 MPEG1or2DemuxedServerMediaSubsession在其父類成員函式OnDemandServerMediaSubsession::sdpLines()中生成會話描述資訊。在sdpLines()實現裡面,建立一個虛構(dummy)的FramedSource(具體實現類為MPEG1or2AudioStreamFramer和MPEG1or2VideoStreamFramer)和RTPSink(具體實現類為MPEG1or2AudioRTPSink和MPEG1or2VideoRTPSink),最後呼叫setSDPLinesFromRTPSink(...)成員函式生成子會話描述。

Live555庫是一個使用開放標準協議如RTP/RTCP、RTSP、SIP等實現多媒體流式傳輸的開源C 庫集。這些函式庫可以在Unix、Windows、QNX等作業系統下編譯使用,基於此建立RTSP/SIP伺服器和客戶端來實現多媒體流的傳輸。下面給出具體實現過程[4]

(1)客戶端發起RTSP OPTION請求,目的是得到伺服器提供什麼方法。RTSP提供的方法一般包括OPTIONS、DESCRIBE、SETUP、TEARDOWN、PLAY、PAUSE、SCALE、GET_PARAMETER。

(2)伺服器對RTSP OPTION迴應,伺服器實現什麼方法就回應哪些方法。在此係統中,我們只對DESCRIBE、SETUP、TEARDOWN、PLAY、PAUSE方法做了實現。

(3)客戶端發起RTSP DESCRIBE請求,伺服器收到的資訊主要有媒體的名字,解碼型別,視訊解析度等描述,目的是為了從伺服器那裡得到會話描述資訊(SDP)。

(4)伺服器對RTSP DESCRIBE響應,傳送必要的媒體引數,在傳輸H.264檔案時,主要包括SPS/PPS、媒體名、傳輸協議等資訊。

(5)客戶端發起RTSP SETUP請求,目的是請求會話建立並準備傳輸。請求資訊主要包括傳輸協議和客戶端埠號。

(6)伺服器對RTSP SETUP響應,發出相應伺服器端的埠號和會話識別符號。

(7)客戶端發出了RTSP PLAY的請求,目的是請求播放視訊流。

(8)伺服器對RTSP PLAY響應,響應的訊息包括會話識別符號,RTP包的序列號,時間戳。此時伺服器對H264視訊流封裝打包進行傳輸。

(9)客戶端發出RTSP TEARDOWN請求,目的是關閉連線,終止傳輸。

(10)伺服器關閉連線,停止傳輸。


3. SETUP請求訊息處理過程

        RTSPClientSession類用於處理單獨的客戶會話。其類成員函式handleCmd_SETUP()處理客戶端的SETUP請求。呼叫parseTransportHeader()對SETUP請求的傳輸頭解析,呼叫子會話(這裡具體實現類為OnDemandServerMediaSubsession)的getStreamParameters()函式獲取流媒體傳送傳輸引數。將這些引數組裝成響應訊息,返回給客戶端。

        獲取傳送傳輸引數的過程:呼叫子會話(具體實現類MPEG1or2DemuxedServerMediaSubsession)的createNewStreamSource(...)建立MPEG1or2VideoStreamFramer,選擇傳送傳輸引數,並呼叫子會話的createNewRTPSink(...)建立MPEG1or2VideoRTPSink。同時將這些資訊儲存在StreamState類物件中,用於記錄流的狀態。

        客戶端傳送兩個SETUP請求,分別用於建立音訊和視訊的RTP接收。

4. PLAY請求訊息處理過程

      RTSPClientSession類成員函式handleCmd_PLAY()處理客戶端的播放請求。首先呼叫子會話的startStream(),內部呼叫MediaSink::startPlaying(...),然後是MultiFramedRTPSink::continuePlaying(),接著呼叫MultiFramedRTPSink::buildAndSendPacket(...)。buildAndSendPacke內部先設定RTP包頭,內部再呼叫MultiFramedRTPSink::packFrame()填充編碼幀資料。

      packFrame內部通過FramedSource::getNextFrame(), 接著MPEGVideoStreamFramer::doGetNextFrame(),再接著經過MPEGVideoStreamFramer::continueReadProcessing(), FramedSource::afterGetting(...), MultiFramedRTPSink::afterGettingFrame(...), MultiFramedRTPSink::afterGettingFrame1(...)等一系列繁瑣呼叫,最後到了MultiFramedRTPSink::sendPacketIfNecessary(), 這裡才真正傳送RTP資料包。然後是計算下一個資料包傳送時間,把MultiFramedRTPSink::sendNext(...)函式控制代碼傳給任務排程器,作為一個延時事件排程。在主迴圈中,當MultiFramedRTPSink::sendNext()被排程時,又開始呼叫MultiFramedRTPSink::buildAndSendPacket(...)開始新的傳送資料過程,這樣客戶端可以源源不斷的收到伺服器傳來的RTP包了。

傳送RTP資料包的間隔計算方法:

        Update the time at which the next packet should be sent, based on the duration of the frame that we just packed into it.