handleCmd_SETUP 函数

handleCmd_SETUP函数,会在客户端发送setup时调用,如果有多个subsession客户端会每个都会发送一个setup的,主要就是创建rtp/rtcp,还有创建source.具体看代码中的注释。


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.
	char const* streamName = urlPreSuffix; // in the normal case
	char const* trackId = urlSuffix; // in the normal case
	char* concatenatedStreamName = NULL; // in the normal case

	//操作一次,更新一下时间到最新状态,默认65,如果65秒没有调用,表示连接以及失效,会删除此session
	noteLiveness();
	do
	{
		//查找媒体流,一般都在初始化时创建,或者在describe中创建的
		// First, make sure the specified stream name exists:
		ServerMediaSession* sms = fOurServer.lookupServerMediaSession(streamName);
		if (sms == NULL)
		{
			// Check for the special case (noted above), before we give up:
			if (urlPreSuffix[0] == '\0')
			{
				streamName = urlSuffix;
			}
			else
			{
				concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'
				sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);
				streamName = concatenatedStreamName;
			}
			trackId = NULL;

			// Check again:
			sms = fOurServer.lookupServerMediaSession(streamName);
		}
		if (sms == NULL)
		{
			if (fOurServerMediaSession == NULL)
			{
				// The client asked for a stream that doesn't exist (and this session descriptor has not been used before):
				ourClientConnection->handleCmd_notFound();
			}
			else
			{
				// The client asked for a stream that doesn't exist, but using a stream id for a stream that does exist. Bad request:
				ourClientConnection->handleCmd_bad();
			}
			break;
		}
		else
		{
			if (fOurServerMediaSession == NULL)
			{
				// We're accessing the "ServerMediaSession" for the first time.
				fOurServerMediaSession = sms;
				fOurServerMediaSession->incrementReferenceCount();
			}
			else if (sms != fOurServerMediaSession)
			{
				// The client asked for a stream that's different from the one originally requested for this stream id.  Bad request:
				ourClientConnection->handleCmd_bad();
				break;
			}
		}

		//第一次fStreamStates 是NULL
		if (fStreamStates == NULL)
		{
			//计算有几个子sub session 就创建几个streamState
			// This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
			ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
			for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)

			fStreamStates = new struct streamState[fNumStreamStates];

			iter.reset();

			//fStreamStates[i].subsession 赋值为 各个subsession
			ServerMediaSubsession* subsession;
			for (unsigned i = 0; i < fNumStreamStates; ++i)
			{
				subsession = iter.next();
				fStreamStates[i].subsession = subsession;
				fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
			}
		}

		// Look up information for the specified subsession (track):
		ServerMediaSubsession* subsession = NULL;
		unsigned streamNum;
		//如果trackid 不是空字符,找到对应的子session
		if (trackId != NULL && trackId[0] != '\0')   // normal case
		{
			for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum)
			{
				subsession = fStreamStates[streamNum].subsession;
				if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
			}
			if (streamNum >= fNumStreamStates)
			{
				// The specified track id doesn't exist, so this request fails:
				ourClientConnection->handleCmd_notFound();
				break;
			}
		}
		else
		{
			//trackid 为NULL 或者空字符

			// Weird case: there was no track id in the URL.
			// This works only if we have only one subsession:
			//如果子任务为多个,或者没有子任务,回复命令错误
			if (fNumStreamStates != 1 || fStreamStates[0].subsession == NULL)
			{
				ourClientConnection->handleCmd_bad();
				break;
			}

			//默认设置为第一个subsession
			streamNum = 0;
			subsession = fStreamStates[streamNum].subsession;
		}
		// ASSERT: subsession != NULL

		//解析setup 中的Transport 信息,使用UDP还是TCP,rtp/rtcp端口,
		// Look for a "Transport:" header in the request string, to extract client parameters:
		StreamingMode streamingMode;
		char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
		char* clientsDestinationAddressStr;
		u_int8_t clientsDestinationTTL;
		portNumBits clientRTPPortNum, clientRTCPPortNum;
		unsigned char rtpChannelId, rtcpChannelId;
		parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
			clientsDestinationAddressStr, clientsDestinationTTL,
			clientRTPPortNum, clientRTCPPortNum,
			rtpChannelId, rtcpChannelId);
		if ((streamingMode == RTP_TCP && rtpChannelId == 0xFF) ||
			(streamingMode != RTP_TCP && ourClientConnection->fClientOutputSocket != ourClientConnection->fClientInputSocket))
		{
			// An anomolous situation, caused by a buggy client.  Either:
			//     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or
			//     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).
			// In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:
			streamingMode = RTP_TCP;
			rtpChannelId = fTCPStreamIdCount;
			rtcpChannelId = fTCPStreamIdCount + 1;
		}
		if (streamingMode == RTP_TCP) fTCPStreamIdCount += 2;


		//客户端的rtp/rtcp端口
		Port clientRTPPort(clientRTPPortNum);
		Port clientRTCPPort(clientRTCPPortNum);

		//播放开始时间
		// Next, check whether a "Range:" or "x-playNow:" header is present in the request.
		// This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
		double rangeStart = 0.0, rangeEnd = 0.0;
		char* absStart = NULL;
		char* absEnd = NULL;
		if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd))
		{
			delete[] absStart;
			delete[] absEnd;
			fStreamAfterSETUP = True;
		}
		else if (parsePlayNowHeader(fullRequestStr))
		{
			fStreamAfterSETUP = True;
		}
		else
		{
			fStreamAfterSETUP = False;
		}

		// Then, get server parameters from the 'subsession':
		int tcpSocketNum = streamingMode == RTP_TCP ? ourClientConnection->fClientOutputSocket : -1;
		netAddressBits destinationAddress = 0;
		u_int8_t destinationTTL = 255;
