live555 源码分析

最近由于项目需要,自己开始阅读live555的源码,下面是一些总结,希望和大家多多交流。

这次项目

1.live555 开发库源代码包括6个部分:UsageEnviroment、BasicUsageEnviroment、groupsock、liveMedia、testProgs、mediaServer

1)UsageEnviroment

UsageEnviroment目录中,声明一些虚类 class UsageEnviroment    class HashTable  class TaskScheduler

这些类中包括各种纯虚函数,这些虚函数定义出这些类的整体轮廓。这些类的某些子类,如果想要实例化的,必须描绘出这些轮廓。即实现这些函数。

class UsageEnviroment 是程序运行环境的抽象描述,通过包含TaskScheduler& fScheduler 的私有变量,表示网络功能、异步事件处理等。

class TaskScheduler 是定义如何设置RTSPServer端的网络功能、异步事件处理。

class HashTable 是定义类似于字典的功能。(key,value)。

注: virtual 关键字修饰的虚函数,同时不包括实现部分的函数,成为纯虚函数。带有纯虚函数的类是无法实例化的。

2)BasicUsageEnviroment

BasicUsageEnviroment目录中,主要是对UsageEnviroment目录中的class UsageEnviroment 、class TaskScheduler 各个部分的逐步实现,在实现的过程中逐步引入相应的成员数据。

3)groupsock

groupsock目录,主要是对基本的socket封装,在liveMedia目录中,涉及网络的部分会有使用。

这部分,我现在并没有太多关注。

4)liveMedia

liveMeida目录,是整个live555开发库的核心部分。

(1)class Medim,class _Tables,class MediaLookupTable

class Medim,class _Tables在Media.hh文件中定义。在 Media.cpp文件中实现。class MediaLookupTable在 Media.cpp文件中定义和实现。

liveMeida目录中最基础的类是class Medium。liveMedia目录下,很多class类型都是class Media 的子类。

class _Tables,class MediaLookupTable主要是实现对Medium的管理


#define mediumNameMaxLen 30

class Medium {
public:
  static Boolean lookupByName(UsageEnvironment& env,
			      char const* mediumName,
			      Medium*& resultMedium);          //通过env.liveMediaPriv.mediaTable 这样一个MediaLookupTable里面的lookup方法查找Medium

  static void close(UsageEnvironment& env, char const* mediumName);      //关闭特定的env中,特定的Medium,释放相关的内存
  static void close(Medium* medium); // alternative close() method using ptrs
      // (has no effect if medium == NULL)

  UsageEnvironment& envir() const {return fEnviron;}

  char const* name() const {return fMediumName;}

  // Test for specific types of media:
  virtual Boolean isSource() const;
  virtual Boolean isSink() const;
  virtual Boolean isRTCPInstance() const;
  virtual Boolean isRTSPClient() const;
  virtual Boolean isRTSPServer() const;
  virtual Boolean isMediaSession() const;
  virtual Boolean isServerMediaSession() const;
  virtual Boolean isDarwinInjector() const;

protected:
  Medium(UsageEnvironment& env); // abstract base class
  virtual ~Medium(); // instances are deleted using close() only

  TaskToken& nextTask() {
	return fNextTask;
  }

private:
  friend class MediaLookupTable;
  UsageEnvironment& fEnviron;                // Medium 属于的UsageEnviroment,在创建Meium实例的时候,赋值。
  char fMediumName[mediumNameMaxLen];        // Medium 的名称,例如:liveMedium0/1/2....
  TaskToken fNextTask;                       // void*  与Medium相关的TaskToken,
};

上面是class Medium的定义。

class Medium    某种媒体

class _Tables    查找表,包括void *mediaTable,class MediaLookupTable 类型;void* socketTable,具体类型我现在没有找到。

class MedaLookupTable    通过HashTabel实现的Medium的查找表,包括所有在UsageEnviroment 中创建的Medium实体。


上面三种类型是通过class UsageEnviroment类型中的void *liveMediaPriv 成员变量联系起来。

其中liveMediaPriv实际上是_Tables* 类型,而在_Tables类型中有void *mediaTable成员变量,mediaTable是MediaLookupTable*类型的。

如果我们知道UsageEnviroment& env,给出key值,即Medium的名称,我们就可以查找相关的Medium。


2)class RTSPServer, class RTSPServer::class RTSPClientSession

class RTSPServer,是class Medium 的子类,是对RTSPServer的抽象描述。

