live555學習筆記6-建立RTP會話

六 建立RTP會話


首先更正一個概念:
ServerMediaSession原先說代表一個流,其實是不準確的。它代表的是server端的一個媒體的名字,而說ServerMediaSubsession代表一個Track是準確的。以後流指的是那些有數據流動的組合。


RTP的建立:
RTP的建立過程無非是這樣:client告訴server自己的rtp/rtcp端口號,server建立自己的rtp/rtcp socket,然後在收到PLAY請求時向客戶端發數據。看起來非常簡單。
在收到SETUP請求時才建立連接,讓我們看一下處理這個命令的函數:

void RTSPServer::RTSPClientSession::handleCmd_SETUP(
		char const* cseq,
		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


	do {
		// First, make sure the specified stream name exists:
		fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
		if (fOurServerMediaSession == NULL) {
			// Check for the special case (noted above), before we 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:
			fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
		}
		if (fOurServerMediaSession == NULL) {
			handleCmd_notFound(cseq);
			break;
		}


		fOurServerMediaSession->incrementReferenceCount();


		//爲一個流中所有的track都分配一個stream state
		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):
			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();
			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
			}
		}


		//查找當前請求的track的信息
		// Look up information for the specified subsession (track):
		ServerMediaSubsession* subsession = NULL;
		unsigned streamNum;
		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:
				handleCmd_notFound(cseq);
				break;
			}
		} else {
			// Weird case: there was no track id in the URL.
			// This works only if we have only one subsession:
			if (fNumStreamStates != 1) {
				handleCmd_bad(cseq);
				break;
			}
			streamNum = 0;
			subsession = fStreamStates[streamNum].subsession;
		}
		// ASSERT: subsession != NULL


		//分析RTSP請求字符串中的傳輸要求
		// 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	&&
				fClientOutputSocket != 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;
		}
		fTCPStreamIdCount += 2;


		Port clientRTPPort(clientRTPPortNum);
		Port clientRTCPPort(clientRTCPPortNum);


		// Next, check whether a "Range:" 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;
		fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart,
				rangeEnd) || parsePlayNowHeader(fullRequestStr);


		// Then, get server parameters from the 'subsession':
		int tcpSocketNum = streamingMode == RTP_TCP ? 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(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


		//獲取rtp連接信息,在其中已建立起了server端的rtp和rtcp socket,返回
		//fStreamStates[streamNum].streamToken表示數據流已經建立起來了
		subsession->getStreamParameters(fOurSessionId,
				fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort,
				tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress,
				destinationTTL, fIsMulticast, serverRTPPort, serverRTCPPort,
				fStreamStates[streamNum].streamToken);
		SendingInterfaceAddr = origSendingInterfaceAddr;
		ReceivingInterfaceAddr = origReceivingInterfaceAddr;


		//形成RTSP迴應字符串
		struct in_addr destinationAddr;
		destinationAddr.s_addr = destinationAddress;
		char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));
		char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));
		if (fIsMulticast) {
			switch (streamingMode) {
			case RTP_UDP:
				snprintf(
						(char*) fResponseBuffer,
						sizeof 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", cseq, dateHeader(),
						destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()),
						ntohs(serverRTCPPort.num()), destinationTTL,
						fOurSessionId);
				break;
			case RTP_TCP:
				// multicast streams can't be sent via TCP
				handleCmd_unsupportedTransport(cseq);
				break;
			case RAW_UDP:
				snprintf(
						(char*) fResponseBuffer,
						sizeof 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", cseq, dateHeader(),
						streamingModeString, destAddrStr, sourceAddrStr,
						ntohs(serverRTPPort.num()), destinationTTL,
						fOurSessionId);
				break;
			}
		} else {
			switch (streamingMode) {
			case RTP_UDP: {
				snprintf(
						(char*) fResponseBuffer,
						sizeof 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", cseq, dateHeader(),
						destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()),
						ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()),
						ntohs(serverRTCPPort.num()), fOurSessionId);
				break;
			}
			case RTP_TCP: {
				snprintf(
						(char*) fResponseBuffer,
						sizeof 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", cseq, dateHeader(),
						destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,
						fOurSessionId);
				break;
			}
			case RAW_UDP: {
				snprintf(
						(char*) fResponseBuffer,
						sizeof 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", cseq, dateHeader(),
						streamingModeString, destAddrStr, sourceAddrStr,
						ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
						fOurSessionId);
				break;
			}
			}
		}
		delete[] destAddrStr;
		delete[] sourceAddrStr;
		delete[] streamingModeString;
	} while (0);


	delete[] concatenatedStreamName;
	//返回後,迴應字符串會被立即發送
}

