码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-09-03 10:57:26      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   os   io   ar   for   

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协议的解析

标签:des   style   blog   http   color   os   io   ar   for   

原文地址:http://www.cnblogs.com/4k4s/p/3952940.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!