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

      

  • 相关阅读:
    nginx-1.8.1的安装
    ElasticSearch 在3节点集群的启动
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    sqoop导入导出对mysql再带数据库test能跑通用户自己建立的数据库则不行
    LeetCode 501. Find Mode in Binary Search Tree (找到二叉搜索树的众数)
    LeetCode 437. Path Sum III (路径之和之三)
    LeetCode 404. Sum of Left Leaves (左子叶之和)
    LeetCode 257. Binary Tree Paths (二叉树路径)
    LeetCode Questions List (LeetCode 问题列表)- Java Solutions
    LeetCode 561. Array Partition I (数组分隔之一)
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4385602.html
Copyright © 2011-2022 走看看