#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
		if (clientsDestinationAddressStr != NULL)
		{
			// Use the client-provided "destination" address.
			// Note: This potentially allows the server to be used in denial-of-service
			// attacks, so don't enable this code unless you're sure that clients are
			// trusted.
			destinationAddress = our_inet_addr(clientsDestinationAddressStr);
		}
		// Also use the client-provided TTL.
		destinationTTL = clientsDestinationTTL;
#endif
		delete[] clientsDestinationAddressStr;
		Port serverRTPPort(0);
		Port serverRTCPPort(0);

		// Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):
		struct sockaddr_in sourceAddr;
		SOCKLEN_T namelen = sizeof sourceAddr;
		getsockname(ourClientConnection->fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);
		netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;
		netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;
		// NOTE: The following might not work properly, so we ifdef it out for now:
#ifdef HACK_FOR_MULTIHOMED_SERVERS
		ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;
#endif

		//子媒体任务获取参数
		subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
			clientRTPPort, clientRTCPPort,
			tcpSocketNum, rtpChannelId, rtcpChannelId,
			destinationAddress, destinationTTL, fIsMulticast,
			serverRTPPort, serverRTCPPort,
			fStreamStates[streamNum].streamToken);

		SendingInterfaceAddr = origSendingInterfaceAddr;
		ReceivingInterfaceAddr = origReceivingInterfaceAddr;

		//组合rtsp包
		AddressString destAddrStr(destinationAddress);
		AddressString sourceAddrStr(sourceAddr);
		if (fIsMulticast)
		{
			switch (streamingMode)
			{
			case RTP_UDP:
				snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
					"RTSP/1.0 200 OK\r\n"
					"CSeq: %s\r\n"
					"%s"
					"Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
					"Session: %08X\r\n\r\n",
					ourClientConnection->fCurrentCSeq,
					dateHeader(),
					destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
					fOurSessionId);
				break;
			case RTP_TCP:
				// multicast streams can't be sent via TCP
				ourClientConnection->handleCmd_unsupportedTransport();
				break;
			case RAW_UDP:
				snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
					"RTSP/1.0 200 OK\r\n"
					"CSeq: %s\r\n"
					"%s"
					"Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
					"Session: %08X\r\n\r\n",
					ourClientConnection->fCurrentCSeq,
					dateHeader(),
					streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), destinationTTL,
					fOurSessionId);
				break;
			}
		}
		else
		{
			switch (streamingMode)
			{
			case RTP_UDP:
			{
							snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
								"RTSP/1.0 200 OK\r\n"
								"CSeq: %s\r\n"
								"%s"
								"Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
								"Session: %08X\r\n\r\n",
								ourClientConnection->fCurrentCSeq,
								dateHeader(),
								destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
								fOurSessionId);
							break;
			}
			case RTP_TCP:
			{
							snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
								"RTSP/1.0 200 OK\r\n"
								"CSeq: %s\r\n"
								"%s"
								"Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
								"Session: %08X\r\n\r\n",
								ourClientConnection->fCurrentCSeq,
								dateHeader(),
								destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
								fOurSessionId);
							break;
			}
			case RAW_UDP:
			{
							snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
								"RTSP/1.0 200 OK\r\n"
								"CSeq: %s\r\n"
								"%s"
								"Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
								"Session: %08X\r\n\r\n",
								ourClientConnection->fCurrentCSeq,
								dateHeader(),
								streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
								fOurSessionId);
							break;
			}
			}
		}
		delete[] streamingModeString;
	} while (0);

	delete[] concatenatedStreamName;
}