live555 中有兩個 streamstate,一個是類 StreamState ,一個是此處的結構 struct streamState。類 SteamState 就是 streamToken,而 struct streamState 中保存了 MediaSubsession (即track) 和類 StreamState 的對應。類 StreamState 代表一個真正流動起來的數據流。這個數據流是從源流到 Sink 。客戶端與服務端的一個 rtp 會話中,有兩個數據流,服務端是從 XXXFileSouce 流到 RTPSink,而客戶端則是從 RTPSource 流到 XXXFileSink 。建立數據流的過程就是把 Source 與 Sink 連接起來。
爲何不把 StreamToken 保存在 MediaSubsession 中呢?看起來在 struct streamState 中是一個 MediaSubsession 對應一個 streamToken 呀? 因爲 MediaSubsession  代表一個 track 的靜態數據,它是可以被其它 rtp 會話重用的。比如不同的用戶可能會連接到同一個媒體的同一個 track 。所以 streamToken 與 MediaSubsession 獨立存在,只是被 RTSPClientSession 給對應了起來。


streamToken的建立過程存在於函數subsession->getStreamParameters()中,讓我們看一下下:

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)
{
	if (destinationAddress == 0)
		destinationAddress = clientAddress;


	struct in_addr destinationAddr;
	destinationAddr.s_addr = destinationAddress;
	isMulticast = False;


	//ServerMediaSubsession並沒有保存所有基於自己的數據流,而是隻記錄了最後一次建立的數據流。
	//利用這個變量和fReuseFirstSource可以實現多client連接到一個流的形式。
	if (fLastStreamToken != NULL && fReuseFirstSource) {
		//如果已經基於這個ServerMediaSubsession創建了一個連接,並且希望使用這個連接
		//則直接返回這個連接。
		// Special case: Rather than creating a new 'StreamState',
		// we reuse the one that we've already created:
		serverRTPPort = ((StreamState*) fLastStreamToken)->serverRTPPort();
		serverRTCPPort = ((StreamState*) fLastStreamToken)->serverRTCPPort();
		++((StreamState*) fLastStreamToken)->referenceCount();
		streamToken = fLastStreamToken;
	} else {
		// 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;
		if (clientRTCPPort.num() == 0) {
			// We're streaming raw UDP (not RTP). Create a single groupsock:
			NoReuse dummy; // 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 {
			// 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; // 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
			}


			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;
			increaseSendBufferTo(envir(), rtpGroupsock->socketNum(),rtpBufSize);
		}


		// Set up the state of the stream.  The stream will get started later:
		streamToken = fLastStreamToken = new StreamState(*this, serverRTPPort,
				serverRTCPPort, rtpSink, udpSink, streamBitrate, mediaSource,
				rtpGroupsock, rtcpGroupsock);
	}


	// Record these destinations as being for this client session id:
	Destinations* destinations;
	if (tcpSocketNum < 0) { // UDP
		destinations = new Destinations(destinationAddr, clientRTPPort,	clientRTCPPort);
	} else { // TCP
		destinations = new Destinations(tcpSocketNum, rtpChannelId,	rtcpChannelId);
	}
	
	//記錄下所有clientSessionID對應的目的rtp/rtcp地址,是因爲現在不能把目的rtp,rtcp地址加入到
	//server端rtp的groupSocket中。試想在ReuseFirstSource時,這樣會引起client端立即收到rtp數據。
	//其次,也可以利用這個hash table找出client的rtp/rtcp端口等信息,好像其它地方還真沒有可以保存的
	//RTSPClientSession中的streamstates在ReuseFirstSource時也不能準確找出client端的端口等信息。
	fDestinationsHashTable->Add((char const*) clientSessionId, destinations);
}


流程不復雜:如果需要重用上一次建立的流,就利用之(這樣就可以實現一rtp server對應多個rtp client的形式);如果不需要,則創建合適的source,然後創建rtp sink,然後利用它們創建streamSoken。

