Live555源代码解读(4)上

原创
2016/02/24 21:11
阅读数 923

五、RTSP服务运作

     基础基本搞明白了,那么RTSP,RTP等这些协议又是如何利用这些基础机制运作的呢?首先来看RTSP.RTSP首先需建立TCP侦听socket。可见于此函数:


[cpp]
 view plaincopy

  1. DynamicRTSPServer* DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,  

  2. UserAuthenticationDatabase* authDatabase,  

  3. unsigned reclamationTestSeconds) {  

  4. int ourSocket = setUpOurSocket(env, ourPort); //建立TCP socket  

  5. if (ourSocket == -1)  

  6. return NULL;  

  7.   

  8.   

  9. return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase,  

  10. reclamationTestSeconds);  

  11. }  

要帧听客户端的连接,就需要利用任务调度机制了,所以需添加一个socket handler。可见于此函数:


[cpp]
 view plaincopy

  1. RTSPServer::RTSPServer(UsageEnvironment& env,   

  2.         int ourSocket,   

  3.         Port ourPort,  

  4.         UserAuthenticationDatabase* authDatabase,  

  5.         unsigned reclamationTestSeconds) :  

  6.         Medium(env),   

  7.         fRTSPServerSocket(ourSocket),  

  8.         fRTSPServerPort(ourPort),  

  9.         fHTTPServerSocket(-1),  

  10.         fHTTPServerPort(0),  

  11.         fClientSessionsForHTTPTunneling(NULL),   

  12.         fAuthDB(authDatabase),  

  13.         fReclamationTestSeconds(reclamationTestSeconds),  

  14.         fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))   

  15. {  

  16. #ifdef USE_SIGNALS  

  17.     // Ignore the SIGPIPE signal, so that clients on the same host that are killed  

  18.     // don't also kill us:  

  19.     signal(SIGPIPE, SIG_IGN);  

  20. #endif  

  21.   

  22.   

  23.     // Arrange to handle connections from others:  

  24.     env.taskScheduler().turnOnBackgroundReadHandling(  

  25.             fRTSPServerSocket,  

  26.             (TaskScheduler::BackgroundHandlerProc*) &incomingConnectionHandlerRTSP,  

  27.             this);  

  28. }  

当收到客户的连接时需保存下代表客户端的新socket,以后用这个socket与这个客户通讯。每个客户将来会对应一个rtp会话,而且各客户的RTSP请求只控制自己的rtp会话,那么最好建立一个会话类,代表各客户的rtsp会话。于是类RTSPServer::RTSPClientSession产生,它保存的代表客户的socket。下为RTSPClientSession的创建过程


[cpp]
 view plaincopy

  1. void RTSPServer::incomingConnectionHandler(int serverSocket)   

  2. {  

  3.     struct sockaddr_in clientAddr;  

  4.     SOCKLEN_T clientAddrLen = sizeof clientAddr;  

  5.       

  6.     //接受连接  

  7.     int clientSocket = accept(serverSocket,  

  8.             (struct sockaddr*) &clientAddr,  

  9.             &clientAddrLen);  

  10.       

  11.     if (clientSocket < 0) {  

  12.         int err = envir().getErrno();  

  13.         if (err != EWOULDBLOCK) {  

  14.             envir().setResultErrMsg("accept() failed: ");  

  15.         }  

  16.         return;  

  17.     }  

  18.       

  19.     //设置socket的参数  

  20.     makeSocketNonBlocking(clientSocket);  

  21.     increaseSendBufferTo(envir(), clientSocket, 50 * 1024);  

  22.   

  23. #ifdef DEBUG  

  24.     envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << "\n";  

  25. #endif  

  26.   

  27.     //产生一个sesson id  

  28.       

  29.     // Create a new object for this RTSP session.  

  30.     // (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number).  We don't bother checking for  

  31.     //  a collision; the probability of two concurrent sessions getting the same session id is very low.)  

  32.     // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)  

  33.     unsigned sessionId;  

  34.     do {  

  35.         sessionId = (unsigned) our_random();  

  36.     } while (sessionId == 0);  

  37.       

  38.     //创建RTSPClientSession,注意传入的参数  

  39.     (void) createNewClientSession(sessionId, clientSocket, clientAddr);  

  40. }  

 RTSPClientSession要提供什么功能呢?可以想象:需要监听客户端的rtsp请求并回应它,需要在DESCRIBE请求中返回所请求的流的信息,需要在SETUP请求中建立起RTP会话,需要在TEARDOWN请求中关闭RTP会话,等等...

RTSPClientSession要侦听客户端的请求,就需把自己的socket handler加入计划任务。证据如下:


[cpp]
 view plaincopy

  1. RTSPServer::RTSPClientSession::RTSPClientSession(  

  2.             RTSPServer& ourServer,  

  3.             unsigned sessionId,  

  4.             int clientSocket,  

  5.             struct sockaddr_in clientAddr) :  

  6.         fOurServer(ourServer),  

  7.         fOurSessionId(sessionId),  

  8.         fOurServerMediaSession(NULL),  

  9.         fClientInputSocket(clientSocket),  

  10.         fClientOutputSocket(clientSocket),  

  11.         fClientAddr(clientAddr),  

  12.         fSessionCookie(NULL),  

  13.         fLivenessCheckTask(NULL),  

  14.         fIsMulticast(False),  

  15.         fSessionIsActive(True),  

  16.         fStreamAfterSETUP(False),  

  17.         fTCPStreamIdCount(0),  

  18.         fNumStreamStates(0),  

  19.         fStreamStates(NULL),  

  20.         fRecursionCount(0)  

  21. {  

  22.     // Arrange to handle incoming requests:  

  23.     resetRequestBuffer();  

  24.     envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,  

  25.             (TaskScheduler::BackgroundHandlerProc*) &incomingRequestHandler,  

  26.             this);  

  27.     noteLiveness();  

  28. }  

下面重点讲一下下RTSPClientSession响应DESCRIBE请求的过程:

[cpp] view plaincopy

  1. void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(  

  2.         char const* cseq,  

  3.         char const* urlPreSuffix,  

  4.         char const* urlSuffix,  

  5.         char const* fullRequestStr)  

  6. {  

  7.     char* sdpDescription = NULL;  

  8.     char* rtspURL = NULL;  

  9.     do {  

  10.         //整理一下下RTSP地址  

  11.         char urlTotalSuffix[RTSP_PARAM_STRING_MAX];  

  12.         if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2  

  13.                 > sizeof urlTotalSuffix) {  

  14.             handleCmd_bad(cseq);  

  15.             break;  

  16.         }  

  17.         urlTotalSuffix[0] = '\0';  

  18.         if (urlPreSuffix[0] != '\0') {  

  19.             strcat(urlTotalSuffix, urlPreSuffix);  

  20.             strcat(urlTotalSuffix, "/");  

  21.         }  

  22.         strcat(urlTotalSuffix, urlSuffix);  

  23.   

  24.   

  25.         //验证帐户和密码  

  26.         if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr))  

  27.             break;  

  28.   

  29.   

  30.         // We should really check that the request contains an "Accept:" #####  

  31.         // for "application/sdp", because that's what we're sending back #####  

  32.   

  33.   

  34.         // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":  

  35.         //跟据流的名字查找ServerMediaSession,如果找不到,会创建一个。每个ServerMediaSession中至少要包含一个  

  36.         //ServerMediaSubsession。一个ServerMediaSession对应一个媒体,可以认为是Server上的一个文件,或一个实时获取设备。其包含的每个ServerMediaSubSession代表媒体中的一个Track。所以一个ServerMediaSession对应一个媒体,如果客户请求的媒体名相同,就使用已存在的ServerMediaSession,如果不同,就创建一个新的。一个流对应一个StreamState,StreamState与ServerMediaSubsession相关,但代表的是动态的,而ServerMediaSubsession代表静态的。  

  37.         ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);  

  38.         if (session == NULL) {  

  39.             handleCmd_notFound(cseq);  

  40.             break;  

  41.         }  

  42.   

  43.   

  44.         // Then, assemble a SDP description for this session:  

  45.         //获取SDP字符串,在函数内会依次获取每个ServerMediaSubSession的字符串然连接起来。  

  46.         sdpDescription = session->generateSDPDescription();  

  47.         if (sdpDescription == NULL) {  

  48.             // This usually means that a file name that was specified for a  

  49.             // "ServerMediaSubsession" does not exist.  

  50.             snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  

  51.                     "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"  

  52.                     "CSeq: %s\r\n"  

  53.                     "%s\r\n", cseq, dateHeader());  

  54.             break;  

  55.         }  

  56.         unsigned sdpDescriptionSize = strlen(sdpDescription);  

  57.   

  58.   

  59.         // Also, generate our RTSP URL, for the "Content-Base:" header  

  60.         // (which is necessary to ensure that the correct URL gets used in  

  61.         // subsequent "SETUP" requests).  

  62.         rtspURL = fOurServer.rtspURL(session, fClientInputSocket);  

  63.   

  64.   

  65.         //形成响应DESCRIBE请求的RTSP字符串。  

  66.         snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  

  67.                 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"  

  68.                 "%s"  

  69.                 "Content-Base: %s/\r\n"  

  70.                 "Content-Type: application/sdp\r\n"  

  71.                 "Content-Length: %d\r\n\r\n"  

  72.                 "%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize,  

  73.                 sdpDescription);  

  74.     } while (0);  

  75.   

  76.   

  77.     delete[] sdpDescription;  

  78.     delete[] rtspURL;  

  79.   

  80.   

  81.     //返回后会被立即发送(没有把socket write操作放入计划任务中)。  

  82. }  

fOurServer.lookupServerMediaSession(urlTotalSuffix)中会在找不到同名ServerMediaSession时新建一个,代表一个RTP流的ServerMediaSession们是被RTSPServer管理的,而不是被RTSPClientSession拥有。为什么呢?因为ServerMediaSession代表的是一个静态的流,也就是可以从它里面获取一个流的各种信息,但不能获取传输状态。不同客户可能连接到同一个流,所以ServerMediaSession应被RTSPServer所拥有。创建一个ServerMediaSession过程值得一观:


展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部