首页 > 代码库 > 从头写rtsp服务器-RTSP协议的解析

从头写rtsp服务器-RTSP协议的解析

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 的实现

 

从头写rtsp服务器-RTSP协议的解析