Live555學習之(二)------- testOnDemandRTSPServer

轉自:https://www.cnblogs.com/jqctop1/p/4385602.html

首先,看看這個程序的說明:

// A test program that demonstrates how to stream - via unicast RTP

// - various kinds of file on demand, using a built-in RTSP server.

  就是說這個程序演示瞭如何利用RTSPServer這個類來對媒體文件進行單播,OnDemand的意思是收到RTSP客戶端請求時才進行流化和單播。

下面,首先看main函數,很簡單,主要包含以下幾個步驟:

 1 // Begin by setting up our usage environmen
 2 // 創建工具類
 3 TaskScheduler* scheduler = BasicTaskScheduler::createNew();
 4 env = BasicUsageEnvironment::createNew(*scheduler);
 5 // Create the RTSP server:
 6 // 創建RTSPServer,指定端口爲8554
 7 RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
 8 if (rtspServer == NULL) {
 9 *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
10 exit(1);
11 }
12 
13 char const* descriptionString
14 = "Session streamed by \"testOnDemandRTSPServer\"";
15 
16 // Set up each of the possible streams that can be served by the
17 // RTSP server. Each such stream is implemented using a
18 // "ServerMediaSession" object, plus one or more
19 // "ServerMediaSubsession" objects for each audio/video substream.
20 
21 
22 /* 爲每一種媒體文件創建會話,簡單理解就是:一個ServerMediaSession對象對應一個媒體文件,一個媒體文件中可能同時包含音頻和視頻,對於每個視頻或者音頻,對應一個ServerMediaSubsession對象,
23 一個ServerMediaSession中可以包含多個ServerMediaSubsession對象 */
24 // 這裏我們只看H.264文件
25 
26 // A H.264 video elementary stream:
27 {
28   char const* streamName = "h264ESVideoTest";                   //標識請求播放該媒體文件時使用的名稱
29   char const* inputFileName = "test.264";                       //默認媒體文件名爲test.264
30   ServerMediaSession* sms = ServerMediaSession::createNew(*env, streamName, streamName,descriptionString);      //爲該媒體文件創建一個ServerMediaSession
31   /* .264文件只包含視頻,創建一個ServerMediaSubsession對象並添加到ServerMediaSession
32    H264VideoFileServerMediaSubsession是ServerMediaSubsession的子類,針對不同格式有不同的實現類 */
33   sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName, reuseFirstSource)); 
34   rtspServer->addServerMediaSession(sms);                       //將剛纔創建的ServerMediaSession添加到RTSPServer中
35   announceStream(rtspServer, sms, streamName, inputFileName);   //打印出播放該媒體文件的rtsp地址 
36 }
37 
38 // 程序從下面開始進入一個主循環,後面的return 0不會被執行。
39 env->taskScheduler().doEventLoop(); // does not return
40 return 0; // only to prevent compiler warning

Live555是單線程的,基於事件驅動模式,程序從doEventLoop函數出進入無限循環,在這個循環中不斷地查看事件隊列是否有事件需要去處理,有就去處理,沒有則休眠一小會兒,看下doEventLoop函數,該函數在live/BasicUsageEnvironment/BasicTaskScheduler0.cpp文件中定義。

1 void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
2   // Repeatedly loop, handling readble sockets and timed events:
3   while (1) {
4     if (watchVariable != NULL && *watchVariable != 0) break;
5     SingleStep();
6     //SingleStep函數中,對可讀數據的socket進行讀數據,對事件隊列中的事件調用對應的處理函數處理
7   }
8 }

主循環部分的代碼比較簡單,那我們就需要仔細看看創建RTSPServer,創建ServerMediaSession以及ServerMediaSubsession這部分的代碼,看這部分代碼之前,我們需要先對RTSP協議的建立連接過程有個大概的瞭解,在此我就不再詳述,網上有很多講解這個過程的博文,在此推薦一個:http://www.cnblogs.com/qq78292959/archive/2010/08/12/2077039.html

