ZLMedia中RTSP协议的处理简要分析(2)--setup
setup要接收两次,指明流媒体的传输方式 。
代码如下

void RtspSession::handleReq_Setup(const Parser &parser) { //处理setup命令,该函数可能进入多次 int trackIdx = getTrackIndexByControlUrl(parser.FullUrl()); SdpTrack::Ptr &trackRef = _sdp_track[trackIdx]; if (trackRef->_inited) { //已经初始化过该Track throw SockException(Err_shutdown, "can not setup one track twice"); } trackRef->_inited = true; //现在初始化 if(_rtp_type == Rtsp::RTP_Invalid){ auto &strTransport = parser["Transport"]; if(strTransport.find("TCP") != string::npos){ _rtp_type = Rtsp::RTP_TCP; }else if(strTransport.find("multicast") != string::npos){ _rtp_type = Rtsp::RTP_MULTICAST; }else{ _rtp_type = Rtsp::RTP_UDP; } } //允许接收rtp、rtcp包 RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP); switch (_rtp_type) { case Rtsp::RTP_TCP: { if(_push_src){ //rtsp推流时,interleaved由推流者决定 auto key_values = Parser::parseArgs(parser["Transport"],";","="); int interleaved_rtp = -1 , interleaved_rtcp = -1; if(2 == sscanf(key_values["interleaved"].data(),"%d-%d",&interleaved_rtp,&interleaved_rtcp)){ trackRef->_interleaved = interleaved_rtp; }else{ throw SockException(Err_shutdown, "can not find interleaved when setup of rtp over tcp"); } }else{ //rtsp播放时,由于数据共享分发,所以interleaved必须由服务器决定 trackRef->_interleaved = 2 * trackRef->_type; } sendRtspResponse("200 OK", {"Transport", StrPrinter << "RTP/AVP/TCP;unicast;" << "interleaved=" << (int) trackRef->_interleaved << "-" << (int) trackRef->_interleaved + 1 << ";" << "ssrc=" << printSSRC(trackRef->_ssrc), "x-Transport-Options", "late-tolerance=1.400000", "x-Dynamic-Rate", "1" }); } break; case Rtsp::RTP_UDP: { std::pair<Socket::Ptr, Socket::Ptr> pr = std::make_pair(createSocket(),createSocket()); try { makeSockPair(pr, get_local_ip()); } catch (std::exception &ex) { //分配端口失败 send_NotAcceptable(); throw SockException(Err_shutdown, ex.what()); } _rtp_socks[trackIdx] = pr.first; _rtcp_socks[trackIdx] = pr.second; //设置客户端内网端口信息 string strClientPort = FindField(parser["Transport"].data(), "client_port=", NULL); uint16_t ui16RtpPort = atoi(FindField(strClientPort.data(), NULL, "-").data()); uint16_t ui16RtcpPort = atoi(FindField(strClientPort.data(), "-", NULL).data()); auto peerAddr = SockUtil::make_sockaddr(get_peer_ip().data(), ui16RtpPort); //设置rtp发送目标地址 pr.first->bindPeerAddr((struct sockaddr *) (&peerAddr)); //设置rtcp发送目标地址 peerAddr = SockUtil::make_sockaddr(get_peer_ip().data(), ui16RtcpPort); pr.second->bindPeerAddr((struct sockaddr *) (&peerAddr)); //尝试获取客户端nat映射地址 startListenPeerUdpData(trackIdx); //InfoP(this) << "分配端口:" << srv_port; sendRtspResponse("200 OK", {"Transport", StrPrinter << "RTP/AVP/UDP;unicast;" << "client_port=" << strClientPort << ";" << "server_port=" << pr.first->get_local_port() << "-" << pr.second->get_local_port() << ";" << "ssrc=" << printSSRC(trackRef->_ssrc) }); } break; case Rtsp::RTP_MULTICAST: { if(!_multicaster){ _multicaster = RtpMultiCaster::get(*this, get_local_ip(), _media_info._vhost, _media_info._app, _media_info._streamid); if (!_multicaster) { send_NotAcceptable(); throw SockException(Err_shutdown, "can not get a available udp multicast socket"); } weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this()); _multicaster->setDetachCB(this, [weakSelf]() { auto strongSelf = weakSelf.lock(); if(!strongSelf) { return; } strongSelf->safeShutdown(SockException(Err_shutdown,"ring buffer detached")); }); } int iSrvPort = _multicaster->getMultiCasterPort(trackRef->_type); //我们用trackIdx区分rtp和rtcp包 //由于组播udp端口是共享的,而rtcp端口为组播udp端口+1,所以rtcp端口需要改成共享端口 auto pSockRtcp = UDPServer::Instance().getSock(*this, get_local_ip().data(), 2 * trackIdx + 1, iSrvPort + 1); if (!pSockRtcp) { //分配端口失败 send_NotAcceptable(); throw SockException(Err_shutdown, "open shared rtcp socket failed"); } startListenPeerUdpData(trackIdx); GET_CONFIG(uint32_t,udpTTL,MultiCast::kUdpTTL); sendRtspResponse("200 OK", {"Transport", StrPrinter << "RTP/AVP;multicast;" << "destination=" << _multicaster->getMultiCasterIP() << ";" << "source=" << get_local_ip() << ";" << "port=" << iSrvPort << "-" << pSockRtcp->get_local_port() << ";" << "ttl=" << udpTTL << ";" << "ssrc=" << printSSRC(trackRef->_ssrc) }); } break; default: break; } }
1.首先得到SdpTrack信息。
2. auto &strTransport = parser["Transport"];得到要解析的传输信息。
3. auto key_values = Parser::parseArgs(parser["Transport"],";","=");对Transport进行解析。
4. if(2 == sscanf(key_values["interleaved"].data(),"%d-%d",&interleaved_rtp,&interleaved_rtcp)解析interleaved,回复时使用。
5.回复时x-Transport-Options,x-Dynamic-Rate写了固定值,不知什么意思。
分类:
ZLMedia
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!