開源項目live555學習心得

最近因項目開發需要,開始學習開源項目live555,特別將個人的一些學習心得做一下記錄,如有理解不正確之處,歡迎各位朋友指出。

首先是源碼的下載,這可以從http://www.live555.com上下載,同時上面亦提供了相關的文檔,文檔雖較爲粗糙,但總比找不到任何文檔說明強。當然,我在學習的過程中,也從網上查找了一段時間,收穫還是有一點的,其中就有諸如:RTSP服務器實例live555源代碼分析、live555源代碼簡介、live555代碼解讀系列、基於live555的rtp-rtcp研究等文章。

我的學習是在VS2008環境下進行的,要能夠在VS環境下順利進行,還得做相關工作。live開源雖說是用C++寫的,代碼風格也非常優秀,但是其是用makefile文件來做的,而對未接觸過makefile文件的我是一個問題。幸運的是在網上偶然發現了一篇介紹在VC6環境下編譯live的文章,然後照着上面所說的做,磕磕碰碰,編譯成功了四個庫並轉移到了VS2008環境中。更幸運的是又是偶然在網上發現了一篇介紹用VS2008編譯live的文章並提供了編譯後的工程下載,download,然後就開始了相對漫長的學習過程。

從程序的結構來看,live項目包括了四個基本庫、程序入口類(在mediaServer中)和一些測試代碼(在testProgs中)。四個基本庫是UsageEnvironment、BasicUsageEnvironment、groupsock和liveMedia。

UsageEnvironment包括抽象類UsageEnvironment和抽象類TaskScheduler,這兩個類用於事件調度,其中包括實現了對事件的異步讀取、對事件句柄的設置及對錯誤信息的輸出等;該庫中還有一個HashTable,這是一個通用的HashTable,在整個項目中都可以使用它,當然該HashTable也是一個抽象類。

BasicUsageEnvironment中的類主要是對UsageEnvironment中對應類的實現。

groupsock,顧名思義,用於數據包的接收和發送,其同時支持多播和單播。groupsock庫中包括了GroupEId、Groupsock、GroupsockHelper、NetAddress、NetInterface等類,其中Groupsock類有兩個構造函數,一個是“for a source-independent multicast group”,另一個是“for a source-specific multicast group”;而GroupsockHelper類主要用於讀寫Socket。

liveMedia是很重要的一個庫,其不僅包含了實現RTSP Server的類,還包含了針對不同流媒體類型(如TS流、PS流等)編碼的類。在該庫中,基類是Medium,層次關係非常清晰。在該庫中,有幾個很重要的類,如RTSPServer、ServerMediaSession、RTPSink、RTPInterface、FramedSource等。

http://www.live555.com上的相關文檔中提到穿透防火牆的問題,方法是開啓一個HTTP的tunnel,然後我們可以在liveMedia庫中找到一個RTSPOverHTTPServer的類,該類解決了這樣的問題。

mediaServer下的live555MediaServer提供了main函數,DynamicRTSPServer繼承了RTSPServer並重寫了虛函數lookupServerMediaSession

前面已經講到,通過不斷地嘗試(其實要在XP SP3環境下使用VS2008編譯成功還是挺費神的),總算把源代碼編譯成功,同時又參考了新下載的一個用VS2008編譯通過的live555源代碼。結合這些,開始對主要類結構進行初步分析。

鑑於UsageEnvironment庫、BasicUsageEnvironment庫和groupsock庫中的類較少,就暫且不作分析了。這裏主要針對liveMedia庫中的主要類結構進行分析。通過查看類關係圖,可以從整體把握,但是苦於類太多,用類關係圖看起來也不方便,於是就自己重新整理了一下,下面是 liveMedia庫的主要類結構。(注:其他單類及結構體等不在此列出)

l  Medium

n  RTSPServer

n  RTSPOverHTTPServer

n  MediaSession

n  ServerMediaSession

n  ServerMediaSubsession

u  OnDemandServerMediaSubsession

l  FileServerMediaSubsession

n  ADTSAudioFileServerMediaSubsession

n  AMRAudioFileServerMediaSubsession

n  H263plusVideoFileServerMediaSubsession

n  MP3AudioFileServerMediaSubsession

n  MPEG1or2VideoFileServerMediaSubsession

n  MPEG2TransportFileServerMediaSubsession

n  MPEG4VideoFileServerMediaSubsession

