live555 信令分发

本文介绍LIVE555 RTSP服务器的主要功能模块和工作流程,包括客户端连接建立、RTSP信令处理过程以及信令交互实例。深入分析了RTSPClientConnection类如何处理客户端请求,并解释了信令流程中各步骤的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

llive 555 信令类及消息

main  函数:

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

  UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
  // To implement client access control to the RTSP server, do the following:
  authDB = new UserAuthenticationDatabase;
  authDB->addUserRecord("username1", "password1"); // replace these with real strings
  // Repeat the above with each <username>, <password> that you wish to allow
  // access to the server.
#endif

  // Create the RTSP server.  Try first with the default port number (554),
  // and then with the alternative port number (8554):
  RTSPServer* rtspServer;
  portNumBits rtspServerPortNum = 554;    
  /*创建rtsp 服务*/
  rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
  if (rtspServer == NULL) {
    rtspServerPortNum = 8554;
    rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
  }
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  *env << "LIVE555 Media Server\n";
  *env << "\tversion " << MEDIA_SERVER_VERSION_STRING
       << " (LIVE555 Streaming Media library version "
       << LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";

  *env << "Play streams from this server using the URL\n";
  if (weHaveAnIPv4Address(*env)) {
    char* rtspURLPrefix = rtspServer->ipv4rtspURLPrefix();
    *env << "\t" << rtspURLPrefix << "<filename>\n";
    delete[] rtspURLPrefix;
    if (weHaveAnIPv6Address(*env)) *env << "or\n";
  }
  if (weHaveAnIPv6Address(*env)) {
    char* rtspURLPrefix = rtspServer->ipv6rtspURLPrefix();
    *env << "\t" << rtspURLPrefix << "<filename>\n";
    delete[] rtspURLPrefix;
  }
  *env << "where <filename> is a file present in the current directory.\n";

  *env << "Each file's type is inferred from its name suffix:\n";
  *env << "\t\".264\" => a H.264 Video Elementary Stream file\n";
  *env << "\t\".265\" => a H.265 Video Elementary Stream file\n";
  *env << "\t\".aac\" => an AAC Audio (ADTS format) file\n";
  *env << "\t\".ac3\" => an AC-3 Audio file\n";
  *env << "\t\".amr\" => an AMR Audio file\n";
  *env << "\t\".dv\" => a DV Video file\n";
  *env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n";
  *env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n";
  *env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n";
  *env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n";
  *env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n";
  *env << "\t\".ts\" => a MPEG Transport Stream file\n";
  *env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n";
  *env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n";
  *env << "\t\".wav\" => a WAV Audio file\n";
  *env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n";
  *env << "See http://www.live555.com/mediaServer/ for additional documentation.\n";

  // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
  // Try first with the default HTTP port (80), and then with the alternative HTTP
  // port numbers (8000 and 8080).

  if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
    *env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling).)\n";
  } else {
    *env << "(RTSP-over-HTTP tunneling is not available.)\n";
  }

  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}

main 函数中:1)new 一个DynamicRTSPServer类:DynamicRTSPServer 改类继承RTSPServer

2)setUpTunnelingOverHTTP,监听来自80等端口

Boolean RTSPServer::setUpTunnelingOverHTTP(Port httpPort) {
  fHTTPServerSocketIPv4 = setUpOurSocket(envir(), httpPort, AF_INET);
  fHTTPServerSocketIPv6 = setUpOurSocket(envir(), httpPort, AF_INET6);
  if (fHTTPServerSocketIPv4 >= 0 || fHTTPServerSocketIPv6 >= 0) {
    fHTTPServerPort = httpPort;
    envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocketIPv4,
							 incomingConnectionHandlerHTTPIPv4, this);
    envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocketIPv6,
							 incomingConnectionHandlerHTTPIPv6, this);
    return True;
  }
  
  return False;
}

setUpTunnelingOverHTTP  函数一个任务,一直调用incomingConnectionHandlerHTTPIPv4或者

incomingConnectionHandlerHTTPIPv6

void RTSPServer::incomingConnectionHandlerHTTPIPv4(void* instance, int /*mask*/) {
  RTSPServer* server = (RTSPServer*)instance;
  server->incomingConnectionHandlerHTTPIPv4();
}
void RTSPServer::incomingConnectionHandlerHTTPIPv4() {
  incomingConnectionHandlerOnSocket(fHTTPServerSocketIPv4);
}
void RTSPServer::incomingConnectionHandlerHTTPIPv6(void* instance, int /*mask*/) {
  RTSPServer* server = (RTSPServer*)instance;
  server->incomingConnectionHandlerHTTPIPv6();
}
void RTSPServer::incomingConnectionHandlerHTTPIPv6() {
  incomingConnectionHandlerOnSocket(fHTTPServerSocketIPv6);
}