啓動一個流:
當RTSPClientSession收到PLAY請求時,就開始傳輸RTP數據。下面看一下流啓動的代碼:

void RTSPServer::RTSPClientSession::handleCmd_PLAY(
		ServerMediaSubsession* subsession,
		char const* cseq,
		char const* fullRequestStr)
{
	char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession,fClientInputSocket);
	unsigned rtspURLSize = strlen(rtspURL);


	// Parse the client's "Scale:" header, if any:
	float scale;
	Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);


	// Try to set the stream's scale factor to this value:
	if (subsession == NULL /*aggregate op*/) {
		fOurServerMediaSession->testScaleFactor(scale);
	} else {
		subsession->testScaleFactor(scale);
	}


	char buf[100];
	char* scaleHeader;
	if (!sawScaleHeader) {
		buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
	} else {
		sprintf(buf, "Scale: %f\r\n", scale);
	}
	scaleHeader = strDup(buf);


	//分析客戶端對於播放範圍的要求
	// Parse the client's "Range:" header, if any:
	double rangeStart = 0.0, rangeEnd = 0.0;
	Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart,rangeEnd);


	// Use this information, plus the stream's duration (if known), to create
	// our own "Range:" header, for the response:
	float duration = subsession == NULL /*aggregate op*/
	? fOurServerMediaSession->duration() : subsession->duration();
	if (duration < 0.0) {
		// We're an aggregate PLAY, but the subsessions have different durations.
		// Use the largest of these durations in our header
		duration = -duration;
	}


	// Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values
	// before we send back our own "Range:" header in our response:
	if (rangeStart < 0.0)
		rangeStart = 0.0;
	else if (rangeStart > duration)
		rangeStart = duration;
	if (rangeEnd < 0.0)
		rangeEnd = 0.0;
	else if (rangeEnd > duration)
		rangeEnd = duration;
	if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0)
			|| (scale < 0.0 && rangeStart < rangeEnd)) {
		// "rangeStart" and "rangeEnd" were the wrong way around; swap them:
		double tmp = rangeStart;
		rangeStart = rangeEnd;
		rangeEnd = tmp;
	}


	char* rangeHeader;
	if (!sawRangeHeader) {
		buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back
	} else if (rangeEnd == 0.0 && scale >= 0.0) {
		sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
	} else {
		sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
	}
	rangeHeader = strDup(buf);


	// Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
	char const* rtpInfoFmt = "%s" // "RTP-Info:", plus any preceding rtpInfo items
					"%s"// comma separator, if needed
					"url=%s/%s"
					";seq=%d"
					";rtptime=%u";
	unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
	char* rtpInfo = strDup("RTP-Info: ");
	unsigned i, numRTPInfoItems = 0;


	// Do any required seeking/scaling on each subsession, before starting streaming:
	for (i = 0; i < fNumStreamStates; ++i) {
		if (subsession == NULL /* means: aggregated operation */
		|| subsession == fStreamStates[i].subsession) {
			if (sawScaleHeader) {
				fStreamStates[i].subsession->setStreamScale(fOurSessionId,
						fStreamStates[i].streamToken, scale);
			}
			if (sawRangeHeader) {
				double streamDuration = 0.0; // by default; means: stream until the end of the media
				if (rangeEnd > 0.0 && (rangeEnd + 0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places
					// We want the stream to end early.  Set the duration we want:
					streamDuration = rangeEnd - rangeStart;
					if (streamDuration < 0.0)
						streamDuration = -streamDuration; // should happen only if scale < 0.0
				}
				u_int64_t numBytes;
				fStreamStates[i].subsession->seekStream(fOurSessionId,
						fStreamStates[i].streamToken, rangeStart,
						streamDuration, numBytes);
			}
		}
	}


	// Now, start streaming:
	for (i = 0; i < fNumStreamStates; ++i) {
		if (subsession == NULL /* means: aggregated operation */
				|| subsession == fStreamStates[i].subsession) {
			unsigned short rtpSeqNum = 0;
			unsigned rtpTimestamp = 0;
			//啓動流
			fStreamStates[i].subsession->startStream(fOurSessionId,
					fStreamStates[i].streamToken,
					(TaskFunc*) noteClientLiveness, this, rtpSeqNum,
					rtpTimestamp, handleAlternativeRequestByte, this);
			const char *urlSuffix = fStreamStates[i].subsession->trackId();
			char* prevRTPInfo = rtpInfo;
			unsigned rtpInfoSize = rtpInfoFmtSize + strlen(prevRTPInfo) + 1
					+ rtspURLSize + strlen(urlSuffix) + 5 /*max unsigned short len*/
			+ 10 /*max unsigned (32-bit) len*/
			+ 2 /*allows for trailing \r\n at final end of string*/;
			rtpInfo = new char[rtpInfoSize];
			sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo,
					numRTPInfoItems++ == 0 ? "" : ",", rtspURL, urlSuffix,
					rtpSeqNum, rtpTimestamp);
			delete[] prevRTPInfo;
		}
	}
	if (numRTPInfoItems == 0) {
		rtpInfo[0] = '\0';
	} else {
		unsigned rtpInfoLen = strlen(rtpInfo);
		rtpInfo[rtpInfoLen] = '\r';
		rtpInfo[rtpInfoLen + 1] = '\n';
		rtpInfo[rtpInfoLen + 2] = '\0';
	}


	// Fill in the response:
	snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,
			"RTSP/1.0 200 OK\r\n"
					"CSeq: %s\r\n"
					"%s"
					"%s"
					"%s"
					"Session: %08X\r\n"
					"%s\r\n", cseq, dateHeader(), scaleHeader, rangeHeader,
			fOurSessionId, rtpInfo);
	delete[] rtpInfo;
	delete[] rangeHeader;
	delete[] scaleHeader;
	delete[] rtspURL;
}


