首页 > 代码库 > 从头写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协议的解析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。