incomingConnectionHandlerHTTPIPv4 调用incomingConnectionHandlerOnSocket函数

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
  struct sockaddr_storage clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
  int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
  if (clientSocket < 0) {
    int err = envir().getErrno();
    if (err != EWOULDBLOCK) {
      envir().setResultErrMsg("accept() failed: ");
    }
    return;
  }
  ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill us
  makeSocketNonBlocking(clientSocket);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);
  
#ifdef DEBUG
  envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif
  
  // Create a new object for handling this connection:
  (void)createNewClientConnection(clientSocket, clientAddr);
}

调用accept 函数,有网络请求,就调用createNewClientConnection创建RTSPClientConnection

GenericMediaServer::ClientConnection*
RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_storage const& clientAddr) {
  return new RTSPClientConnection(*this, clientSocket, clientAddr, fOurConnectionsUseTLS);
}
RTSPServer::RTSPClientConnection
::RTSPClientConnection(RTSPServer& ourServer,
		       int clientSocket, struct sockaddr_storage const& clientAddr,
		       Boolean useTLS)
  : GenericMediaServer::ClientConnection(ourServer, clientSocket, clientAddr, useTLS),
    fOurRTSPServer(ourServer), fClientInputSocket(fOurSocket), fClientOutputSocket(fOurSocket),
    fAddressFamily(clientAddr.ss_family),
    fIsActive(True), fRecursionCount(0), fOurSessionCookie(NULL), fScheduledDelayedTask(0) {
  resetRequestBuffer();
}

RTSPClientConnection 类构造函数:调用GenericMediaServer::ClientConnection(ourServer, clientSocket, clientAddr, useTLS);


GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer,
		   int clientSocket, struct sockaddr_storage const& clientAddr,
		   Boolean useTLS)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr), fTLS(envir()) {
  // Add ourself to our 'client connections' table:
  fOurServer.fClientConnections->Add((char const*)this, this);
  
  if (useTLS) {
    // Perform extra processing to handle a TLS connection:
    fTLS.setCertificateAndPrivateKeyFileNames(ourServer.fTLSCertificateFileName,
					      ourServer.fTLSPrivateKeyFileName);
    fTLS.isNeeded = True;

    fTLS.tlsAcceptIsNeeded = True; // call fTLS.accept() the next time the socket is readable
  }

  // Arrange to handle incoming requests:
  resetRequestBuffer();
  envir().taskScheduler()
    .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}

ClientConnection 构造函数:起一个任务一直调用incomingRequestHandler 函数

void GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {
  ClientConnection* connection = (ClientConnection*)instance;
  connection->incomingRequestHandler();
}

void GenericMediaServer::ClientConnection::incomingRequestHandler() {
  if (fTLS.tlsAcceptIsNeeded) { // we need to successfully call fTLS.accept() first:
    if (fTLS.accept(fOurSocket) <= 0) return; // either an error, or we need to try again later

    fTLS.tlsAcceptIsNeeded = False;
    // We can now read data, as usual:
  }

  int bytesRead;
  if (fTLS.isNeeded) {
    bytesRead = fTLS.read(&fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft);
  } else {
    struct sockaddr_storage dummy; // 'from' address, meaningless in this case
  
    bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  }
  handleRequestBytes(bytesRead);
}

  incomingRequestHandler  函数调用handleRequestBytes 函数

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
  int numBytesRemaining = 0;
  ++fRecursionCount;
  
  do {
    RTSPServer::RTSPClientSession* clientSession = NULL;

    if (newBytesRead < 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
      // Either the client socket has died, or the request was too big for us.
      // Terminate this connection:
#ifdef DEBUG
      fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
#endif
      fIsActive = False;
      break;
    }
    
    Boolean endOfMsg = False;
    unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
#ifdef DEBUG
    ptr[newBytesRead] = '\0';
    fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() %s %d new bytes:%s\n",
	    this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
#endif
    
    if (fClientOutputSocket != fClientInputSocket && numBytesRemaining == 0) {
      // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
      // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes).
      
      // But first, we remove any whitespace that may be in the input data:
      unsigned toIndex = 0;
      for (int fromIndex = 0; fromIndex < newBytesRead; ++fromIndex) {
	char c = ptr[fromIndex];
	if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) { // not 'whitespace': space,tab,CR,NL
	  ptr[toIndex++] = c;
	}
      }
      newBytesRead = toIndex;
      
      unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
      unsigned newBase64RemainderCount = numBytesToDecode%4;
      numBytesToDecode -= newBase64RemainderCount;
      if (numBytesToDecode > 0) {
	ptr[newBytesRead] = '\0';
	unsigned decodedSize;
	unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount), numBytesToDecode, decodedSize);