有個問題,如果這個streamToken使用的是已存在的(還記得ReuseFirstSource嗎),爲它再次調用startStream()時,究竟會做什麼呢?你猜啊!呵呵我猜吧:應該是把這個客戶端的地址和rtp/rtcp端口傳給rtp server的groupSocket,rtp server自然就開始向這個客戶端發送數據了。是不是這樣尼?看代碼吧:

void StreamState::startPlaying(
		Destinations* dests,
		TaskFunc* rtcpRRHandler,
		void* rtcpRRHandlerClientData,
		ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
		void* serverRequestAlternativeByteHandlerClientData) 
{
	//目標ip address rtp && rtcp port
	if (dests == NULL)
		return;


	//創建RTCPInstance對象,以計算和收發RTCP包
	if (fRTCPInstance == NULL && fRTPSink != NULL) {
		// Create (and start) a 'RTCP instance' for this RTP sink:
		fRTCPInstance = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,
				fTotalBW, (unsigned char*) fMaster.fCNAME, fRTPSink,
				NULL /* we're a server */);
		// Note: This starts RTCP running automatically
	}


	if (dests->isTCP) {
		//如果RTP over TCP
		// Change RTP and RTCP to use the TCP socket instead of UDP:
		if (fRTPSink != NULL) {
			fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);
			fRTPSink->setServerRequestAlternativeByteHandler(
					dests->tcpSocketNum, serverRequestAlternativeByteHandler,
					serverRequestAlternativeByteHandlerClientData);
		}
		if (fRTCPInstance != NULL) {
			fRTCPInstance->addStreamSocket(dests->tcpSocketNum,
					dests->rtcpChannelId);
			fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum,
					dests->rtcpChannelId, rtcpRRHandler,
					rtcpRRHandlerClientData);
		}
	} else {
		//向RTP和RTCP的groupsocket增加這個目標
		// Tell the RTP and RTCP 'groupsocks' about this destination
		// (in case they don't already have it):
		if (fRTPgs != NULL)
			fRTPgs->addDestination(dests->addr, dests->rtpPort);
		if (fRTCPgs != NULL)
			fRTCPgs->addDestination(dests->addr, dests->rtcpPort);
		if (fRTCPInstance != NULL) {
			fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr,
					dests->rtcpPort, rtcpRRHandler, rtcpRRHandlerClientData);
		}
	}


	if (!fAreCurrentlyPlaying && fMediaSource != NULL) {
		//如果還沒有啓動傳輸,現在啓動之。
		if (fRTPSink != NULL) {
			fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState,
					this);
			fAreCurrentlyPlaying = True;
		} else if (fUDPSink != NULL) {
			fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState,this);
			fAreCurrentlyPlaying = True;
		}
	}
}



轉自: http://blog.csdn.net/niu_gao/article/details/6914637

發佈了7 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章