1. 程式人生 > >RTSP伺服器處理客戶端點播的基本流程

RTSP伺服器處理客戶端點播的基本流程

處理連線請求的基本流程:

l  Step 1:與客戶端建立RTSP連線(呼叫incomingConnectionHandler方法),建立ClientSession並關聯fClientSocket與incomingRequestHandler(呼叫incomingConnectionHandler1)。

l  Step 2:接收客戶端請求(呼叫incomingRequestHandler方法)。

l  Step 3:從客戶端Socket讀取資料,並對請求資料(即the request string)進行轉換(呼叫parseRTSPRequestString方法,該方法在RTSPCommon類中)。

l  Step 4:根據分離出來的指令進行分別處理:

n  OPTIONS→handleCmd_OPTIONS 

n  DESCRIBE→handleCmd_DESCRIBE

handleCmd_DESCRIBE這一個方法比較重要,首先根據urlSuffix查詢ServerMediaSession是否存在(呼叫lookupServerMediaSession方法,該方法中通過HashTable來查詢)。

在testOnDemandRTSPServer專案工程中,僅僅是通過streamName來確認session是否為NULL。而在完整的live555MediaServer專案工程中,則是通過DynamicRTSPServer類來處理,其首先是查詢檔案是否存在,若檔案不存在,則判斷ServerMediaSession(即smsExists)是否存在,如果存在則將其remove(呼叫removeServerMediaSession方法);若檔案存在,則根據檔名建立一個ServerMediaSession(呼叫createNewSMS方法,若在該方法中找不到對應的副檔名,則將返回NULL)。

如果通過lookupServerMediaSession返回的是NULL,則向客戶端傳送響應訊息並將fSessionIsActive置為FALSE;否則,為該session組裝一個SDP描述資訊(呼叫generateSDPDescription方法,該方法在ServerMediaSession類中),組裝完成後,生成一個RTSP URL(呼叫rtspURL方法,該方法在RTSPServer類中)。

n  SETUP→handleCmd_SETUP

handleCmd_SETUP方法中,有兩個關鍵的名詞,一個是urlPreSuffix,代表了session name(即stream name);一個是urlSuffix,代表了subsession name(即track name),後面經常用到的streamName和trackId分別與這兩個名詞有關。

接下來會建立session's state,包括incrementReferenceCount等。緊接著,會針對確定的subsession(track)查詢相應的資訊。接著,在request string查詢一個"Transport:" header,目的是為了從中提取客戶端請求的一些引數(呼叫parseTransportHeader方法,該方法在RTSPServer類中),如clientsDestinationAddressStr、ClientRTPPortNum等。

再接著是getStreamParameters(該方法在ServerMediaSession類中被定義為純虛擬函式並在OnDemandServerMediaSubsession類中被重定義),然後通過fIsMulticast和streamingMode來組裝不同的響應訊息。

n  PLAY→handleCmd_PLAY:處理播放請求,具體的實現流程請參見後面的步驟。

n  PAUSE→handleCmd_PAUSE:處理暫停請求,在執行了該請求後,最終會呼叫StopPlaying方法,並將fAreCurrentlyPlaying置為FALSE。

n  TEARDOWN→handleCmd_TEARDOWN:處理停止請求,將fSessionIsActive置為FALSE。

n  GET_PARAMETER→handleCmd_GET_PARAMETER:該方法主要是維持客戶端與伺服器通訊的生存狀態,just for keep alive。

n  SET_PARAMETER→handleCmd_SET_PARAMETER:該方法未針對SET_PARAMETER作實現,使用該方法會呼叫handleCmd_notSupported方法,並將最終引發與客戶端斷開連線。

l  Step 5:根據Step 4的不同指令進行訊息響應(呼叫send方法),該訊息響應是即時的。

l  Step 6:處理客戶端傳送“SETUP”指令後即開始播放的特殊情況。

l  Step 7:將RequestBuffer進行重置,以便於為之後到來的請求做好準備。

l  Step 8:檢查fSessionIsActive是否為FALSE,如果是則刪除當前的ClientSession。

  處理PLAY的基本流程:

l  Step 1:對rtspURL及相關header的處理,涉及較多的細節。

l  Step 2:根據不同的header對流進行縮放比例或定位的處理。

如果為sawScaleHeader,則進行縮放比例的處理(呼叫setStreamScale方法,該方法在OnDemandServerMediaSubsession類中實現)。

如果為sawRangeHeader,則進行尋找流的處理(即是對流進行定位,呼叫seekStream方法,該方法在OnDemandServerMediaSubsession類中實現;同時,該方法的呼叫是在初始播放前及播放過程中由於使用者拖動播放進度條而產生的系列請求)。

在OnDemandServerMediaSubsession類中,seekStream方法中呼叫了seekStreamSource方法,該方法在對應的媒體格式檔案的FileServerMediaSubsession類中實現(如針對WAV格式,則在WAVAudioFileServerMediaSubsession類中實現;針對MP3格式,則在MP3AudioFileServerMediaSubsession類中實現)。

同理,OnDemandServerMediaSubsession類中的setStreamScale方法中所呼叫的setStreamSourceScale方法亦是類似的實現機制。

l  Step 3:開始進行流式播放(呼叫startStream方法,該方法在OnDemandServerMediaSubsession類中實現)。

n  Step 3.1:根據clientSessionId從fDestinationsHashTable中查詢到destinations(包括了客戶端的IP地址、RTP埠號、RTCP埠號等資訊)。

n  Step 3.2:呼叫startPlaying方法,在該方法中根據RTPSink或UDPSink分別呼叫startPlaying方法。

如果是呼叫RTPSink的startPlaying方法,則接著會呼叫MediaSink類中的startPlaying方法,並在該方法中呼叫MultiFramedRTPSink類中的continuePlaying方法,之後便是buildAndSendPacket了。這裡已經來到重點了,即是關於不斷讀取Frame並Send的要點。在MultiFramedRTPSink類中,通過buildAndSendPacket、packFrame、afterGettingFrame、afterGettingFrame1、sendPacketIfNecessary和sendNext構成了一個迴圈圈,資料包的讀取和傳送在這裡迴圈進行著。特別注意的是sendPacketIfNecessary方法中的後面程式碼(nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);),通過Delay amount of time後,繼續下一個Task,並回過來繼續呼叫buildAndSendPacket方法。

在packFrame方法中,正常情況下,需要呼叫getNextFrame方法(該方法在FramedSource類中,並且對不同媒體格式的Frame的獲取出現在FramedSource類的getNextFrame方法中,通過呼叫doGetNextFrame方法來實現)來獲取新的Frame。

如果是呼叫UDPSink的startPlaying方法,則接著會呼叫MediaSink類中的startPlaying方法,並在該方法中呼叫BasicUDPSink類中的continuePlaying方法。在這之後由若干個方法構成了一個迴圈圈:continuePlaying1、afterGettingFrame、afterGettingFrame1、sendNext。並在afterGettingFrame1方法中實現了packet的傳送(fGS->output(envir(), fGS->ttl(), fOutputBuffer, frameSize);)。

Step 3.3:針對RTPSink建立RTCP instance(RTP與RTCP的配套使用決定了其必須這麼做,否則可能就跟直接使用UDP傳送資料包沒什麼兩樣了^_^),建立RTCP instance時,將incomingReportHandler控制代碼作為BackgroundHandlerProc,以便於處理RTCP的報告,並開始startNetworkReading。這裡RTP/RTCP的使用方式有兩種,一種建立在TCP之上,一種建立在UDP之上。