最近由于项目需要,自己开始阅读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 _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); }
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()函数中调用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的整体工作流程。