Live555学习之(二)------- testOnDemandRTSPServer

首先,看看这个程序的说明:

// 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.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连接的详细过程。

猜你喜欢

转载自blog.csdn.net/qq_43716137/article/details/108638258