RTSPServer主要功能包括

  [ 1 ] RTSPServer可以接收客户端TCP连接请求。在接收客户端TCP连接请求后,会创建class RTSPServer::class RTSPClientSession类。

class RTSPClientSession 是真正负责与客户端进行RTSP消息的接收、解包、打包、发送。是RTSP协议在RTSPServer端的具体实现。

接收客户端请求的Socket和端口号,在创建RTSPServer实例时,会调用

static int setUpOurSocket(UsageEnvironment& env, Port& ourPort);

创建RTSPServer端的非阻塞的监听Socket,并且调用listen,监听网络数据。

如果后续有请求消息到达,RTSPServer进行处理。

这里需要特别提出RTSPServer处理网络数据采用的是Select模型。

例如:在创建RTSPServer实例时,源代码如下:

RTSPServer::RTSPServer(UsageEnvironment& env,
		       int ourSocket, Port ourPort,
		       UserAuthenticationDatabase* authDatabase,
		       unsigned reclamationTestSeconds)
  : Medium(env),
    fRTSPServerSocket(ourSocket), fRTSPServerPort(ourPort),
    fHTTPServerSocket(-1), fHTTPServerPort(0), fClientSessionsForHTTPTunneling(NULL),
    fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)) {
#ifdef USE_SIGNALS
  // Ignore the SIGPIPE signal, so that clients on the same host that are killed
  // don't also kill us:
  signal(SIGPIPE, SIG_IGN);
#endif

  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
						   (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
}

我们通过调用TaskScheduler : :  turnOnBackgroundReadHandling(),开启监听SOCKET fRTSOServerSocket的READ功能,设置回调函数,以及回调函数的参数,RTSPServer* this。
static void incomingConnectionHandlerRTSP(void*, int /*mask*/);        //RTSPServer接收到

TaskScheduler : : doEventLoop( )循环中

void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
  }
}

会调用TaskScheduler : : SingleStep()函数,在SingleStep()函数中,我们会处理网络数据的发送和接收(select模型)、异步事件的处理、触发事件的处理。

在这里,我们会在TaskScheduler : : SingleStep()函数中调用select函数,发现监听SOCKET fRTSOServerSocket有数据需要去读。会调用:

static void incomingConnectionHandlerRTSP(void*, int /*mask*/);        //RTSPServer接收到

调用accept()函数,创建RTSPServer端,与特定的Client通信的SOCKET,获取Client的地址。之后实例化class RTSPServer::class RTSPClientSession。

RTSPServer::RTSPClientSession
::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSessionId(sessionId),
    fOurServerMediaSession(NULL),
    fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
    fSessionCookie(NULL), fLivenessCheckTask(NULL),
    fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
    fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) {
  // Arrange to handle incoming requests:
  resetRequestBuffer();                                                     
  envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
     (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);  //打开int fClientInputSocket的READ功能,接收Client的RTSP消息,设置处理函数。
  noteLiveness();
}

赋值:

 int fRTSPServerSocket;                           //接收客户端TCP连接请求的SOCKET
 Port fRTSPServerPort;                            //接收客户端TCP请求的端口

  [ 2 ] 增加,查找,删除RTSPServer 支持的ServerMediaSession集合。

  void addServerMediaSession(ServerMediaSession* serverMediaSession);
  virtual ServerMediaSession* lookupServerMediaSession(char const* streamName);
  void removeServerMediaSession(ServerMediaSession* serverMediaSession);
  void removeServerMediaSession(char const* streamName);

ServerMediaSession集合的相关数据保存在HashTable中,在创建RTSPServer时,new HashTable:

HashTable* fServerMediaSessions;          //服务器端ServerMediaSession的查找表,(Medium名称,指向ServerMediaSession的地址)

  [ 3 ] 用户验证。

UserAuthenticationDatabase* fAuthDB

  [ 4 ]  TCP打洞。

RTSPServer.hh源文件:

#define RTSP_BUFFER_SIZE 10000 // for incoming requests, and outgoing responses

class RTSPServer: public Medium {
public:
  static RTSPServer* createNew(UsageEnvironment& env, Port ourPort = 554,
			       UserAuthenticationDatabase* authDatabase = NULL,
			       unsigned reclamationTestSeconds = 65);
      // If ourPort.num() == 0, we'll choose the port number
      // Note: The caller is responsible for reclaiming "authDatabase"
      // If "reclamationTestSeconds" > 0, then the "RTSPClientSession" state for
      //     each client will get reclaimed (and the corresponding RTP stream(s)
      //     torn down) if no RTSP commands - or RTCP "RR" packets - from the
      //     client are received in at least "reclamationTestSeconds" seconds.

