天天看點

live555源代碼分析

各種測試代碼在testProgram 目錄下,比如openRTSP 等,這些代碼有助于了解liveMedia 的應用。

Media Server 是一個純粹的RTSP 伺服器。支援多種格式的媒體檔案:

* TS 流檔案,擴充名ts。 * PS 流檔案,擴充名mpg。 * MPEG-4視訊基本流檔案,擴充名m4e。 * MP3檔案,擴充名mp3。 * WAV 檔案(PCM),擴充名wav。 * AMR 音頻檔案,擴充名.amr。 * AAC 檔案,ADTS 格式,擴充名aac。

基于liveMedia 的程式,需要通過繼承UsageEnvironment 抽象類和TaskScheduler 抽象類,定義相應的類來處理事件排程,資料讀寫以及錯誤處理。live 項目的源代碼裡有這些類的一個基本實作,這就是“BasicUsageEnvironment”庫。BasicUsageEnvironment 主要是針對簡單的控制台應用程式,利用select 實作事件擷取和處理。這個庫利用Unix 或者Windows 的控制台作為輸入輸出,處于應用程式原形或者調試的目的,可以用這個庫使用者可以開發傳統的運作與控制台的應用。

通過使用自定義的“UsageEnvironment”和“TaskScheduler”抽象類的子類,這些應用程式就可以在特定的環境中運作, 不需要做過多的修改。需要指出的是在圖形環境(GUI toolkit)下,抽象類TaskScheduler 的子類在實作doEventLoop()的時候應該與圖形環境自己的事件處理架構內建。

先來熟悉在liveMedia 庫中Source,Sink 以及Filter 等概念。Sink 就是消費資料的對象,比如把接收到的資料存儲到檔案, 這個檔案就是一個Sink。Source 就是生産資料的對象,比如通過RTP 讀取資料。資料流經過多個'source'和'sink's,下面是一個示例:

'source1' -> 'source2' (a filter) -> 'source3' (a filter) -> 'sink'

從其它Source 接收資料的source 也叫做"filters"。Module 是一個sink 或者一個filter。資料接收的終點是Sink 類,MediaSink 是所有Sink 類的基類。Sink 類實作對資料的處理是通過實作純虛函數continuePlaying(),通常情況下continuePlaying 調用fSource->getNextFrame 來為Source 設定資料緩沖區,處理資料的回調函數等,fSource是MediaSink 的類型為FramedSource*的類成員。

基于liveMedia 的應用程式的控制流程如下:

應用程式是事件驅動的,使用如下方式的循環

while (1) {  

通過查找讀網絡句柄的清單和延遲隊列(delay queue)來發現需要完成的任務  

完成這個任務  

}  

對于每個sink,在進入這個循環之前,應用程式通常調用下面的方法來啟動需要做的生成任務: someSinkObject->startPlaying()。任何時候,一個Module 需要擷取資料都通過調用剛好在它之前的那個Module 的FramedSource::getNextFrame() 方法。這是通過純虛函數FramedSource::doGetNextFrame() 實作的,每一個Source module 都有相應的實作。

Each 'source' module's implementation of "doGetNextFrame()" works by arranging for an 'after getting' function to be called (from an event handler) when new data becomes available for the caller.

Note that the flow of data from 'sources' to 'sinks' happens within each application, and doesn't necessarily correspond to the sending or receiving of network packets. For example, a server application (such as "testMP3Streamer") that sends RTP packets will do so using one or more "RTPSink" modules. These "RTPSink" modules receive data from other, "*Source" modules (e.g., to read data from a file), and, as a side effect, transmit RTP packets.

live555代碼解讀之一: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.1.109/1.mpg)連接配接伺服器時,select 傳回對應的scoket,進而根據前面儲存的對應關系, 可找到對應處理函數句柄, 這裡就是前面提到的incomingConnectionHandler 了。在incomingConnectionHandler 中建立了RTSPClientSession,開始對這個用戶端的會話進行處理。

live555代碼解讀之二:DESCRIBE 請求消息處理過程