RTSPServer類即表示一個流媒體服務器實例,RTSPServer::createNew是一個簡單工廠函數,使用指定的端口(8554)創建一個TCP的socket用於等待客戶端的連接,然後new一個RTSPServer實例。

1 RTSPServer* RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
2               UserAuthenticationDatabase* authDatabase,
3               unsigned reclamationTestSeconds) {
4   int ourSocket = setUpOurSocket(env, ourPort);
5   if (ourSocket == -1) return NULL;
6   
7   return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
8 }

RTSPServer的構造函數:

 1 RTSPServer::RTSPServer(UsageEnvironment& env,
 2                int ourSocket, Port ourPort,
 3                UserAuthenticationDatabase* authDatabase,
 4                unsigned reclamationTestSeconds)
 5   : Medium(env),
 6     fRTSPServerPort(ourPort), fRTSPServerSocket(ourSocket), fHTTPServerSocket(-1), fHTTPServerPort(0),
 7     fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
 8     fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
 9     fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
10     fClientSessions(HashTable::create(STRING_HASH_KEYS)),
11     fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), fRegisterRequestCounter(0),
12     fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
13     fAllowStreamingRTPOverTCP(True) {
14   ignoreSigPipeOnSocket(ourSocket); // so that clients on the same host that are killed don't also kill us
15   
16   // Arrange to handle connections from others:
17   env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
18                            (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
19 }

這裏主要看一下turnOnBackgroundReadHandling函數,這個函數的作用即將某個socket加入SOCKET SET(參見select模型),並指定相應的處理函數。這裏的處理函數即收到RTSP客戶端連接請求時的回調處理函數incomingConnectionHandlerRTSP,第三個參數作爲回調函數的參數。

ServerMediaSession::createNew是一個簡單工廠模式函數,在其中new了一個ServerMediaSession對象,看一下ServerMediaSession這個類的定義。

 1 class ServerMediaSession: public Medium {
 2 public:
 3   static ServerMediaSession* createNew(UsageEnvironment& env,
 4                        char const* streamName = NULL,
 5                        char const* info = NULL,
 6                        char const* description = NULL,
 7                        Boolean isSSM = False,
 8                        char const* miscSDPLines = NULL);
 9 
10   static Boolean lookupByName(UsageEnvironment& env,
11                               char const* mediumName,
12                               ServerMediaSession*& resultSession);
13 
14   char* generateSDPDescription(); // based on the entire session          //產生媒體描述信息(SDP),在收到DESCRIBE命令後回覆給RTSP客戶端
15       // Note: The caller is responsible for freeing the returned string
16   
17   char const* streamName() const { return fStreamName; }                       // 返回流的名稱
18 
19   Boolean addSubsession(ServerMediaSubsession* subsession);             // 添加表示子會話的ServerMediaSubsession對象
20   unsigned numSubsessions() const { return fSubsessionCounter; }
21 
22   void testScaleFactor(float& scale); // sets "scale" to the actual supported scale
23   float duration() const;                                  // 返回流的持續時間
24     // a result == 0 means an unbounded session (the default)
25     // a result < 0 means: subsession durations differ; the result is -(the largest).
26     // a result > 0 means: this is the duration of a bounded session
27 
28   unsigned referenceCount() const { return fReferenceCount; }                      // 返回請求該流的RTSP客戶端數目
29   void incrementReferenceCount() { ++fReferenceCount; }
30   void decrementReferenceCount() { if (fReferenceCount > 0) --fReferenceCount; }
31   Boolean& deleteWhenUnreferenced() { return fDeleteWhenUnreferenced; }            // fDeleteWhenUnreferenced表示在沒有客戶端請求該流時,是否從RTSPServer中刪除該流
32 
33   void deleteAllSubsessions();
34     // Removes and deletes all subsessions added by "addSubsession()", returning us to an 'empty' state
35     // Note: If you have already added this "ServerMediaSession" to a "RTSPServer" then, before calling this function,
36     //   you must first close any client connections that use it,
37     //   by calling "RTSPServer::closeAllClientSessionsForServerMediaSession()".
38 
39 protected:
40   ServerMediaSession(UsageEnvironment& env, char const* streamName,
41              char const* info, char const* description,
42              Boolean isSSM, char const* miscSDPLines);
43   // called only by "createNew()"
44 
45   virtual ~ServerMediaSession();
46 
47 private: // redefined virtual functions
48   virtual Boolean isServerMediaSession() const;
49 
50 private:
51   Boolean fIsSSM;
52 
53   // Linkage fields:
54   friend class ServerMediaSubsessionIterator;                           //  ServerMediaSubsessionIterator是一個用於訪問ServerMediaSubsession對象的迭代器
55   ServerMediaSubsession* fSubsessionsHead;
56   ServerMediaSubsession* fSubsessionsTail;
57   unsigned fSubsessionCounter;
58 
59   char* fStreamName;
60   char* fInfoSDPString;
61   char* fDescriptionSDPString;
62   char* fMiscSDPLines;
63   struct timeval fCreationTime;
64   unsigned fReferenceCount;
65   Boolean fDeleteWhenUnreferenced;
66 };

ServerMediaSession的構造函數比較簡單,主要就是初始化一些成員變量,產生一些對該媒體流的描述信息,然後我們來看一下ServerMediaSubsession這個類。

 1 class ServerMediaSubsession: public Medium {
 2 public:
 3   unsigned trackNumber() const { return fTrackNumber; }            //每個ServerMediaSubsession又叫一個track,有一個整型標識號trackNumber 4   char const* trackId();                                        // trackID函數返回trackNumber的字符串形式,用於填充SDP中的trackID字段
 5   virtual char const* sdpLines() = 0;                              // 產生關於該視頻流或者音頻流的描述信息(SDP)
 6   virtual void getStreamParameters(unsigned clientSessionId, // in
 7                    netAddressBits clientAddress, // in
 8                    Port const& clientRTPPort, // in
 9                    Port const& clientRTCPPort, // in
10                    int tcpSocketNum, // in (-1 means use UDP, not TCP)
11                    unsigned char rtpChannelId, // in (used if TCP)
12                    unsigned char rtcpChannelId, // in (used if TCP)
13                    netAddressBits& destinationAddress, // in out
14                    u_int8_t& destinationTTL, // in out
15                    Boolean& isMulticast, // out
16                    Port& serverRTPPort, // out
17                    Port& serverRTCPPort, // out
18                    void*& streamToken // out
19                    ) = 0;
20   virtual void startStream(unsigned clientSessionId, void* streamToken,                      // 開始流化
21                TaskFunc* rtcpRRHandler,
22                void* rtcpRRHandlerClientData,
23                unsigned short& rtpSeqNum,
24                unsigned& rtpTimestamp,
25                ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
26                void* serverRequestAlternativeByteHandlerClientData) = 0;
27   virtual void pauseStream(unsigned clientSessionId, void* streamToken);                     // 暫停流化
28   virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT,      // 從指定位置處開始流化(對應的操作即客戶端指定從進度條上的某一個點開始播放)
29               double streamDuration, u_int64_t& numBytes);
30      // This routine is used to seek by relative (i.e., NPT) time.
31      // "streamDuration", if >0.0, specifies how much data to stream, past "seekNPT".  (If <=0.0, all remaining data is streamed.)
32      // "numBytes" returns the size (in bytes) of the data to be streamed, or 0 if unknown or unlimited.
33   virtual void seekStream(unsigned clientSessionId, void* streamToken, char*& absStart, char*& absEnd);
34      // This routine is used to seek by 'absolute' time.
35      // "absStart" should be a string of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.<frac>Z".
36      // "absEnd" should be either NULL (for no end time), or a string of the same form as "absStart".
37      // These strings may be modified in-place, or can be reassigned to a newly-allocated value (after delete[]ing the original).
38   virtual void nullSeekStream(unsigned clientSessionId, void* streamToken,
39                   double streamEndTime, u_int64_t& numBytes);
40      // Called whenever we're handling a "PLAY" command without a specified start time.
41   virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);
42   virtual float getCurrentNPT(void* streamToken);
43   virtual FramedSource* getStreamSource(void* streamToken);                                      // FramedSource從名字即可以看出它即每一幀視頻流的來源(視頻或者音頻數據的來源)
44   virtual void deleteStream(unsigned clientSessionId, void*& streamToken);
45 
46   virtual void testScaleFactor(float& scale); // sets "scale" to the actual supported scale
47   virtual float duration() const;                                                                // 返回該子會話的持續時間
48     // returns 0 for an unbounded session (the default)
49     // returns > 0 for a bounded session
50   virtual void getAbsoluteTimeRange(char*& absStartTime, char*& absEndTime) const;               //  返回該子會話的時間範圍
51     // Subclasses can reimplement this iff they support seeking by 'absolute' time.
52 
53   // The following may be called by (e.g.) SIP servers, for which the
54   // address and port number fields in SDP descriptions need to be non-zero:
55   void setServerAddressAndPortForSDP(netAddressBits addressBits,
56                      portNumBits portBits);
57 
58 protected: // we're a virtual base class
59   ServerMediaSubsession(UsageEnvironment& env);
60   virtual ~ServerMediaSubsession();
61 
62   char const* rangeSDPLine() const;                                  // 產生rangeLine信息用於填充SDP信息中的rangeLine字段
63       // returns a string to be delete[]
64 
65   ServerMediaSession* fParentSession;                                // 父會話
66   netAddressBits fServerAddressForSDP;
67   portNumBits fPortNumForSDP;
68 
69 private:
70   friend class ServerMediaSession;
71   friend class ServerMediaSubsessionIterator;
72   ServerMediaSubsession* fNext;
73 
74   unsigned fTrackNumber; // within an enclosing ServerMediaSession
75   char const* fTrackId;
76 };