n  WAVAudioFileServerMediaSubsession

l  MPEG1or2DemuxedServerMediaSubsession

u  PassiveServerMediaSubsession

n  MediaSource

u  FramedSource

l  FramedFileSource

n  ByteStreamFileSource

n  ADTSAudioFileSource

n  MP3FileSource

u  MP3HTTPSource

l  BasicUDPSource

l  RTPSource

n  MultiFramedRTPSource

u  RawQCELPRTPSource

u  AC3AudioRTPSource

u  MPEG4GenericRTPSource

u  RawAMRRTPSource

u  H261VideoRTPSource

u  H263plusVideoRTPSource

u  H264VideoRTPSource

u  JPEGVideoRTPSource

u  MP3ADURTPSource

u  MPEG1or2AudioRTPSource

u  MPEG1or2VideoRTPSource

u  MPEG4ESVideoRTPSource

u  MPEG4GenericRTPSource

u  MPEG4LATMAudioRTPSource

u  DVVideoRTPSource

u  QuickTimeGenericRTPSource

u  SimpleRTPSource

l  AMRAudioSource

n  AMRDeinterleaver

n  AMRAudioFileSource

l  ByteStreamMultiFileSource

l  DeviceSource

l  JPEGVideoSource

l  MPEG1or2DemuxedElementaryStream

l  MPEG2TransportStreamMultiplexor

n  MPEG2TransportStreamFromESSource

n  MPEG2TransportStreamFromPESSource

l  AudioInputDevice

n  WAVAudioFileSource

l  FramedFilter

n  H264FUAFragmenter

n  QCELPDeinterleaver

n  AC3AudioStreamFramer

n  ADUFromMP3Source

n  uLawFromPCMAudioSource

n  H264VideoStreamFramer

n  MP3FromADUSource

u  MP3Transcoder

n  PCMFromuLawAudioSource

n  MPEG2IFrameIndexFromTransportStream

n  NetworkFromHostOrder16

n  HostFromNetworkOrder16

n  MP3ADUinterleaverBase

u  MP3ADUinterleaver

u  MP3ADUdeinterleaver

n  MPEG2TransportStreamFramer

n  EndianSwap16

n  H263plusVideoStreamFramer

n  MPEGVideoStreamFramer

u  MPEG1or2VideoStreamFramer

l  MPEG1or2VideoStreamDiscreteFramer

u  MPEG4VideoStreamFramer

l  MPEG4VideoStreamDiscreteFramer

n  MPEG1or2AudioStreamFramer

n  DVVideoStreamFramer

n  MP3ADUTranscoder

n  MPEG2TransportStreamTrickModeFilter

n  MediaSink

u  DummySink

u  BasicUDPSink

u  RTPSink

l  MultiFramedRTPSink

n  MPEG4GenericRTPSink

n  VideoRTPSink

u  H264VideoRTPSink

u  MPEG1or2VideoRTPSink

u  H263plusVideoRTPSink

u  JPEGVideoRTPSink

u  DVVideoRTPSink

u  MPEG4ESVideoRTPSink

n  AudioRTPSink

u  AC3AudioRTPSink

u  MPEG4LATMAudioRTPSink

u  GSMAudioRTPSink

u  MPEG1or2AudioRTPSink

u  AMRAudioRTPSink

u  MP3ADURTPSink

n  SimpleRTPSink

u  HTTPSink

l  MPEG1or2VideoHTTPSink

u  FileSink

l  AMRAudioFileSink

l  H264VideoFileSink

n  RTCPInstance

n  RTSPClient

n  SIPClient

n  DarwinInjector

n  QuickTimeFileSink

n  MPEG1or2Demux

n  MPEG2TransportStreamIndexFile

n  MPEG1or2FileServerDemux

n  AVIFileSink

 

l  BufferedPacketFactory

n  QCELPBufferedPacketFactory

n  AMRBufferedPacketFactory

n  MPEG4GenericBufferedPacketFactory

n  ADUBufferedPacketFactory

n  QTGenericBufferedPacketFactory

n  LATMBufferedPacketFactory

n  H264BufferedPacketFactory

n  JPEGBufferedPacketFactory

 

l  BufferedPacket

n  QCELPBufferedPacket

n  AMRBufferedPacket

n  MPEG4GenericBufferedPacket

n  ADUBufferedPacket