RTSP 伺服器收到用戶端的DESCRIBE 請求後,根據請求URL(rtsp://192.168.1.109/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(...)成員函數生成子會話描述。

以上涉及到的類以及繼承關系:

Medium <- ServerMediaSession Medium <- ServerMediaSubsession <- OnDemandServerMediaSubsession <-MPEG1or2DemuxedServerMediaSubsession Medium <- MediaSource <- FramedSouse <- FramedFileSource <- ByteStreamFileSource Medium <- MediaSource <- FramedSouse <- MPEG1or2DemuxedElementaryStream Medium <- MPEG1or2FileServerDemux Medium <- MPEG1or2Demux Medium <- MediaSource <- FramedSouse <- FramedFilter <- MPEGVideoStreamFramer <-MPEG1or2VideoStreamFramer Medium <- MediaSink <- RTPSink <- MultiFramedRTPSink <- VideoRTPSink <-MPEG1or2VideoRTPSink

live555代碼解讀之三:SETUP 和PLAY 請求消息處理過程

前面已經提到RTSPClientSession 類, 用于處理單獨的客戶會話。其類成員函數handleCmd_SETUP()處理用戶端的SETUP 請求。調用parseTransportHeader()對SETUP 請求的傳輸頭解析,調用子會話(這裡具體實作類為OnDemandServerMediaSubsession)的getStreamParameters()函數擷取流媒體發送傳輸參數。将這些參數組裝成響應消息,傳回給用戶端。

擷取發送傳輸參數的過程:

調用子會話( 具體實作類MPEG1or2DemuxedServerMediaSubsession) 的createNewStreamSource(...) 建立MPEG1or2VideoStreamFramer,選擇發送傳輸參數,并調用子會話的createNewRTPSink(...)建立MPEG1or2VideoRTPSink。同時将這些資訊儲存在StreamState 類對象中,用于記錄流的狀态。

用戶端發送兩個SETUP 請求,分别用于建立音頻和視訊的RTP 接收。

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.

涉及到一些類有:

MPEGVideoStreamFramer: A filter that breaks up an MPEG video elementary stream into headers and frames MPEG1or2VideoStreamFramer: A filter that breaks up an MPEG 1 or 2 video elementary stream into frames for: Video_Sequence_Header, GOP_Header, Picture_Header MPEG1or2DemuxedElementaryStream: A MPEG 1 or 2 Elementary Stream, demultiplexed from a Program Stream MPEG1or2Demux: Demultiplexer for a MPEG 1 or 2 Program Stream ByteStreamFileSource: A file source that is a plain byte stream (rather than frames) MPEGProgramStreamParser: Class for parsing MPEG program stream StreamParser: Abstract class for parsing a byte stream StreamState: A class that represents the state of an ongoing stream

rtsp 簡介(ZT)

Real Time Streaming Protocol 或者RTSP(實時流媒體協定),是由Real network 和Netscape共同提出的如何有效地在IP 網絡上傳輸流媒體資料的應用層協定。RTSP 提供一種可擴充的架構,使能夠提供能控制的,按需傳輸實時資料,比如音頻和視訊檔案。源資料可以包括現場資料的回報和存貯的檔案。rtsp 對流媒體提供了諸如暫停,快進等控制,而它本身并不傳輸資料,rtsp 作用相當于流媒體伺服器的遠端控制。傳輸資料可以通過傳輸層的tcp,udp協定,rtsp 也提供了基于rtp 傳輸機制的一些有效的方法。

RTSP 消息格式:

RTSP 的消息有兩大類,一是請求消息(request),一是回應消息(response),兩種消息的格式不同.

請求消息:

方法URI RTSP 版本CR LF

消息頭CR LF CR LF

消息體CR LF

其中方法包括OPTION 回應中所有的指令,URI 是接受方的位址,例如:rtsp://192.168.20.136。

RTSP 版本一般都是RTSP/1.0.每行後面的CR LF 表示回車換行,需要接受端有相應的解析,最後一個消息頭需要有兩個CR LF

回應消息:

RTSP 版本狀态碼解釋CR LF

其中RTSP 版本一般都是RTSP/1.0,狀态碼是一個數值,200表示成功,解釋是與狀态碼對應的文本解釋。

簡單的rtsp 互動過程:

C 表示rtsp 用戶端,S 表示rtsp 服務端

1.C->S:OPTION request //詢問S 有哪些方法可用  

1.S->C:OPTION response //S 回應資訊中包括提供的所有可用方法  

2.C->S:DESCRIBE request //要求得到S 提供的媒體初始化描述資訊  

2.S->C:DESCRIBE response //S 回應媒體初始化描述資訊,主要是sdp  

3.C->S:SETUP request //設定會話的屬性,以及傳輸模式,提醒S 建立會  

話  

3.S->C:SETUP response //S 建立會話,傳回會話辨別符,以及會話相關資訊  

4.C->S:PLAY request //C 請求播放  

4.S->C:PLAY response //S 回應該請求的資訊  

S->C:發送流媒體資料  

5.C->S:TEARDOWN request //C 請求關閉會話  

5.S->C:TEARDOWN response //S 回應該請求  

上述的過程是标準的、友好的rtsp 流程,但實際的需求中并不一定按部就班來。其中第3和4步是必需的!第一步,隻要伺服器用戶端約定好,有哪些方法可用,則option 請求可以不要。第二步,如果我們有其他途徑得到媒體初始化描述資訊(比如http 請求等等),則我們也不需要通過rtsp 中的describe 請求來完成。第五步,可以根據系統需求的設計來決定是否需要。

rtsp 中常用方法:

1.OPTION

目的是得到伺服器提供的可用方法:

OPTIONS rtsp://192.168.20.136:5000/xxx666 RTSP/1.0

CSeq: 1 //每個消息都有序号來标記,第一個包通常是option 請求消息

User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)

