Live555源代碼解讀(4)上

5、RTSP服務運做git

     基礎基本搞明白了,那麼RTSP,RTP等這些協議又是如何利用這些基礎機制運做的呢?首先來看RTSP.RTSP首先需創建TCP偵聽socket。可見於此函數:session


[cpp]
 view plaincopyapp

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

  2. UserAuthenticationDatabase* authDatabase,  socket

  3. unsigned reclamationTestSeconds) {  函數

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

  5. if (ourSocket == -1)  url

  6. return NULL;  spa

  7.   

  8.   

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

  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過程值得一觀:

相關文章
相關標籤/搜索