n  QTGenericBufferedPacket

n  LATMBufferedPacket

n  H264BufferedPacket

n  JPEGBufferedPacket

 

l  StreamParser

n  AC3AudioStreamParser

n  MPEGVideoStreamParser

u  MPEG1or2VideoStreamParser

u  MPEG4VideoStreamParser

n  MPEG1or2AudioStreamParser

n  H263plusVideoStreamParser

n  MPEGProgramStreamParser

 

從上面這個主要的類結構可以看出,liveMedia庫中的基類爲Medium,其下又有幾個非常重要的部分,一個是×××Subsession,除Medium父類外,所有的×××Subsession類都繼承於ServerMediaSubsession;一個是×××SourceMediaSourceframeSource下主要包含FramedFileSourceRTPSourceFramedFilter等幾個主要的部分;一個是MediaSink,以繼承於RTPSink的類居多。

此外,還包含了用於處理packetBufferedPacketFactoryBufferedPacket及其相關子類,用於處理流分析的StreamParser及其子類。

基本上,整個liveMedia庫的主要類結構就是這樣。不過,類太多了,分析起來還是有較大的困難。於是乎,採取去掉枝葉保留主幹的做法,將整個服務器精簡成支持一種格式的服務器,如MP3流服務器。經過分離,一個基於MP3的測試服務器的主要類結構如下所示。(注:其他單類及結構體等不在此列出)

l  Medium

n  RTSPServer

n  MediaSession

n  ServerMediaSession

n  ServerMediaSubsession

u  OnDemandServerMediaSubsession

l  FileServerMediaSubsession

n  MP3AudioFileServerMediaSubsession

n  MediaSource

u  FramedSource

l  FramedFileSource

n  MP3FileSource

u  MP3HTTPSource

l  BasicUDPSource

l  RTPSource

n  MultiFramedRTPSource

u  MP3ADURTPSource

u  MPEG1or2AudioRTPSource

u  SimpleRTPSource

l  FramedFilter

n  ADUFromMP3Source

n  MP3FromADUSource

u  MP3Transcoder

n  MP3ADUinterleaverBase

u  MP3ADUinterleaver

u  MP3ADUdeinterleaver

n  MP3ADUTranscoder

n  MediaSink

u  BasicUDPSink

u  RTPSink

l  MultiFramedRTPSink

n  AudioRTPSink

u  MPEG1or2AudioRTPSink

u  MP3ADURTPSink

n  RTCPInstance

 

l  BufferedPacketFactory

n  ADUBufferedPacketFactory

 

l  BufferedPacket

n  ADUBufferedPacket

 

根據上面這種相對簡單的類結構,分析起來就方便多了。於是,開始進入具體的分析細節了,這是一個相對漫長的過程,再加上本人的C++水平有限,這又將是一個相對艱苦的過程,一切慢慢來吧……

末了,講講啓動服務器的過程:

LIVE555是一個純粹的RTSP服務器,其服務器主類爲liveMedia庫下的RTSPServermediaServer下的live555MediaServer爲主程序的入口類,DynamicRTSPServerRTSPServer的實現類。

live555MediaServer類的入口函數main中可以非常清晰地分析出服務器的啓動過程。

首先是createNew一個TaskSchedulers對象和一個UsageEnvironment對象,這是初始工作。

之後是一段訪問控制的代碼。然後開始進入創建RTSP服務器的代碼段,服務器指定了一個默認端口554和一個可供替代的端口8554

接下來,createNew一個DynamicRTSPServer,這裏建立了Socket(ourSocket)TCP554端口(默認端口)進行監聽,然後把連接處理函數句柄和socket句柄傳給任務調度器(taskScheduler),既是RTSPServer類中的這句代碼:env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket,        (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,this)。緊接着就是對socket句柄和incomingConnectionHandler句柄的處理,主要是進行關聯等。

最後,進入主循環(即env->taskScheduler().doEventLoop();),等待客戶端連接。服務器啓動完畢。

 

    文章的最後,需要說明的是,在編譯運行的過程中,我是使用VLC播放器來進行測試的,同時通過使用Ethereal的網絡分析工具抓包分析其建立到傳輸的過程,雖然在live555源代碼中關於RTSP建立及收發數據包的過程已經用代碼寫得非常清楚(這個好好分析一下源碼就可以了),但我想,學習使用一下一些網絡分析工具對自身也是頗爲有益的。

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

