Parsing RTSP server-RTSP protocol from scratch

Source: Internet
Author: User

Rtps demo (64-bit for Linux and 32-bit for Windows): rtsp_demo.rar

1. First, the server receives a client connection request to produce an rtspclientconnection object. For details about the rtspclientconnection definition, refer to the definition of RTSP server-module from scratch.

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. The server parses the RTSP protocol when receiving a message from the client.

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 (); // copy the message STD: String requst; n-> _ PEEK (requst, nread); size_type Pos = requst. find ("\ r \ n"); // incomplete message if (Pos = STD: String: NPOs) {return 0 ;} int msglen = (INT) POS + 4; // retrieves the message N-> _ Pop (MS Glen); // The message is too long 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 ("cmdcmd % s \ n ", cmd. c_str (); // printf ("[cmd: % s] C-> S: % s \ 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. The following is rtspclientconnection. rtps protocol processing is implemented in this class.

1). the option implementation is as follows: return the methods supported by the server

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) The describle implementation is as follows, mainly used to generate media objects and decode media SDP and SDP clients.

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. the setup implementation is as follows, mainly to establish a session, each stream will establish a session, generate a session ID, and the server RTP, RTCP port, create UDP socke for RTP, here, the RTP value implements the UDP mode.

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 DESCRIBEif (!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;  //UDPuint  rtcpPort = 1; //UDPuint  rtpChannel = 0xff; //tcpuint  rtcpChannel = 0xff; //tcpuint16 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/ndo{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 codeMediaSubSession * 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());// unicaststd::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 nowbreak;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 nowprintf("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. The implementation of play is as follows, mainly to read files, compress them into RTP packets, and register the timer (the scheduled shipping task is sent to the thread for the next time)

int RtspClientConnection::handle_play(MediaSession * session, MediaSubSession * subSession,   uint seq,   std::string & fullRequestStr){std::string strScale;//parse scalesize_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){//errorreturn -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;}

As for pause, get_parameter, set_parameter, and teardown implementation, we will not talk about it here. Next time we will talk about the implementation of mediasession.

 

Parsing RTSP server-RTSP protocol from scratch

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.