#ifdef DEBUG
	fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
	for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
	fprintf(stderr, "\n");
#endif
	
	// Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
	unsigned char* to = ptr-fBase64RemainderCount;
	for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
	
	// Then copy any remaining (undecoded) bytes to the end:
	for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
	
	newBytesRead = decodedSize - fBase64RemainderCount + newBase64RemainderCount;
	  // adjust to allow for the size of the new decoded data (+ remainder)
	delete[] decodedBytes;
      }
      fBase64RemainderCount = newBase64RemainderCount;
    }
    
    unsigned char* tmpPtr = fLastCRLF + 2;
    if (fBase64RemainderCount == 0) { // no more Base-64 bytes remain to be read/decoded
      // Look for the end of the message: <CR><LF><CR><LF>
      if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
      while (tmpPtr < &ptr[newBytesRead-1]) {
	if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
	  if (tmpPtr - fLastCRLF == 2) { // This is it:
	    endOfMsg = True;
	    break;
	  }
	  fLastCRLF = tmpPtr;
	}
	++tmpPtr;
      }
    }
    
    fRequestBufferBytesLeft -= newBytesRead;
    fRequestBytesAlreadySeen += newBytesRead;
    
    if (!endOfMsg) break; // subsequent reads will be needed to complete the request
    
    // Parse the request string into command name and 'CSeq', then handle the command:
    fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
    char cmdName[RTSP_PARAM_STRING_MAX];
    char urlPreSuffix[RTSP_PARAM_STRING_MAX];
    char urlSuffix[RTSP_PARAM_STRING_MAX];
    char cseq[RTSP_PARAM_STRING_MAX];
    char sessionIdStr[RTSP_PARAM_STRING_MAX];
    unsigned contentLength = 0;
    Boolean urlIsRTSPS;
    Boolean playAfterSetup = False;
    fLastCRLF[2] = '\0'; // temporarily, for parsing
    Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,
						    cmdName, sizeof cmdName,
						    urlPreSuffix, sizeof urlPreSuffix,
						    urlSuffix, sizeof urlSuffix,
						    cseq, sizeof cseq,
						    sessionIdStr, sizeof sessionIdStr,
						    contentLength, urlIsRTSPS);
    fLastCRLF[2] = '\r'; // restore its value
    // Check first for a bogus "Content-Length" value that would cause a pointer wraparound:
    if (tmpPtr + 2 + contentLength < tmpPtr + 2) {
#ifdef DEBUG
      fprintf(stderr, "parseRTSPRequestString() returned a bogus \"Content-Length:\" value: 0x%x (%d)\n", contentLength, (int)contentLength);
#endif
      contentLength = 0;
      parseSucceeded = False;
    }
    if (parseSucceeded) {
#ifdef DEBUG
      fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
#endif
      // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
      if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us 
      
      // If the request included a "Session:" id, and it refers to a client session that's
      // current ongoing, then use this command to indicate 'liveness' on that client session:
      Boolean const requestIncludedSessionId = sessionIdStr[0] != '\0';
      if (requestIncludedSessionId) {
	clientSession
	  = (RTSPServer::RTSPClientSession*)(fOurRTSPServer.lookupClientSession(sessionIdStr));
	if (clientSession != NULL) clientSession->noteLiveness();
      }
    
      // We now have a complete RTSP request.
      // Handle the specified command (beginning with commands that are session-independent):
      fCurrentCSeq = cseq;

      // If the request specified the wrong type of URL
      // (i.e., "rtsps" instead of "rtsp", or vice versa), then send back a 'redirect':
      if (urlIsRTSPS != fOurRTSPServer.fWeServeSRTP) {
#ifdef DEBUG
	fprintf(stderr, "Calling handleCmd_redirect()\n");
#endif
	handleCmd_redirect(urlSuffix);
      } else if (strcmp(cmdName, "OPTIONS") == 0) {
	// If the "OPTIONS" command included a "Session:" id for a session that doesn't exist,
	// then treat this as an error:
	if (requestIncludedSessionId && clientSession == NULL) {
#ifdef DEBUG
	  fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 1)\n");
#endif
	  handleCmd_sessionNotFound();
	} else {
	  // Normal case:
	  handleCmd_OPTIONS();
	}
      } else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {
	// The special "*" URL means: an operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:
	if (strcmp(cmdName, "GET_PARAMETER") == 0) {
	  handleCmd_GET_PARAMETER((char const*)fRequestBuffer);
	} else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
	  handleCmd_SET_PARAMETER((char const*)fRequestBuffer);
	} else {
	  handleCmd_notSupported();
	}
      } else if (strcmp(cmdName, "DESCRIBE") == 0) {
	handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
      } else if (strcmp(cmdName, "SETUP") == 0) {
	Boolean areAuthenticated = True;

	if (!requestIncludedSessionId) {
	  // No session id was present in the request.
	  // So create a new "RTSPClientSession" object for this request.

	  // But first, make sure that we're authenticated to perform this command:
	  char urlTotalSuffix[2*RTSP_PARAM_STRING_MAX];
	      // enough space for urlPreSuffix/urlSuffix'\0'
	  urlTotalSuffix[0] = '\0';
	  if (urlPreSuffix[0] != '\0') {
	    strcat(urlTotalSuffix, urlPreSuffix);
	    strcat(urlTotalSuffix, "/");
	  }
	  strcat(urlTotalSuffix, urlSuffix);
	  if (authenticationOK("SETUP", urlTotalSuffix, (char const*)fRequestBuffer)) {
	    clientSession
	      = (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
	  } else {
	    areAuthenticated = False;
	  }
	}
	if (clientSession != NULL) {
	  clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
	  playAfterSetup = clientSession->fStreamAfterSETUP;
	} else if (areAuthenticated) {
#ifdef DEBUG
	  fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 2)\n");
#endif
	  handleCmd_sessionNotFound();
	}
      } else if (strcmp(cmdName, "TEARDOWN") == 0
		 || strcmp(cmdName, "PLAY") == 0
		 || strcmp(cmdName, "PAUSE") == 0
		 || strcmp(cmdName, "GET_PARAMETER") == 0
		 || strcmp(cmdName, "SET_PARAMETER") == 0) {
	if (clientSession != NULL) {
	  clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
	} else {
#ifdef DEBUG
	  fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 3)\n");
#endif
	  handleCmd_sessionNotFound();
	}
      } else if (strcmp(cmdName, "REGISTER") == 0 || strcmp(cmdName, "DEREGISTER") == 0) {
	// Because - unlike other commands - an implementation of this command needs
	// the entire URL, we re-parse the command to get it:
	char* url = strDupSize((char*)fRequestBuffer);
	if (sscanf((char*)fRequestBuffer, "%*s %s", url) == 1) {
	  // Check for special command-specific parameters in a "Transport:" header:
	  Boolean reuseConnection, deliverViaTCP;
	  char* proxyURLSuffix;
	  parseTransportHeaderForREGISTER((const char*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);

	  handleCmd_REGISTER(cmdName, url, urlSuffix, (char const*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
	  delete[] proxyURLSuffix;
	} else {
	  handleCmd_bad();
	}
	delete[] url;
      } else {
	// The command is one that we don't handle:
	handleCmd_notSupported();
      }
    } else {
#ifdef DEBUG
      fprintf(stderr, "parseRTSPRequestString() failed; checking now for HTTP commands (for RTSP-over-HTTP tunneling)...\n");
#endif
      // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
      char sessionCookie[RTSP_PARAM_STRING_MAX];
      char acceptStr[RTSP_PARAM_STRING_MAX];
      *fLastCRLF = '\0'; // temporarily, for parsing
      parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
					      urlSuffix, sizeof urlPreSuffix,
					      sessionCookie, sizeof sessionCookie,
					      acceptStr, sizeof acceptStr);
      *fLastCRLF = '\r';
      if (parseSucceeded) {
#ifdef DEBUG
	fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
#endif
	// Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
	Boolean isValidHTTPCmd = True;
	if (strcmp(cmdName, "OPTIONS") == 0) {
	  handleHTTPCmd_OPTIONS();
	} else if (sessionCookie[0] == '\0') {
	  // There was no "x-sessioncookie:" header.  If there was an "Accept: application/x-rtsp-tunnelled" header,
	  // then this is a bad tunneling request.  Otherwise, assume that it's an attempt to access the stream via HTTP.
	  if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
	    isValidHTTPCmd = False;
	  } else {
	    handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
	  }
	} else if (strcmp(cmdName, "GET") == 0) {
	  handleHTTPCmd_TunnelingGET(sessionCookie);
	} else if (strcmp(cmdName, "POST") == 0) {
	  // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
	  // Check for this, and handle it if it exists:
	  unsigned char const* extraData = fLastCRLF+4;
	  unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
	  if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
	    // We don't respond to the "POST" command, and we go away:
	    fIsActive = False;
	    break;
	  }
	} else {
	  isValidHTTPCmd = False;
	}
	if (!isValidHTTPCmd) {
	  handleHTTPCmd_notSupported();
	}
      } else {
#ifdef DEBUG
	fprintf(stderr, "parseHTTPRequestString() failed!\n");
#endif
	handleCmd_bad();
      }
    }
    