void OnDemandServerMediaSubsession
::getStreamParameters(unsigned clientSessionId,
		      netAddressBits clientAddress,
		      Port const& clientRTPPort,
		      Port const& clientRTCPPort,
		      int tcpSocketNum,
		      unsigned char rtpChannelId,
		      unsigned char rtcpChannelId,
		      netAddressBits& destinationAddress,
		      u_int8_t& /*destinationTTL*/,
		      Boolean& isMulticast,
		      Port& serverRTPPort,
		      Port& serverRTCPPort,
		      void*& streamToken,
		      unsigned streamNum,
		      char const *streamName) 
{
	if (destinationAddress == 0) destinationAddress = clientAddress;
	struct in_addr destinationAddr; 
	destinationAddr.s_addr = destinationAddress;
	isMulticast = False;

	// Special case: Rather than creating a new 'StreamState',
	// we reuse the one that we've already created:

	//重复使用第一个source,如果是直播话可以使用fReuseFirstSource = true,在创建subsession时设置
	if (fLastStreamToken != NULL && fReuseFirstSource)//video
	{
		serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort();
		serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort();
		++((StreamState*)fLastStreamToken)->referenceCount();
		streamToken = fLastStreamToken;
	}
	else 
	{
		//正常情况,创建一个新的media source
		// Normal case: Create a new media source:
		unsigned streamBitrate;
		FramedSource* mediaSource
			= createNewStreamSource(clientSessionId, streamBitrate);

		// Create 'groupsock' and 'sink' objects for the destination,
		// using previously unused server port numbers:
		RTPSink* rtpSink;
		BasicUDPSink* udpSink;
		Groupsock* rtpGroupsock;
		Groupsock* rtcpGroupsock;
		portNumBits serverPortNum;

		//如果没有设置客户端的rtcpport
		if (clientRTCPPort.num() == 0) 
		{
			// We're streaming raw UDP (not RTP). Create a single groupsock:
			NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
			for (serverPortNum = fInitialPortNum;; ++serverPortNum) 
			{
				struct in_addr dummyAddr; dummyAddr.s_addr = 0;

				serverRTPPort = serverPortNum;
				rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
				if (rtpGroupsock->socketNum() >= 0) break; // success
			}

			rtcpGroupsock = NULL;
			rtpSink = NULL;
			udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);
		}
		else 
		{
			//服务端默认端口fInitialPortNum=6970开始 rtp和rtcp 成对出现,rtcp= rtp+1, 所以serverPortNum += 2
			// Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of
			// groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):
			NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
			for (portNumBits serverPortNum = fInitialPortNum;; serverPortNum += 2) 
			{
				struct in_addr dummyAddr; dummyAddr.s_addr = 0;

				serverRTPPort = serverPortNum;
				rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
				if (rtpGroupsock->socketNum() < 0) 
				{
					delete rtpGroupsock;
					continue; // try again
				}
				
				serverRTCPPort = serverPortNum + 1;
				rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255);
				if (rtcpGroupsock->socketNum() < 0) 
				{
					delete rtpGroupsock;
					delete rtcpGroupsock;
					continue; // try again
				}

				break; // success
			}

			//创建rtpSink,
			unsigned char rtpPayloadType = 96 + trackNumber() - 1; // if dynamic
			rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
			udpSink = NULL;
		}

		// Turn off the destinations for each groupsock.  They'll get set later
		// (unless TCP is used instead):
		if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();
		if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();

		if (rtpGroupsock != NULL) 
		{
			// Try to use a big send buffer for RTP -  at least 0.1 second of
			// specified bandwidth and at least 50 KB
			unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
			if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;
			//发送缓存区大小最小为50K
			increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize);
		}

		//StreamState,包含各种流信息
		streamToken = fLastStreamToken
			= new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,
			streamBitrate, mediaSource,
			rtpGroupsock, rtcpGroupsock);

	}

	// Record these destinations as being for this client session id:
	//增加目的信息到hashtable
	Destinations* destinations;
	if (tcpSocketNum < 0)
	{ 
		// UDP
		destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);
	}
	else
	{
		// TCP
		destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);
	}
	fDestinationsHashTable->Add((char const*)clientSessionId, destinations);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值