首先,看看這個程序的說明:
// 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連接的詳細過程。