zoukankan      html  css  js  c++  java
  • 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() << "
    ";
    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] = ''; // just in case
    18 }

      关于testOnDemandRTSPServer.cpp就先介绍到这里,后面详细分析RTSP客户端与RTSPServer建立RTSP连接的详细过程。

      

  • 相关阅读:
    (Vue中)cehart在同一个dom上画图图切换时饼图有折线图的坐标系
    linux(centos7)修改服务器时间
    centos6 yum源不能使用
    Linux 使用 history 来减少重复命令的几个实用技巧。
    7个Shell 拿来就用脚本实例!
    keepalived的配置解析&安装与爬坑
    linux最全命令使用手册
    linux各种误删文件恢复方法(经典强推)
    位运算符
    SQL 书写、执行顺序
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4385602.html
Copyright © 2011-2022 走看看