  static Boolean lookupByName(UsageEnvironment& env, char const* name,
			      RTSPServer*& resultServer);

  void addServerMediaSession(ServerMediaSession* serverMediaSession);
  virtual ServerMediaSession* lookupServerMediaSession(char const* streamName);
  void removeServerMediaSession(ServerMediaSession* serverMediaSession);
  void removeServerMediaSession(char const* streamName);

  char* rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket = -1) const;
      // returns a "rtsp://" URL that could be used to access the
      // specified session (which must already have been added to
      // us using "addServerMediaSession()".
      // This string is dynamically allocated; caller should delete[]
      // (If "clientSocket" is non-negative, then it is used (by calling "getsockname()") to determine
      //  the IP address to be used in the URL.)
  char* rtspURLPrefix(int clientSocket = -1) const;
      // like "rtspURL()", except that it returns just the common prefix used by
      // each session's "rtsp://" URL.
      // This string is dynamically allocated; caller should delete[]

  UserAuthenticationDatabase* setAuthenticationDatabase(UserAuthenticationDatabase* newDB);
      // Changes the server's authentication database to "newDB", returning a pointer to the old database (if there was one).
      // "newDB" may be NULL (you can use this to disable authentication at runtime, if desired).

  Boolean setUpTunnelingOverHTTP(Port httpPort);
      // (Attempts to) enable RTSP-over-HTTP tunneling on the specified port.
      // Returns True iff the specified port can be used in this way (i.e., it's not already being used for a separate HTTP server).
      // Note: RTSP-over-HTTP tunneling is described in http://developer.apple.com/quicktime/icefloe/dispatch028.html
  portNumBits httpServerPortNum() const; // in host byte order.  (Returns 0 if not present.)

protected:
  RTSPServer(UsageEnvironment& env,
	     int ourSocket, Port ourPort,
	     UserAuthenticationDatabase* authDatabase,
	     unsigned reclamationTestSeconds);
      // called only by createNew();
  virtual ~RTSPServer();

  static int setUpOurSocket(UsageEnvironment& env, Port& ourPort);
  virtual Boolean specialClientAccessCheck(int clientSocket, struct sockaddr_in& clientAddr,
					   char const* urlSuffix);
      // a hook that allows subclassed servers to do server-specific access checking
      // on each client (e.g., based on client IP address), without using
      // digest authentication.

private: // redefined virtual functions
  virtual Boolean isRTSPServer() const;