伺服器的回應資訊包括提供的一些方法,例如:

RTSP/1.0 200 OK

Server: UServer 0.9.7_rc1

Cseq: 1 //每個回應消息的cseq 數值和請求消息的cseq 相對應

Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SCALE,GET_PARAMETER //伺服器提供的可用的方法

2.DESCRIBE

C 向S 發起DESCRIBE 請求,為了得到會話描述資訊(SDP):

DESCRIBE rtsp://192.168.20.136:5000/xxx666 RTSP/1.0

CSeq: 2

token:

Accept: application/sdp

伺服器回應一些對此會話的描述資訊(sdp):

Cseq: 2

x-prev-url: rtsp://192.168.20.136:5000

x-next-url: rtsp://192.168.20.136:5000

x-Accept-Retransmit: our-retransmit

x-Accept-Dynamic-Rate: 1

Cache-Control: must-revalidate

Last-Modified: Fri, 10 Nov 2006 12:34:38 GMT

Date: Fri, 10 Nov 2006 12:34:38 GMT

Expires: Fri, 10 Nov 2006 12:34:38 GMT

Content-Base: rtsp://192.168.20.136:5000/xxx666/

Content-Length: 344

Content-Type: application/sdp

v=0 //以下都是sdp 資訊

o=OnewaveUServerNG 1451516402 1025358037 IN IP4 192.168.20.136

s=/xxx666

u=http:///

e=admin@

c=IN IP4 0.0.0.0

t=0 0

a=isma-compliance:1,1.0,1

a=range:npt=0-

m=video 0 RTP/AVP 96 //m 表示媒體描述,下面是對會話中視訊通道的媒體描述

a=rtpmap:96 MP4V-ES/90000

a=fmtp:96

profile-level-id=245;config=000001B0F5000001B509000001000000012000C888B0E0E0FA62D

089028307

a=control:trackID=0//trackID=0表示視訊流用的是通道0

3.SETUP

用戶端提醒伺服器建立會話,并确定傳輸模式:

SETUP rtsp://192.168.20.136:5000/xxx666/trackID=0 RTSP/1.0

CSeq: 3

Transport: RTP/AVP/TCP;unicast;interleaved=0-1

//uri 中帶有trackID=0,表示對該通道進行設定。Transport 參數設定了傳輸模式,包的結構。接下來的資料標頭部第二個位元組位置就是interleaved,它的值是每個通道都不同的,trackID=0的interleaved 值有兩個0或1,0表示rtp 包,1表示rtcp 包,接受端根據interleaved 的值來差別是哪種資料包。

伺服器回應資訊:

Cseq: 3

Session: 6310936469860791894 //伺服器回應的會話辨別符

Cache-Control: no-cache

Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=6B8B4567

4.PLAY

用戶端發送播放請求:

PLAY rtsp://192.168.20.136:5000/xxx666 RTSP/1.0

CSeq: 4

Session: 6310936469860791894

Range: npt=0.000- //設定播放時間的範圍

Cseq: 4

Range: npt=0.000000-

RTP-Info: url=trackID=0;seq=17040;rtptime=1467265309

//seq 和rtptime 都是rtp 包中的資訊

5.TEARDOWN

用戶端發起關閉請求:

TEARDOWN rtsp://192.168.20.136:5000/xxx666 RTSP/1.0

CSeq: 5

伺服器回應:

Cseq: 5

Connection: Close

以上方法都是互動過程中最為常用的,其它還有一些重要的方法如get/set_parameter,pause,redirect 等等

v=<version>  

o=<username> <session id> <version> <network type> <address type> <address>  

s=<session name>  

i=<session description>  

u=<URI>  

e=<email address>  

p=<phone number>  

c=<network type> <address type> <connection address>  

b=<modifier>:<bandwidth-value>  

t=<start time> <stop time>  

r=<repeat interval> <active duration> <list of offsets from start-time>  

z=<adjustment time> <offset> <adjustment time> <offset> ....  

k=<method>  

k=<method>:<encryption key>  

a=<attribute>  

a=<attribute>:<value>  

m=<media> <port> <transport> <fmt list>  

v = (協定版本)  

o = (所有者/建立者和會話辨別符)  

s = (會話名稱)  

i = * (會話資訊)  

u = * (URI 描述)  

e = * (Email 位址)  

p = * (電話号碼)  

c = * (連接配接資訊)  

b = * (帶寬資訊)  

z = * (時間區域調整)  

k = * (加密密鑰)  

a = * (0 個或多個會話屬性行)  

時間描述:  

t = (會話活動時間)  

r = * (0或多次重複次數)  

媒體描述:  

m = (媒體名稱和傳輸位址)  

i = * (媒體标題)  

c = * (連接配接資訊— 如果包含在會話層則該字段可選)  

a = * (0 個或多個媒體屬性行)  

參考文章:rfc2326(rtsp);rfc2327(sdp)

(用戶端:VLC, RTSP 伺服器:LIVE555 Media Server)

1)C(Client)-> M(Media Server)

OPTIONS rtsp://192.168.1.109/1.mpg RTSP/1.0  

CSeq: 1  

user-Agent: VLC media player(LIVE555 Streaming Media v2007.02.20)  

1)M -> C  

RTSP/1.0 200 OK  

Date: wed, Feb 20 2008 07:13:24 GMT  

Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE  

2)C -> M

DESCRIBE rtsp://192.168.1.109/1.mpg RTSP/1.0  

CSeq: 2  

Accept: application/sdp  

User-Agent: VLC media player(LIVE555 Streaming Media v2007.02.20)  

2)M -> C

Date: wed, Feb 20 2008 07:13:25 GMT  

Content-Base: rtsp://192.168.1.109/1.mpg/  

Content-type: application/sdp  

Content-length: 447  

v=0  

o =- 2284269756 1 IN IP4 192.168.1.109  

s=MPEG-1 or 2 program Stream, streamed by the LIVE555 Media Server  

i=1.mpg  

t=0 0  

a=tool:LIVE555 Streaming Media v2008.02.08  

a=type:broadcast  

a=control:*  

a=range:npt=0-66.181  

a=x-qt-text-nam:MPEG-1 or Program Stream, streamed by the LIVE555 Media Server  

a=x-qt-text-inf:1.mpg  

m=video 0 RTP/AVP 32  

c=IN IP4 0.0.0.0  

a=control:track1  

m=audio 0 RTP/AVP 14  

a=control:track2  

3)C -> M

SETUP rtsp://192.168.1.109/1.mpg/track1 RTSP/1.0  

CSeq: 3  

Transport: RTP/AVP; unicast;client_port=1112-1113  

3)M -> C

Transport:  

RTP/AVP;unicast;destination=192.168.1.222;source=192.168.1.109;client_port=1112-1113;server  

_port=6970-6971  

Session: 3  

4)C -> M

SETUP rtsp://192.168.1.109/1.mpg/track2 RTSP/1.0  

CSeq: 4  

Transport: RTP/AVP; unicast;client_port=1114-1115  

4)M -> C

RTP/AVP;unicast;destination=192.168.1.222;source=192.168.1.109;client_port=1114-1115;server  

_port=6972-6973  

5)C -> M

PLAY rtsp://192.168.1.109/1.mpg/ RTSP/1.0  

CSeq: 5  

Range: npt=0.000-  

5)M -> C

RTP-Info:  

url=rtsp://192.168.1.109/1.mpg/track1;seq=9200;rtptime=214793785,url=rtsp://192.168.1.109/1.  

mpg/track2;seq=12770;rtptime=31721  

(開始傳輸流媒體...)