6 live555 source code analysis (6) - live555 RTSP workflow (3)

The inheritance relationship of live555 is relatively complicated, so make a picture to record the types of key variables in the process of sending h264 files.
insert image description here
The previous article analyzed the operation process of OPTION and DESCRIBE from the source code. This article analyzes the remaining SETUP and PLAY operation process.

1. SETUP

	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;
	} 

When executing the SETUP command, a clientsession will be created first, and then the ClientSession will be asked to perform the operation corresponding to SETUP.

void RTSPServer::RTSPClientSession
::handleCmd_SETUP(RTSPServer::RTSPClientConnection* ourClientConnection,
		  char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
    
    
  // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.
  // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),
  // in the special case where we have only a single track.  I.e., in this case, we also handle:
  //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or
  //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.
  fOurClientConnection = ourClientConnection;
  fURLPreSuffix = urlPreSuffix; fURLSuffix = urlSuffix; fFullRequestStr = fullRequestStr;
  fTrackId = urlSuffix; // in the normal case

  // Begin by checking whether the specified stream name exists:
  char const* streamName = urlPreSuffix; // in the normal case
  fOurServer.lookupServerMediaSession(streamName, SETUPLookupCompletionFunction1, this,
				      fOurServerMediaSession == NULL);
}

The old routine, first find the corresponding mediasession and then execute SETUPLookupCompletionFunction1. After finding it, it will run to handleCmd_SETUP_afterLookup2.

    if (fStreamStates == NULL) {
    
    
      // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
      fNumStreamStates = fOurServerMediaSession->numSubsessions();
      fStreamStates = new struct streamState[fNumStreamStates];
      
      ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
      ServerMediaSubsession* subsession;
      for (unsigned i = 0; i < fNumStreamStates; ++i) {
    
    
	subsession = iter.next();
	fStreamStates[i].subsession = subsession;
	fStreamStates[i].tcpSocketNum = -1; // for now; may get set for RTP-over-TCP streaming
	fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
      }

Then a fStreamStates will be created and initialized. After doing some configuration later, execute to

 subsession->getStreamParameters(fOurSessionId, fOurClientConnection->fClientAddr,
				    clientRTPPort, clientRTCPPort,
				    fStreamStates[trackNum].tcpSocketNum, rtpChannelId, rtcpChannelId,
				    destinationAddress, destinationTTL, fIsMulticast,
				    serverRTPPort, serverRTCPPort,
				    fStreamStates[trackNum].streamToken);
    FramedSource* mediaSource
      = createNewStreamSource(clientSessionId, streamBitrate);
...
    rtpGroupsock = createGroupsock(nullAddress(destinationAddress.ss_family), serverRTPPort);
...
    rtcpGroupsock = createGroupsock(nullAddress(destinationAddress.ss_family), serverRTCPPort);
...
	rtpSink = mediaSource == NULL ? NULL
	  : createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
...
	streamToken = fLastStreamToken
      = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,
			streamBitrate, mediaSource,
			rtpGroupsock, rtcpGroupsock);

When getStreamParameters, a mediaSource will be created and an rtpSink will be created, which is the same as the previous steps for simulating RTPSink. The difference is that rtpGroupsock and rtpGroupsock are configured with real IP addresses and ports.
Then create a streamToken with mediaSource and rtpSink as parameters. This steamToken is used as a unique identifier to identify the sub-session corresponding to the current client session, which actually corresponds to our h264 file stream session. Stored in fStreamStates[0]. And determine whether to use TCP or UDP as the sending protocol of RTP, and save the communication information of the client.
This completes the processing of SETUP.

2. PLAY

After SETUP, there will be clientSession. Then when the PLAY command is received, the corresponding clientSession will execute the PLAY command.
The same is also to find the corresponding subsession to process according to the url.

    handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);

After doing some configuration in handleCmd_Play, come to the startStream function.

void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,
						void* streamToken,
						TaskFunc* rtcpRRHandler,
						void* rtcpRRHandlerClientData,
						unsigned short& rtpSeqNum,
						unsigned& rtpTimestamp,
						ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
						void* serverRequestAlternativeByteHandlerClientData) {
    
    
  StreamState* streamState = (StreamState*)streamToken;
  Destinations* destinations
    = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));
  if (streamState != NULL) {
    
    
    streamState->startPlaying(destinations, clientSessionId,
			      rtcpRRHandler, rtcpRRHandlerClientData,
			      serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
    RTPSink* rtpSink = streamState->rtpSink(); // alias
    if (rtpSink != NULL) {
    
    
      rtpSeqNum = rtpSink->currentSeqNo();
      rtpTimestamp = rtpSink->presetNextTimestamp();
    }
  }
}					      

Then execute to streamState->startPlaying

    if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort, clientSessionId);
    if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && dests->rtcpPort.num() == dests->rtpPort.num())) {
    
    
      fRTCPgs->addDestination(dests->addr, dests->rtcpPort, clientSessionId);
    }
    ...
    ...
    if (fRTPSink != NULL) {
    
    
      fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
      fAreCurrentlyPlaying = True;
    } 
     

After adding and updating the client's RTP and RTCP addresses and ports in the startPlaying function, execute fRTPSink->startPlaying
, and the process of starting the simulated RTPSink in DESCRIBE in the previous article is the same. In this way, the workflow of live555RTSP is roughly analyzed.

Guess you like

Origin blog.csdn.net/qq_36383272/article/details/121014163