#ifdef DEBUG
    fprintf(stderr, "sending response: %s", fResponseBuffer);
#endif
    unsigned const numBytesToWrite = strlen((char*)fResponseBuffer);
    if (fTLS.isNeeded) {
        fTLS.write((char const*)fResponseBuffer, numBytesToWrite);
    } else {
        send(fClientOutputSocket, (char const*)fResponseBuffer, numBytesToWrite, 0);
   }
    
    if (playAfterSetup) {
      // The client has asked for streaming to commence now, rather than after a
      // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
      clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
    }
    
    // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
    // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
    unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
    numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
    resetRequestBuffer(); // to prepare for any subsequent request
    
    if (numBytesRemaining > 0) {
      memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
      newBytesRead = numBytesRemaining;
    }
  } while (numBytesRemaining > 0);
  
  --fRecursionCount;
  // If it has a scheduledDelayedTask, don't delete the instance or close the sockets. The sockets can be reused in the task.
  if (!fIsActive && fScheduledDelayedTask <= 0) {
    if (fRecursionCount > 0) closeSockets(); else delete this;
    // Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively
    // while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).
    // In such a case we don't want to actually delete ourself until we leave the outermost call.
  }
}

handleRequestBytes 处理rtsp 信令流程。

信息令抓包数据

