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的类图

 


你可能感兴趣的:(server)