此處我們的媒體文件是.264文件,創建的ServerMediaSubsession對象是H264VideoFileServerMediaSubsession類的實例,該類是FileServerMediaSubsession的子類,FileServerMediaSubsession表示從媒體文件中獲取數據的子會話,FileServerMediaSubsession又是OnDemandServerMediaSubsession的子類。

H264VideoFileServerMediaSubsession的構造函數:

1 H264VideoFileServerMediaSubsession::H264VideoFileServerMediaSubsession(UsageEnvironment& env,
2                                        char const* fileName, Boolean reuseFirstSource)
3   : FileServerMediaSubsession(env, fileName, reuseFirstSource),
4     fAuxSDPLine(NULL), fDoneFlag(0), fDummyRTPSink(NULL) {
5 }

FileServerMediaSubsession的構造函數:

1 FileServerMediaSubsession::FileServerMediaSubsession(UsageEnvironment& env, char const* fileName,
2                 Boolean reuseFirstSource)
3   : OnDemandServerMediaSubsession(env, reuseFirstSource),
4     fFileSize(0) {
5   fFileName = strDup(fileName);
6 }

OnDemandServerMediaSubsession的構造函數:

 1 OnDemandServerMediaSubsession
 2 ::OnDemandServerMediaSubsession(UsageEnvironment& env,
 3                 Boolean reuseFirstSource,
 4                 portNumBits initialPortNum,
 5                 Boolean multiplexRTCPWithRTP)
 6   : ServerMediaSubsession(env),
 7     fSDPLines(NULL), fReuseFirstSource(reuseFirstSource),
 8     fMultiplexRTCPWithRTP(multiplexRTCPWithRTP), fLastStreamToken(NULL) {
 9   fDestinationsHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
10   if (fMultiplexRTCPWithRTP) {
11     fInitialPortNum = initialPortNum;
12   } else {
13     // Make sure RTP ports are even-numbered:
14     fInitialPortNum = (initialPortNum+1)&~1;
15   }
16   gethostname(fCNAME, sizeof fCNAME);
17   fCNAME[sizeof fCNAME-1] = '\0'; // just in case
18 }

關於testOnDemandRTSPServer.cpp就先介紹到這裏,後面詳細分析RTSP客戶端與RTSPServer建立RTSP連接的詳細過程。

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