protected:
  // The state of each individual session handled by a RTSP server:
  class RTSPClientSession {
  public:
    RTSPClientSession(RTSPServer& ourServer, unsigned sessionId,
		      int clientSocket, struct sockaddr_in clientAddr);
    virtual ~RTSPClientSession();
  protected:
    // Make the handler functions for each command virtual, to allow subclasses to redefine them:
    virtual void handleCmd_bad(char const* cseq);
    virtual void handleCmd_notSupported(char const* cseq);
    virtual void handleCmd_notFound(char const* cseq);
    virtual void handleCmd_unsupportedTransport(char const* cseq);
    virtual void handleCmd_OPTIONS(char const* cseq);
    virtual void handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
				    char const* fullRequestStr);
    virtual void handleCmd_SETUP(char const* cseq,
				 char const* urlPreSuffix, char const* urlSuffix,
				 char const* fullRequestStr);
    virtual void handleCmd_withinSession(char const* cmdName,
					 char const* urlPreSuffix, char const* urlSuffix,
					 char const* cseq, char const* fullRequestStr);
    virtual void handleCmd_TEARDOWN(ServerMediaSubsession* subsession,
				    char const* cseq);
    virtual void handleCmd_PLAY(ServerMediaSubsession* subsession,
				char const* cseq, char const* fullRequestStr);
    virtual void handleCmd_PAUSE(ServerMediaSubsession* subsession,
				 char const* cseq);
    virtual void handleCmd_GET_PARAMETER(ServerMediaSubsession* subsession,
					 char const* cseq, char const* fullRequestStr);
    virtual void handleCmd_SET_PARAMETER(ServerMediaSubsession* subsession,
					 char const* cseq, char const* fullRequestStr);
    // Support for optional RTSP-over-HTTP tunneling:
    virtual Boolean parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize,
					   char* urlSuffix, unsigned urlSuffixMaxSize,
					   char* sessionCookie, unsigned sessionCookieMaxSize,
					   char* acceptStr, unsigned acceptStrMaxSize);
    virtual void handleHTTPCmd_notSupported();
    virtual void handleHTTPCmd_notFound();
    virtual void handleHTTPCmd_TunnelingGET(char const* sessionCookie);
    virtual Boolean handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize);
    virtual void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* fullRequestStr);
  protected:
    UsageEnvironment& envir() { return fOurServer.envir(); }
    void reclaimStreamStates();
    void resetRequestBuffer();
    Boolean authenticationOK(char const* cmdName, char const* cseq,
                             char const* urlSuffix,
                             char const* fullRequestStr);
    Boolean isMulticast() const { return fIsMulticast; }
    static void incomingRequestHandler(void*, int /*mask*/);
    void incomingRequestHandler1();
    static void handleAlternativeRequestByte(void*, u_int8_t requestByte);
    void handleAlternativeRequestByte1(u_int8_t requestByte);
    void handleRequestBytes(int newBytesRead);
    void noteLiveness();
    static void noteClientLiveness(RTSPClientSession* clientSession);
    static void livenessTimeoutTask(RTSPClientSession* clientSession);
    void changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize);
  protected:
    RTSPServer& fOurServer;
    unsigned fOurSessionId;
    ServerMediaSession* fOurServerMediaSession;
    int fClientInputSocket, fClientOutputSocket;
    struct sockaddr_in fClientAddr;
    char* fSessionCookie; // used for optional RTSP-over-HTTP tunneling
    TaskToken fLivenessCheckTask;
    unsigned char fRequestBuffer[RTSP_BUFFER_SIZE];
    unsigned fRequestBytesAlreadySeen, fRequestBufferBytesLeft;
    unsigned char* fLastCRLF;
    unsigned fBase64RemainderCount; // used for optional RTSP-over-HTTP tunneling (possible values: 0,1,2,3)
    unsigned char fResponseBuffer[RTSP_BUFFER_SIZE];
    Boolean fIsMulticast, fSessionIsActive, fStreamAfterSETUP;
    Authenticator fCurrentAuthenticator; // used if access control is needed
    unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP
    unsigned fNumStreamStates;
    struct streamState {
      ServerMediaSubsession* subsession;
      void* streamToken;
    } * fStreamStates;
  };

  // If you subclass "RTSPClientSession", then you should also redefine this virtual function in order
  // to create new objects of your subclass:
  virtual RTSPClientSession*
  createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr);

  // An iterator over our "ServerMediaSession" objects:
  class ServerMediaSessionIterator {
  public:
    ServerMediaSessionIterator(RTSPServer& server);
    virtual ~ServerMediaSessionIterator();
    ServerMediaSession* next();
  private:
    HashTable::Iterator *fOurIterator;
    ServerMediaSession* fNextPtr;
  };

private:
  static void incomingConnectionHandlerRTSP(void*, int /*mask*/);        //RTSPServer接收到请求
  void incomingConnectionHandlerRTSP1();

  static void incomingConnectionHandlerHTTP(void*, int /*mask*/);
  void incomingConnectionHandlerHTTP1();

  void incomingConnectionHandler(int serverSocket);

private:
  friend class RTSPClientSession;
  friend class ServerMediaSessionIterator;
  int fRTSPServerSocket;                           //接收客户端TCP连接请求的SOCKET
  Port fRTSPServerPort;                            //接收客户端TCP请求的端口
  int fHTTPServerSocket; // for optional RTSP-over-HTTP tunneling
  Port fHTTPServerPort; // ditto
  HashTable* fClientSessionsForHTTPTunneling; // ditto (maps 'session cookie' strings to "RTSPClientSession"s)
  UserAuthenticationDatabase* fAuthDB;        //用户验证
  unsigned fReclamationTestSeconds;
  HashTable* fServerMediaSessions;          //服务器端ServerMediaSession的查找表,(Medium名称,指向ServerMediaSession的地址)
};

class RTSPServer::class RTSPClientSession是对RTSP协议在Server端的描述。主要功能是:

接收客户端RTSP的Request 消息、解析RTSP Request消息、封装Server端的RTSP  Reply消息、发送Server端的RTSP  Reply消息。

static void incomingRequestHandler(void*, int /*mask*/);   //class RTSPServer::class RTSPClientSession的整体工作流程。



你可能感兴趣的:(live555 源码分析)