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函数,很简单,主要包含以下几个步骤:

// Begin by setting up our usage environmen
// 创建工具类
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create the RTSP server:
// 创建RTSPServer,指定端口为8554
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
char const* descriptionString
= "Session streamed by \"testOnDemandRTSPServer\"";
// Set up each of the possible streams that can be served by the
// RTSP server. Each such stream is implemented using a
// "ServerMediaSession" object, plus one or more
// "ServerMediaSubsession" objects for each audio/video substream.
/* 为每一种媒体文件创建会话,简单理解就是:一个ServerMediaSession对象对应一个媒体文件,一个媒体文件中可能同时包含音频和视频,对于每个视频或者音频,对应一个ServerMediaSubsession对象,
一个ServerMediaSession中可以包含多个ServerMediaSubsession对象 */
// 这里我们只看H.264文件
// A H.264 video elementary stream:
{
  char const* streamName = "h264ESVideoTest";                   //标识请求播放该媒体文件时使用的名称
  char const* inputFileName = "test.264";                       //默认媒体文件名为test.264
  ServerMediaSession* sms = ServerMediaSession::createNew(*env, streamName, streamName,descriptionString);      //为该媒体文件创建一个ServerMediaSession
  /* .264文件只包含视频,创建一个ServerMediaSubsession对象并添加到ServerMediaSession
   H264VideoFileServerMediaSubsession是ServerMediaSubsession的子类,针对不同格式有不同的实现类 */
  sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName, reuseFirstSource));
  rtspServer->addServerMediaSession(sms);                       //将刚才创建的ServerMediaSession添加到RTSPServer中
  announceStream(rtspServer, sms, streamName, inputFileName);   //打印出播放该媒体文件的rtsp地址
}
// 程序从下面开始进入一个主循环,后面的return 0不会被执行。
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning

      

Live555是单线程的,基于事件驱动模式,程序从doEventLoop函数出进入无限循环,在这个循环中不断地查看事件队列是否有事件需要去处理,有就去处理,没有则休眠一小会儿,看下doEventLoop函数,该函数在live/BasicUsageEnvironment/BasicTaskScheduler0.cpp文件中定义。


void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
    //SingleStep函数中,对可读数据的socket进行读数据,对事件队列中的事件调用对应的处理函数处理
  }
}


 主循环部分的代码比较简单,那我们就需要仔细看看创建RTSPServer,创建ServerMediaSession以及ServerMediaSubsession这部分的代码,看这部分代码之前,我们需要先对RTSP协议的建立连接过程有个大概的了解,在此我就不再详述,网上有很多讲解这个过程的博文,在此推荐一个:http://www.cnblogs.com/qq78292959/archive/2010/08/12/2077039.html


  RTSPServer类即表示一个流媒体服务器实例,RTSPServer::createNew是一个简单工厂函数,使用指定的端口(8554)创建一个TCP的socket用于等待客户端的连接,然后new一个RTSPServer实例。


RTSPServer* RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
              UserAuthenticationDatabase* authDatabase,
              unsigned reclamationTestSeconds) {
  int ourSocket = setUpOurSocket(env, ourPort);
  if (ourSocket == -1) return NULL;
  return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
}
 RTSPServer的构造函数:
RTSPServer::RTSPServer(UsageEnvironment& env,
               int ourSocket, Port ourPort,
               UserAuthenticationDatabase* authDatabase,
               unsigned reclamationTestSeconds)
  : Medium(env),
    fRTSPServerPort(ourPort), fRTSPServerSocket(ourSocket), fHTTPServerSocket(-1), fHTTPServerPort(0),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
    fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
    fClientSessions(HashTable::create(STRING_HASH_KEYS)),
    fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), fRegisterRequestCounter(0),
    fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
    fAllowStreamingRTPOverTCP(True) {
  ignoreSigPipeOnSocket(ourSocket); // so that clients on the same host that are killed don't also kill us
  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
                           (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
}

 这里主要看一下turnOnBackgroundReadHandling函数,这个函数的作用即将某个socket加入SOCKET  SET(参见select模型),并指定相应的处理函数。这里的处理函数即收到RTSP客户端连接请求时的回调处理函数incomingConnectionHandlerRTSP,第三个参数作为回调函数的参数。


  ServerMediaSession::createNew是一个简单工厂模式函数,在其中new了一个ServerMediaSession对象,看一下ServerMediaSession这个类的定义。

class ServerMediaSession: public Medium {
public:
  static ServerMediaSession* createNew(UsageEnvironment& env,
                       char const* streamName = NULL,
                       char const* info = NULL,
                       char const* description = NULL,
                       Boolean isSSM = False,
                       char const* miscSDPLines = NULL);
  static Boolean lookupByName(UsageEnvironment& env,
                              char const* mediumName,
                              ServerMediaSession*& resultSession);
  char* generateSDPDescription(); // based on the entire session          //产生媒体描述信息(SDP),在收到DESCRIBE命令后回复给RTSP客户端
      // Note: The caller is responsible for freeing the returned string
  char const* streamName() const { return fStreamName; }                       // 返回流的名称
  Boolean addSubsession(ServerMediaSubsession* subsession);             // 添加表示子会话的ServerMediaSubsession对象
  unsigned numSubsessions() const { return fSubsessionCounter; }
  void testScaleFactor(float& scale); // sets "scale" to the actual supported scale
  float duration() const;                                  // 返回流的持续时间
    // a result == 0 means an unbounded session (the default)
    // a result < 0 means: subsession durations differ; the result is -(the largest).
    // a result > 0 means: this is the duration of a bounded session
  unsigned referenceCount() const { return fReferenceCount; }                      // 返回请求该流的RTSP客户端数目
  void incrementReferenceCount() { ++fReferenceCount; }
  void decrementReferenceCount() { if (fReferenceCount > 0) --fReferenceCount; }
  Boolean& deleteWhenUnreferenced() { return fDeleteWhenUnreferenced; }            // fDeleteWhenUnreferenced表示在没有客户端请求该流时,是否从RTSPServer中删除该流
  void deleteAllSubsessions();
    // Removes and deletes all subsessions added by "addSubsession()", returning us to an 'empty' state
    // Note: If you have already added this "ServerMediaSession" to a "RTSPServer" then, before calling this function,
    //   you must first close any client connections that use it,
    //   by calling "RTSPServer::closeAllClientSessionsForServerMediaSession()".
protected:
  ServerMediaSession(UsageEnvironment& env, char const* streamName,
             char const* info, char const* description,
             Boolean isSSM, char const* miscSDPLines);
  // called only by "createNew()"
  virtual ~ServerMediaSession();
private: // redefined virtual functions
  virtual Boolean isServerMediaSession() const;
private:
  Boolean fIsSSM;
  // Linkage fields:
  friend class ServerMediaSubsessionIterator;                           //  ServerMediaSubsessionIterator是一个用于访问ServerMediaSubsession对象的迭代器
  ServerMediaSubsession* fSubsessionsHead;
  ServerMediaSubsession* fSubsessionsTail;
  unsigned fSubsessionCounter;
  char* fStreamName;
  char* fInfoSDPString;
  char* fDescriptionSDPString;
  char* fMiscSDPLines;
  struct timeval fCreationTime;
  unsigned fReferenceCount;
  Boolean fDeleteWhenUnreferenced;
};

这里主要看一下turnOnBackgroundReadHandling函数,这个函数的作用即将某个socket加入SOCKET  SET(参见select模型),并指定相应的处理函数。这里的处理函数即收到RTSP客户端连接请求时的回调处理函数incomingConnectionHandlerRTSP,第三个参数作为回调函数的参数。


  ServerMediaSession::createNew是一个简单工厂模式函数,在其中new了一个ServerMediaSession对象,看一下ServerMediaSession这个类的定义。


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***