Live555源碼分析@njzhujinhua[1]:RTSPServer

本文圖由StarUML生成。

本文分析Live555中RTSPServer代碼。涉及流程從服務啓動到接收到rtsp協議後的交互。

  • 服務啓動及事件註冊

RTSPServer的創建由RTSPServer::createNew()生成

  // Create the RTSP server:
  RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }


createNew調用了RTSPServer構造函數,並在其中設置了該端口號消息對應的處理器的handler

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

其中turnOnBackgroundReadHandling進一步通過void BasicTaskScheduler::setBackgroundHandling將收到消息的處理器設置爲(TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP。

後續任務調度器主循環doEventLoop在檢測到有消息到達時,通過socket檢索到對應處理器並調用之。

  • 會話管理
   1: // A H.264 video elementary stream:
   2: {
   3:   char const* streamName = "h264ESVideoTest";
   4:   char const* inputFileName = "test.264";
   5:   ServerMediaSession* sms
   6:     = ServerMediaSession::createNew(*env, streamName, streamName,
   7:                     descriptionString);
   8:   sms->addSubsession(H264VideoFileServerMediaSubsession
   9:              ::createNew(*env, inputFileName, reuseFirstSource));
  10:   rtspServer->addServerMediaSession(sms);
  11:  
  12:   announceStream(rtspServer, sms, streamName, inputFileName);
  13: }

其中ServerMediaSession負責會話公共描述信息,具體文件格式相關的則由H264VideoFileServerMediaSubsession等負責。H264VideoFileServerMediaSubsession繼承自ServerMediaSubsession,但ServerMediaSubsession與ServerMediaSession類層次上是兄弟關係,均繼承自Medium類。一個ServerMediaSession可同時關聯多個ServerMediaSubsession,但目前沒找到這樣的例子。


 

  • 消息處理

任務調度器主循環一直在調用BasicTaskScheduler::SingleStep,其中依次檢測每個handler是否有消息到達需要處理,如果達到出發條件則調用註冊到該handler的處理器,如上面介紹是RTSPServer::incomingConnectionHandlerRTSP。在其中又調用了供RTSP和HTTP等公用的RTSPServer::incomingConnectionHandler。後者accept連接,如果成功則創建新的RTSPServer::RTSPClientConnection爲此連接服務。

RTSPServer::incomingConnectionHandler的實現

   1: void RTSPServer::incomingConnectionHandler(int serverSocket)
   2: {
   3:     struct sockaddr_in clientAddr;
   4:     SOCKLEN_T clientAddrLen = sizeof clientAddr;
   5:     int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
   6:     if (clientSocket < 0)
   7:     {
   8:         int err = envir().getErrno();
   9:         if (err != EWOULDBLOCK)
  10:         {
  11:             envir().setResultErrMsg("accept() failed: ");
  12:         }
  13:         return;
  14:     }
  15:     makeSocketNonBlocking(clientSocket);
  16:     increaseSendBufferTo(envir(), clientSocket, 50*1024);
  17:  
  18: #ifdef DEBUG
  19:     envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
  20: #endif
  21:  
  22:     // Create a new object for handling this RTSP connection:
  23:     (void)createNewClientConnection(clientSocket, clientAddr);
  24: }

RTSPServer::RTSPClientConnection構造函數的實現

   1: RTSPServer::RTSPClientConnection
   2:     ::RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
   3:     : fOurServer(ourServer), fIsActive(True),
   4:     fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
   5:     fRecursionCount(0), fOurSessionCookie(NULL)
   6: {
   7:     // Add ourself to our 'client connections' table:
   8:     fOurServer.fClientConnections->Add((char const*)this, this);
   9:  
  10:     // Arrange to handle incoming requests:
  11:     resetRequestBuffer();
  12:     envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
  13:         (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
  14: }

在其中,進一步將其回調處理函數設置爲了RTSPServer::RTSPClientConnection::incomingRequestHandler。正常情況下在緊隨其後的下一個事件循環中此新handler將被檢測並被調用。

   1: void RTSPServer::RTSPClientConnection::incomingRequestHandler1()
   2: {
   3:     struct sockaddr_in dummy; // 'from' address, meaningless in this case
   4:  
   5:     int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
   6:     handleRequestBytes(bytesRead);//讀取接收到的數據,並處理
   7: }
   8:  
   9: void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead)
  10: {
  11:     int numBytesRemaining = 0;
  12:     ++fRecursionCount;
  13:  
  14:     do {
  15:         RTSPServer::RTSPClientSession* clientSession = NULL;
  16:  
  17:         if (newBytesRead < 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft)
  18:         {
  19:             // Either the client socket has died, or the request was too big for us.
  20:             // Terminate this connection:
  21: #ifdef DEBUG
  22:             fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
  23: #endif
  24:             fIsActive = False;
  25:             break;
  26:         }
  27:  
  28:         Boolean endOfMsg = False;
  29:         unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
  30:  
  31:         //略,消息組裝等代碼 。
  32:         
  33:         // Parse the request string into command name and 'CSeq', then handle the command:   
  34:         fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
  35:         char cmdName[RTSP_PARAM_STRING_MAX];
  36:         char urlPreSuffix[RTSP_PARAM_STRING_MAX];
  37:         char urlSuffix[RTSP_PARAM_STRING_MAX];
  38:         char cseq[RTSP_PARAM_STRING_MAX];
  39:         char sessionIdStr[RTSP_PARAM_STRING_MAX];
  40:         unsigned contentLength = 0;
  41:         fLastCRLF[2] = '\0'; // temporarily, for parsing
  42:         Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,  //RTSP 命令解析
  43:             cmdName, sizeof cmdName,                      //請求中的cmdName
  44:             urlPreSuffix, sizeof urlPreSuffix,
  45:             urlSuffix, sizeof urlSuffix,
  46:             cseq, sizeof cseq,
  47:             sessionIdStr, sizeof sessionIdStr,
  48:             contentLength);
  49:         fLastCRLF[2] = '\r'; // restore its value
  50:         Boolean playAfterSetup = False;
  51:         if (parseSucceeded)
  52:         {
  53: #ifdef DEBUG
  54:             fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %ld bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
  55: #endif
  56:             // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
  57:             if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us 
  58:  
  59:             // If the request included a "Session:" id, and it refers to a client session that's
  60:             // current ongoing, then use this command to indicate 'liveness' on that client session:
  61:             Boolean const requestIncludedSessionId = sessionIdStr[0] != '\0';
  62:             if (requestIncludedSessionId)
  63:             {
  64:                 clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
  65:                 if (clientSession != NULL) clientSession->noteLiveness();
  66:             }
  67:  
  68:             // We now have a complete RTSP request.
  69:             // Handle the specified command (beginning with commands that are session-independent):
  70:             fCurrentCSeq = cseq;
  71:             if (strcmp(cmdName, "OPTIONS") == 0)  //根據當前的cmdName進行相應處理。 正常流程一般都是options describe setup play
  72:             {
  73:                 handleCmd_OPTIONS();   //簡單拼接一個支持命令的字符串響應
  74:             }
  75:             else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0')
  76:             {
  77:                 // The special "*" URL means: an operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:
  78:                 if (strcmp(cmdName, "GET_PARAMETER") == 0)
  79:                 {
  80:                     handleCmd_GET_PARAMETER((char const*)fRequestBuffer);
  81:                 }
  82:                 else if (strcmp(cmdName, "SET_PARAMETER") == 0)
  83:                 {
  84:                     handleCmd_SET_PARAMETER((char const*)fRequestBuffer);
  85:                 }
  86:                 else
  87:                 {
  88:                     handleCmd_notSupported();
  89:                 }
  90:             }
  91:             else if (strcmp(cmdName, "DESCRIBE") == 0)
  92:             {
  93:                 handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);  //這個涉及到鑑權,會話查找,如果存在的話生成對應sdp,並構造響應字符串
  94:             }
  95:             else if (strcmp(cmdName, "SETUP") == 0)
  96:             {
  97:                 if (!requestIncludedSessionId)
  98:                 {
  99:                     // No session id was present in the request.  So create a new "RTSPClientSession" object
 100:                     // for this request.  Choose a random (unused) 32-bit integer for the session id
 101:                     // (it will be encoded as a 8-digit hex number).  (We avoid choosing session id 0,
 102:                     // because that has a special use (by "OnDemandServerMediaSubsession").)
 103:                     u_int32_t sessionId;
 104:                     do {
 105:                         sessionId = (u_int32_t)our_random32();
 106:                         sprintf(sessionIdStr, "%08X", sessionId);
 107:                     } while (sessionId == 0 || fOurServer.fClientSessions->Lookup(sessionIdStr) != NULL);
 108:                     clientSession = fOurServer.createNewClientSession(sessionId);
 109:                     fOurServer.fClientSessions->Add(sessionIdStr, clientSession);
 110:                 }
 111:                 if (clientSession != NULL)
 112:                 {
 113:                     clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
 114:                     playAfterSetup = clientSession->fStreamAfterSETUP;
 115:                 }
 116:                 else
 117:                 {
 118:                     handleCmd_sessionNotFound();
 119:                 }
 120:             }
 121:             else if (strcmp(cmdName, "TEARDOWN") == 0
 122:                 || strcmp(cmdName, "PLAY") == 0
 123:                 || strcmp(cmdName, "PAUSE") == 0
 124:                 || strcmp(cmdName, "GET_PARAMETER") == 0
 125:                 || strcmp(cmdName, "SET_PARAMETER") == 0)
 126:             {
 127:                 if (clientSession != NULL)
 128:                 {
 129:                     clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
 130:                 }
 131:                 else
 132:                 {
 133:                     handleCmd_sessionNotFound();
 134:                 }
 135:             }
 136:             else if (strcmp(cmdName, "REGISTER") == 0)
 137:             {
 138:                 // Because - unlike other commands - an implementation of this command needs
 139:                 // the entire URL, we re-parse the command to get it:
 140:                 char* url = strDupSize((char*)fRequestBuffer);
 141:                 if (sscanf((char*)fRequestBuffer, "%*s %s", url) == 1)
 142:                 {
 143:                     // Check for special command-specific parameters in a "Transport:" header:
 144:                     Boolean reuseConnection, deliverViaTCP;
 145:                     char* proxyURLSuffix;
 146:                     parseTransportHeaderForREGISTER((const char*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
 147:  
 148:                     handleCmd_REGISTER(url, urlSuffix, (char const*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
 149:                     delete[] proxyURLSuffix;
 150:                 }
 151:                 else
 152:                 {
 153:                     handleCmd_bad();
 154:                 }
 155:                 delete[] url;
 156:             }
 157:             else
 158:             {
 159:                 // The command is one that we don't handle:
 160:                 handleCmd_notSupported();
 161:             }
 162:         }
 163:         else
 164:         {
 165:             // zhu.jinhua delete HTTP code
 166:         }
 167:  
 168: #ifdef DEBUG
 169:         fprintf(stderr, "sending response: %s", fResponseBuffer);
 170: #endif
 171:         send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
 172:  
 173:         if (playAfterSetup)
 174:         {
 175:             // The client has asked for streaming to commence now, rather than after a
 176:             // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
 177:             clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
 178:         }
 179:  
 180:         // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
 181:         // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
 182:         unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
 183:         numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
 184:         resetRequestBuffer(); // to prepare for any subsequent request
 185:  
 186:         if (numBytesRemaining > 0)
 187:         {
 188:             memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
 189:             newBytesRead = numBytesRemaining;
 190:         }
 191:     } while (numBytesRemaining > 0);
 192:  
 193:     --fRecursionCount;
 194:     if (!fIsActive)
 195:     {
 196:         if (fRecursionCount > 0) closeSockets(); else delete this;
 197:         // Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively
 198:         // while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).
 199:         // In such a case we don't want to actually delete ourself until we leave the outermost call.
 200:     }
 201: }

其中option命令的處理如下

   1: void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS()
   2: {
   3:     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
   4:         "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
   5:         fCurrentCSeq, dateHeader(), fOurServer.allowedCommandNames());
   6: }

可見其只是簡單根據支持情況拼接了個字符串而已。同樣describe的響應結構如下

   1: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
   2:     "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
   3:     "%s"
   4:     "Content-Base: %s/\r\n"
   5:     "Content-Type: application/sdp\r\n"
   6:     "Content-Length: %d\r\n\r\n"
   7:     "%s",
   8:     fCurrentCSeq,
   9:     dateHeader(),
  10:     rtspURL,
  11:     sdpDescriptionSize,
  12:     sdpDescription);
至於DESCRIBE及其他CMD的詳細處理尤其是sdp的生成,再開篇分析學習。

最後上一個RTPServer的類圖

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章