OPTIONS rtsp://192.168.31.188:8000/slamtv60.264 RTSP/1.0

CSeq: 2

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

RTSP/1.0 200 OK

CSeq: 2

Date: Sat, Sep 02 2017 07:28:23 GMT

Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

DESCRIBE rtsp://192.168.31.188:8000/slamtv60.264 RTSP/1.0

CSeq: 3

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Accept: application/sdp

RTSP/1.0 200 OK

CSeq: 3

Date: Sat, Sep 02 2017 07:28:23 GMT

Content-Base: rtsp://192.168.31.188:8554/slamtv60.264/

Content-Type: application/sdp

Content-Length: 526

v=0

o=- 1504337303775687 1 IN IP4 192.168.31.188

s=H.264 Video, streamed by the LIVE555 Media Server

i=slamtv60.264

t=0 0

a=tool:LIVE555 Streaming Media v2017.07.18

a=type:broadcast

a=control:*

a=range:npt=0-

a=x-qt-text-nam:H.264 Video, streamed by the LIVE555 Media Server

a=x-qt-text-inf:slamtv60.264

m=video 0 RTP/AVP 96

c=IN IP4 0.0.0.0

b=AS:500

a=rtpmap:96 H264/90000

a=fmtp:96 packetization-mode=1;profile-level-id=4D4033;sprop-parameter-sets=Z01AM5JUDAS0IAAAAwBAAAAM0eMGVA==,aO48gA==

a=control:track1

SETUP rtsp://192.168.31.188:8554/slamtv60.264/track1 RTSP/1.0

CSeq: 4

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Transport: RTP/AVP;unicast;client_port=49484-49485

RTSP/1.0 200 OK

CSeq: 4

Date: Sat, Sep 02 2017 07:28:23 GMT

Transport: RTP/AVP;unicast;destination=192.168.31.146;source=192.168.31.188;client_port=49484-49485;server_port=6970-6971

Session: EF285CD9;timeout=65

PLAY rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 5

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

Range: npt=0.000-

RTSP/1.0 200 OK

CSeq: 5

Date: Sat, Sep 02 2017 07:28:23 GMT

Range: npt=0.000-

Session: EF285CD9

RTP-Info: url=rtsp://192.168.31.188:8554/slamtv60.264/track1;seq=7267;rtptime=3188764371

GET_PARAMETER rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 6

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

RTSP/1.0 200 OK

CSeq: 6

Date: Sat, Sep 02 2017 07:28:23 GMT

Session: EF285CD9

Content-Length: 10

TEARDOWN rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 7

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

RTSP/1.0 200 OK

CSeq: 7

Date: Sat, Sep 02 2017 07:28:34 GMT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值