rtps demo(linux是64位的,windows32位的):rtsp_demo.rar
1.首先服务器收到客户端连接请求,生产 一个RtspClientConnection对象,RtspClientConnection定义详见 从头写rtsp服务器-模块的划分
int rtsp::v_accept(netconnection * n) { netoperation::v_accept(n); printf("client accept\n\n\n"); RtspClientConnection * session = new RtspClientConnection(n, m_serveAddr); n->_setcontext((void*)session); return 0; }
2. 服务端收到客户端发来的消息,解析RTSP协议
int rtsp::v_read(netconnection * n) { netoperation::v_read(n); char ch = 0; n->_peek(&ch, sizeof(ch)); if (ch != '$'){ v_rtspRead(n); } else{ v_rtpRead(n); } return 0; } int rtsp::v_rtspRead(netconnection * n) { RtspClientConnection * clientConnection = (RtspClientConnection*)n->_context(); int nRead = n->_avaliableread(); //拷贝消息 std::string requst; n->_peek(requst, nRead); size_type pos = requst.find("\r\n\r\n"); //消息不完整 if (pos == std::string::npos){ return 0; } int msglen = (int)pos + 4; //取出该条消息 n->_pop(msglen); //消息过长 if (msglen < nRead){ requst[msglen] = 0; } std::string cmd; std::string urlPreSuffix; std::string urlSuffix; std::string strSessionId; uint cseq = 0; int ret = ParseRTSPRequestString(requst, cmd, urlPreSuffix, urlSuffix, cseq, strSessionId); printf("parseRTSPRequestString cmd %s\n", cmd.c_str()); //printf("[cmd:%s] C->S: %s\n\n", cmd.c_str(), requst.c_str()); std::string streamName; std::string trackId; std::string urlTotalSuffix = urlPreSuffix ; if (!urlTotalSuffix.empty()) { urlTotalSuffix.append("/"); } urlTotalSuffix.append(urlSuffix); if (urlSuffix.find(MediaSubSession::TrackFmt()) == std::string::npos) { streamName = urlTotalSuffix; } else { streamName = urlPreSuffix; trackId= urlSuffix; } if (cmd == "OPTIONS") { clientConnection->handle_options(cseq); } else if (cmd == "DESCRIBE") { clientConnection->handle_describle(streamName, cseq, requst); } else if (cmd == "SETUP") { clientConnection->handle_setup(strSessionId, streamName, trackId, cseq, requst); } else if (cmd == "PLAY" || cmd == "PAUSE" || cmd == "GET_PARAMETER" || cmd == "SET_PARAMETER" || cmd == "TEARDOWN") { clientConnection->handle_incmd(strSessionId, cmd, streamName, trackId, cseq, requst); } else if(cmd == "REGISTER" || cmd == "REGISTER_REMOTE") { clientConnection->handle_register(urlSuffix, requst, cmd == "REGISTER_REMOTE"); } else { clientConnection->handleCmd_notSupported(); } return 0; }
3. 下面是RtspClientConnection,rtps协议的处理就在这个类中实现。
1). OPTION实现如下,返回服务器所支持的方法
int RtspClientConnection::handle_options(uint seq){ std::string str; append(str, "RTSP/1.0 200 OK\r\n" "CSeq: %u\r\n" "%s" "Public: %s\r\n\r\n", seq, dateStr().c_str(), "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER"); m_n->_send(str.c_str(), str.length()); return 0; }
2)DESCRIBLE实现如下,主要是生成媒体对象,和媒体SDP, SDP客户端解码用到
int RtspClientConnection::handle_describle(
					  	   std::string & streamName,
					   	   uint seq,
						   std::string & fullRequestStr)
{
	if (m_mediaSession){
		if(m_mediaSession->StreamName() != streamName){
			delete m_mediaSession;
			m_mediaSession =  CreateMediaSession(streamName);
		}
	}else{
		m_mediaSession =  CreateMediaSession(streamName);
	}
	if (!m_mediaSession){
		handleCmd_notFound();
 		return -1;
	}
	MediaSession * session = m_mediaSession;
	std::string sdp = session->GenerateSDPDescription(m_serveAddr);
	//get the rtsp url
	//rtsp://127.0.0.1/
	std::string rtspUrl;
	append(rtspUrl, "rtsp://%s:%u/%s", 
		m_serveAddr._ipstr(),
		m_serveAddr._port() ,
		session->StreamName().c_str());
	std::string response = "RTSP/1.0 200 OK\r\n";
	append(response, "CSeq: %u\r\n"
					 "%s"
					 "Content-Base: %s\r\n"
					 "Content-Type: application/sdp\r\n"
					 "Content-Length: %d\r\n\r\n"
					 "%s",
					 seq,
					 dateStr().c_str(),
					 rtspUrl.c_str(),
					 sdp.length(),
					 sdp.c_str());
//	printf("S-C : %s\n", response.c_str());
	
	m_n->_send(response.c_str(), response.length());
	return 0;
}
3.SETUP实现如下,主要是建立会话,每一条流都会建立一次会话,生成会话id, 和服务器rtp,rtcp端口,创建用于rtp的UDP socke,这里rtp值实现了udp方式
int RtspClientConnection::handle_setup(std::string & sessionId,
								    std::string & streamName, 
									std::string & trackId, 
									uint seq,
									std::string & fullRequestStr)
{
	if (sessionId.empty()){
		m_sessionId = "";
		append(m_sessionId, "%08X",  random_32());
	}
	else{
		if(m_sessionId != sessionId){
			handleCmd_sessionNotFound();
			printf("session error\n");
			return -1;
		}
	}
	enum
	{
		RTP_UDP,
		RTP_TCP,
		RAW_UDP
	};
	//in case : with out no DESCRIBE
	if (!m_mediaSession){
		m_mediaSession = CreateMediaSession(streamName);
		if(!m_mediaSession){
			handleCmd_notFound();
			return -1;
		}
	}
	else{
		if(!streamName.empty()){
			if(m_mediaSession->StreamName() != streamName){
				handleCmd_bad();
				return -1;
			}
		}
	}
	MediaSession * session = m_mediaSession;
	size_type npos = std::string::npos;
	size_type t = fullRequestStr.find("Transport:");
	if (t == npos){
		printf("no Transport\n");
		return -1;
	}
	t += 9;
	while(fullRequestStr[++t] == ' ');
	std::string strParse = fullRequestStr.substr(t, fullRequestStr.length() - t);
	int transMode = RTP_UDP;
	std::string strtransMode = "RTP/AVP";
	std::string strDstAdder;
	uint  dstTTL = 255;
	uint  rtpPort = 0;  //UDP
	uint  rtcpPort = 1; //UDP
	uint  rtpChannel = 0xff; //tcp
	uint  rtcpChannel = 0xff; //tcp
	uint16 p1 = 0, p2 = 0;
	uint   ttl = 0,  rtpId = 0, rtcpId =0;
	size_type pos = 0;
	size_type end = fullRequestStr.length() - (size_type)4; // /r/n/r/n
	do
	{
		pos = fullRequestStr.find(';', t);
		if (pos == npos)
		{
			pos = end;
		}
	
		std::string str = fullRequestStr.substr(t, pos - t);
		t = pos + 1;
		if (str == "RTP/AVP/TCP")
		{
			transMode = RTP_TCP;
		}
		else if (str == "RAW/RAW/UDP" || str == "MP2T/H2221/UDP")
		{
			transMode = RAW_UDP;
			strtransMode = str;
		}
		else if (str.find("destination=") != npos)
		{
			strDstAdder = str.substr(12);
		}
		else if (sscanf(str.c_str(), "ttl%d", &ttl) == 1)
		{
			dstTTL = (int)ttl;
		}
		else if (sscanf(str.c_str(), "client_port=%hu-%hu", &p1, &p2) == 2)
		{
			rtpPort = p1;
			rtcpPort = transMode == RAW_UDP ? 0 : p2;
		}
		else if (sscanf(str.c_str(), "client_port=%hu", &p1) == 1)
		{
			rtpPort = p1;
			rtcpPort = transMode == RAW_UDP ? 0 : (p1 + 1);
		}
		else if (sscanf(str.c_str(), "interleaved=%u-%u", &rtpId, &rtcpId) == 2)
		{
			rtpChannel  = rtpId;
			rtcpChannel = rtcpId;
		}
	}while(pos != end);
	if ((transMode == RTP_TCP && rtpChannel == 0xff) )
	{
		rtpChannel = 0;
		rtcpChannel = 1;
	}
	// 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":
	if (fullRequestStr.find("x-playNow:") != npos)
	{
		handleCmd_bad();
		printf("no support x-playNow\n");
		return -1;
	}
	//look for MediaSubSession add code
	MediaSubSession * subsession = NULL;
	if (trackId.find(MediaSubSession::TrackFmt()) != std::string::npos){
		subsession = session->Lookup(trackId);
		if(!subsession){
			handleCmd_notFound();
			return -1;
		}
	}else{
		// Weird case: there was no track id in the URL.
		// This works only if we have only one subsession:
		if (session->SubSessionCount() > 1){
			handleCmd_bad();
			return -1;
		}
	}
	netaddress clientAddr = m_n->_getclientaddr();
	netaddress destAddr(strDstAdder.c_str());
	if(strDstAdder.empty()){
		destAddr._setip(clientAddr._ip());
	}
	uint serverRtpPort  = 0;
	uint serverRtcpPort = 0;
	bool isMulticast = false;
	subsession->GetStreamParam(	
		rtpPort, 
		rtcpPort,
		rtpChannel,
		rtcpChannel,
		destAddr._ip(),
		dstTTL,
		isMulticast,
		serverRtpPort,
		serverRtcpPort,
		m_serveAddr._ip());
	// unicast
	std::string response;
	switch(transMode)
	{
	case RTP_UDP:
		append(response,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"%s"
			"Transport: RTP/AVP;unicast;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
		    "Session: %s\r\n\r\n",
			seq, dateStr().c_str(), 
			m_serveAddr._ipstr(), 
			rtpPort, rtcpPort, serverRtpPort, serverRtcpPort,m_sessionId.c_str()
			);
		//destination=%s;
		break;
	case RAW_UDP:
		append(response,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"%s"
			"Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
		    "Session: %s\r\n\r\n",
			seq, dateStr().c_str(),
			clientAddr._ipstr(), m_serveAddr._ipstr());
		printf("not support RAW_UDP\n");
		//not support now
		break;
	case RTP_TCP:
		append(response,
			"RTSP/1.0 200 OK\r\n"
			"CSeq: %u\r\n"
			"%s"
			"Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
		    "Session: %s\r\n\r\n",
			seq, dateStr().c_str(),
			clientAddr._ipstr(), m_serveAddr._ipstr());
				//not support now
			printf("not support RTP_TCP\n");
		break;
	}
	//printf("S->C : %s\n", response.c_str());
	m_n->_send(response.c_str(), response.length());
	return 0;
}
4.PLAY实现如下,主要是读取文件,打成RTP包,注册定时器(定时投递任务到线程次发送)
int RtspClientConnection::handle_play(MediaSession * session, 
									MediaSubSession * subSession,
								   uint seq,
								   std::string & fullRequestStr)
{
	std::string strScale;
	//parse scale
	size_type pos = fullRequestStr.find("Scale:");
	if(pos != std::string::npos){
		pos += 5;
		while(fullRequestStr[++pos] == ' ');
		float fscale = (float)atof(fullRequestStr.c_str() + pos);
		append(strScale, "Scale: %f\r\n", fscale);
	}
	std::string rtspUrl;
	MediaSubSession * sub = session->GetSubSession(0);
	for (int i = 0; i < session->SubSessionCount(); i++){
		MediaSubSession * temp = session->GetSubSession(i);
		if (!subSession || temp == subSession){
			sub = temp;
			append(rtspUrl, "RTP-Info: rtsp://%s:%u/%s/%s;seq=%u;rtptime=%u", 
				m_serveAddr._ipstr(),
				m_serveAddr._port() ,
				session->StreamName().c_str(),
				temp->GetTrackId().c_str(),
				temp->SeqNo(),
				temp->RtpTimestamp());
		}
	}
	if (!sub){
		//error
		return -1;
	}
	std::string absstart, absend;
	double startTime = 0, endTime = 0;
	int result = parseRange(fullRequestStr, absstart, absend, startTime, endTime);
	if (result < 0){
	}
	std::string response;
	append(response,
		"RTSP/1.0 200 OK\r\n"
		"CSeq: %u\r\n" 
		"%s" 
		"%s"
		"Session: %s\r\n"
		"%s\r\n\r\n",
		seq, 
		dateStr().c_str(), 
		strScale.c_str(),
		m_sessionId.c_str(),
		rtspUrl.c_str());
	m_n->_send(response.c_str(), response.length());
	sub->StartStream();
	
	return 0;
}
至于PAUSE,GET_PARAMETER,SET_PARAMETER,TEARDOWN实现没有什么,这里就不在说了,下次讲一次 MediaSession 的实现
                    
                
                
            
        
浙公网安备 33010602011771号