l  Step 1與客戶端建立RTSP連接(調用incomingConnectionHandler方法),創建ClientSession並關聯fClientSocketincomingRequestHandler(調用incomingConnectionHandler1

 

l  Step 2接收客戶端請求(調用incomingRequestHandler方法)。

 

l  Step 3從客戶端Socket讀取數據,並對請求數據(即the request string)進行轉換(調用parseRTSPRequestString方法,該方法在RTSPCommon類中)。

 

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

n  OPTIONShandleCmd_OPTIONS 

n  DESCRIBEhandleCmd_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  SETUPhandleCmd_SETUP

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

接下來會創建session's state,包括incrementReferenceCount等。緊接着,會針對確定的subsessiontrack)查找相應的信息。接着,在request string查找一個"Transport:" header,目的是爲了從中提取客戶端請求的一些參數(調用parseTransportHeader方法,該方法在RTSPServer類中),如clientsDestinationAddressStrClientRTPPortNum等。

再接着是getStreamParameters(該方法在ServerMediaSession類中被定義爲純虛函數並在OnDemandServerMediaSubsession類中被重定義),然後通過fIsMulticaststreamingMode來組裝不同的響應消息。

 

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

 

n  PAUSEhandleCmd_PAUSE處理暫停請求,在執行了該請求後,最終會調用StopPlaying方法,並將fAreCurrentlyPlaying置爲FALSE

 

n  TEARDOWNhandleCmd_TEARDOWN處理停止請求,將fSessionIsActive置爲FALSE

 

n  GET_PARAMETERhandleCmd_GET_PARAMETER該方法主要是維持客戶端與服務器通信的生存狀態,just for keep alive

 

n  SET_PARAMETERhandleCmd_SET_PARAMETER該方法未針對SET_PARAMETER作實現,使用該方法會調用handleCmd_notSupported方法,並將最終引發與客戶端斷開連接。

 

l  Step 5根據Step 4的不同指令進行消息響應(調用send方法),該消息響應是即時的。

 

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

 

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

 

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

 

 

*  處理PLAY的基本流程:

l  Step 1rtspURL及相關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根據clientSessionIdfDestinationsHashTable中查找到destinations(包括了客戶端的IP地址、RTP端口號、RTCP端口號等信息)。

 

n  Step 3.2調用startPlaying方法,在該方法中根據RTPSinkUDPSink分別調用startPlaying方法。

如果是調用RTPSinkstartPlaying方法,則接着會調用MediaSink類中的startPlaying方法,並在該方法中調用MultiFramedRTPSink類中的continuePlaying方法,之後便是buildAndSendPacket了。這裏已經來到重點了,即是關於不斷讀取FrameSend的要點。在MultiFramedRTPSink類中,通過buildAndSendPacketpackFrameafterGettingFrameafterGettingFrame1sendPacketIfNecessarysendNext構成了一個循環圈,數據包的讀取和發送在這裏循環進行着。特別注意的是sendPacketIfNecessary方法中的後面代碼(nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);),通過Delay amount of time後,繼續下一個Task,並回過來繼續調用buildAndSendPacket方法。

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

如果是調用UDPSinkstartPlaying方法,則接着會調用MediaSink類中的startPlaying方法,並在該方法中調用BasicUDPSink類中的continuePlaying方法。在這之後由若干個方法構成了一個循環圈:continuePlaying1afterGettingFrameafterGettingFrame1sendNext。並在afterGettingFrame1方法中實現了packet的發送(fGS->output(envir(), fGS->ttl(), fOutputBuffer, frameSize);)。

 

Step 3.3針對RTPSink創建RTCP instanceRTPRTCP的配套使用決定了其必須這麼做,否則可能就跟直接使用UDP發送數據包沒什麼兩樣了^_^),創建RTCP instance時,將incomingReportHandler句柄作爲BackgroundHandlerProc,以便於處理RTCP的報告,並開始startNetworkReading。這裏RTP/RTCP的使用方式有兩種,一種建立在TCP之上,一種建立在UDP之上。

http://blog.csdn.net/huangxinfeng/archive/2010/03/11/5369391.aspx

http://blog.csdn.net/huangxinfeng/archive/2010/03/12/5374721.aspx

http://blog.csdn.net/huangxinfeng/archive/2010/03/22/5404233.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章