diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp index 67184ca5..3f9adb79 100644 --- a/src/Rtsp/RtpReceiver.cpp +++ b/src/Rtsp/RtpReceiver.cpp @@ -116,11 +116,12 @@ bool RtpReceiver::handleOneRtp(int iTrackidx,SdpTrack::Ptr &track, unsigned char _aui32SeqOkCnt[iTrackidx] = 0; _abSortStarted[iTrackidx] = true; // WarnL << "包乱序或丢包:" << iTrackidx <<" " << rtppt.sequence << " " << _aui16LastSeq[iTrackidx]; - if(_aui16LastSeq[iTrackidx] > rtppt.sequence && _aui16LastSeq[iTrackidx] - rtppt.sequence > 0x7FFF){ + if(_aui16LastSeq[iTrackidx] > rtppt.sequence && _aui16LastSeq[iTrackidx] - rtppt.sequence > 0xFF){ //sequence回环,清空所有排序缓存 while (_amapRtpSort[iTrackidx].size()) { POP_HEAD(iTrackidx) } + ++_clcyeCount[iTrackidx]; } }else{ //正确序列的包 @@ -157,6 +158,7 @@ void RtpReceiver::clear() { CLEAR_ARR(_aui32SsrcErrorCnt) CLEAR_ARR(_aui32SeqOkCnt) CLEAR_ARR(_abSortStarted) + CLEAR_ARR(_clcyeCount) _amapRtpSort[0].clear(); _amapRtpSort[1].clear(); @@ -166,4 +168,13 @@ void RtpReceiver::setPoolSize(int size) { _pktPool.setSize(size); } +int RtpReceiver::getJitterSize(int iTrackidx){ + return _amapRtpSort[iTrackidx].size(); +} + +int RtpReceiver::getCycleCount(int iTrackidx){ + return _clcyeCount[iTrackidx]; +} + + }//namespace mediakit diff --git a/src/Rtsp/RtpReceiver.h b/src/Rtsp/RtpReceiver.h index 8d6c2b9b..06d1b45b 100644 --- a/src/Rtsp/RtpReceiver.h +++ b/src/Rtsp/RtpReceiver.h @@ -64,11 +64,14 @@ protected: virtual void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){} void clear(); void setPoolSize(int size); + int getJitterSize(int iTrackidx); + int getCycleCount(int iTrackidx); private: uint32_t _aui32SsrcErrorCnt[2] = { 0, 0 }; /* RTP包排序所用参数 */ uint16_t _aui16LastSeq[2] = { 0 , 0 }; uint32_t _aui32SeqOkCnt[2] = { 0 , 0}; + uint32_t _clcyeCount[2] = { 0 , 0}; bool _abSortStarted[2] = { 0 , 0}; map _amapRtpSort[2]; RtspMediaSource::PoolType _pktPool; diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index d209800a..f0ef4e65 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -113,6 +113,7 @@ public: uint32_t octCount = 0; //网络字节序 uint32_t timeStamp = 0; + uint32_t lastTimeStamp = 0; }; string FindField(const char *buf, const char *start, const char *end, int bufSize = 0); diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 65727c27..c4768ace 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -71,7 +71,6 @@ void RtspPlayer::teardown(){ CLEAR_ARR(_aiFistStamp); CLEAR_ARR(_aiNowStamp); - _pBeatTimer.reset(); _pPlayTimer.reset(); _pRtpTimer.reset(); _iSeekTo = 0; @@ -369,17 +368,6 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) return; } //所有setup命令发送完毕 - //设置心跳包发送定时器 - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as() / 1000.0, [weakSelf](){ - auto strongSelf = weakSelf.lock(); - if (!strongSelf){ - return false; - } - strongSelf->sendRtcpPacket(); - return true; - },getPoller())); - //发送play命令 pause(false); } @@ -491,11 +479,132 @@ void RtspPlayer::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char } -void RtspPlayer::sendRtcpPacket(){ - //目前只实现了通过options命令实现心跳包 - sendOptions(); + +#if 0 +//改代码提取自FFmpeg,参考之 +// Receiver Report + avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ + avio_w8(pb, RTCP_RR); + avio_wb16(pb, 7); /* length in words - 1 */ + // our own SSRC: we use the server's SSRC + 1 to avoid conflicts + avio_wb32(pb, s->ssrc + 1); + avio_wb32(pb, s->ssrc); // server SSRC + // some placeholders we should really fill... + // RFC 1889/p64 + extended_max = stats->cycles + stats->max_seq; + expected = extended_max - stats->base_seq; + lost = expected - stats->received; + lost = FFMIN(lost, 0xffffff); // clamp it since it's only 24 bits... + expected_interval = expected - stats->expected_prior; + stats->expected_prior = expected; + received_interval = stats->received - stats->received_prior; + stats->received_prior = stats->received; + lost_interval = expected_interval - received_interval; + if (expected_interval == 0 || lost_interval <= 0) + fraction = 0; + else + fraction = (lost_interval << 8) / expected_interval; + + fraction = (fraction << 24) | lost; + + avio_wb32(pb, fraction); /* 8 bits of fraction, 24 bits of total packets lost */ + avio_wb32(pb, extended_max); /* max sequence received */ + avio_wb32(pb, stats->jitter >> 4); /* jitter */ + + if (s->last_rtcp_ntp_time == AV_NOPTS_VALUE) { + avio_wb32(pb, 0); /* last SR timestamp */ + avio_wb32(pb, 0); /* delay since last SR */ + } else { + uint32_t middle_32_bits = s->last_rtcp_ntp_time >> 16; // this is valid, right? do we need to handle 64 bit values special? + uint32_t delay_since_last = av_rescale(av_gettime_relative() - s->last_rtcp_reception_time, + 65536, AV_TIME_BASE); + + avio_wb32(pb, middle_32_bits); /* last SR timestamp */ + avio_wb32(pb, delay_since_last); /* delay since last SR */ + } + + // CNAME + avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ + avio_w8(pb, RTCP_SDES); + len = strlen(s->hostname); + avio_wb16(pb, (7 + len + 3) / 4); /* length in words - 1 */ + avio_wb32(pb, s->ssrc + 1); + avio_w8(pb, 0x01); + avio_w8(pb, len); + avio_write(pb, s->hostname, len); + avio_w8(pb, 0); /* END */ + // padding + for (len = (7 + len) % 4; len % 4; len++) + avio_w8(pb, 0); +#endif + +void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ + static const char s_cname[] = "ZLMediaKitRtsp"; + uint8_t aui8Rtcp[4 + 32 + 10 + sizeof(s_cname) + 1] = {0}; + uint8_t *pui8Rtcp_RR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_RR + 32; + auto &track = _aTrackInfo[iTrackIndex]; + auto &counter = _aRtcpCnt[iTrackIndex]; + + aui8Rtcp[0] = '$'; + aui8Rtcp[1] = track->_interleaved + 1; + aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >> 8; + aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF; + + pui8Rtcp_RR[0] = 0x81;/* 1 report block */ + pui8Rtcp_RR[1] = 0xC9;//RTCP_RR + pui8Rtcp_RR[2] = 0x00; + pui8Rtcp_RR[3] = 0x07;/* length in words - 1 */ + + uint32_t ssrc=htonl(track->_ssrc + 1); + // our own SSRC: we use the server's SSRC + 1 to avoid conflicts + memcpy(&pui8Rtcp_RR[4], &ssrc, 4); + ssrc=htonl(track->_ssrc); + // server SSRC + memcpy(&pui8Rtcp_RR[8], &ssrc, 4); + + //FIXME: 8 bits of fraction, 24 bits of total packets lost + pui8Rtcp_RR[12] = 0x00; + pui8Rtcp_RR[13] = 0x00; + pui8Rtcp_RR[14] = 0x00; + pui8Rtcp_RR[15] = 0x00; + + //FIXME: max sequence received + int cycleCount = getCycleCount(iTrackIndex); + pui8Rtcp_RR[16] = cycleCount >> 8; + pui8Rtcp_RR[17] = cycleCount & 0xFF; + pui8Rtcp_RR[18] = counter.pktCnt >> 8; + pui8Rtcp_RR[19] = counter.pktCnt & 0xFF; + + uint32_t jitter = htonl(getJitterSize(iTrackIndex)); + //FIXME: jitter + memcpy(pui8Rtcp_RR + 20, &jitter , 4); + /* last SR timestamp */ + memcpy(pui8Rtcp_RR + 24, &counter.lastTimeStamp, 4); + uint32_t msInc = htonl(ntohl(counter.timeStamp) - ntohl(counter.lastTimeStamp)); + /* delay since last SR */ + memcpy(pui8Rtcp_RR + 28, &msInc, 4); + + // CNAME + pui8Rtcp_SDES[0] = 0x81; + pui8Rtcp_SDES[1] = 0xCA; + pui8Rtcp_SDES[2] = 0x00; + pui8Rtcp_SDES[3] = 0x06; + + memcpy(&pui8Rtcp_SDES[4], &ssrc, 4); + + pui8Rtcp_SDES[8] = 0x01; + pui8Rtcp_SDES[9] = 0x0f; + memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname)); + pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00; + + if(overTcp){ + send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp))); + }else if(_apRtcpSock[iTrackIndex]) { + _apRtcpSock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); + } } + void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){ //统计丢包率 if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) { @@ -608,9 +717,29 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC send(printer << "\r\n"); } -void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) { +void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) { _rtpTicker.resetTime(); - onRecvRTP(pRtppt,track); + onRecvRTP(pkt,track); + + int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved); + if(iTrackIndex == -1){ + return; + } + RtcpCounter &counter = _aRtcpCnt[iTrackIndex]; + counter.pktCnt = pkt->sequence; + auto &ticker = _aRtcpTicker[iTrackIndex]; + if (ticker.elapsedTime() > 5 * 1000) { + //send rtcp every 5 second + counter.lastTimeStamp = counter.timeStamp; + //直接保存网络字节序 + memcpy(&counter.timeStamp, pkt->payload + 8 , 4); + if(counter.lastTimeStamp != 0){ + sendReceiverReport(_eType == Rtsp::RTP_TCP,iTrackIndex); + ticker.resetTime(); + } + } + + } void RtspPlayer::onPlayResult_l(const SockException &ex) { WarnL << ex.getErrCode() << " " << ex.what(); diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 4993e31d..acf032a8 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -94,11 +94,6 @@ protected: * @param uiLen */ virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen); - - /** - * 发送rtcp包维持心跳 - */ - virtual void sendRtcpPacket(); private: void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track); void onPlayResult_l(const SockException &ex); @@ -125,6 +120,7 @@ private: void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap()); void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list &header); + void sendReceiverReport(bool overTcp,int iTrackIndex); private: string _strUrl; SdpAttr _sdpAttr; @@ -151,15 +147,17 @@ private: Ticker _rtpTicker; std::shared_ptr _pPlayTimer; std::shared_ptr _pRtpTimer; - //心跳定时器 - std::shared_ptr _pBeatTimer; - + //播放进度控制,单位毫秒 uint32_t _iSeekTo = 0; //单位毫秒 uint32_t _aiFistStamp[2] = {0,0}; uint32_t _aiNowStamp[2] = {0,0}; + + //rtcp相关 + RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标 + Ticker _aRtcpTicker[2]; //rtcp发送时间,trackid idx 为数组下标 }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index db36e08c..58725bc6 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -1190,7 +1190,6 @@ inline void RtspSession::sendRtpPacket(const RtpPacket::Ptr & pkt) { break; } -#ifdef RTSP_SEND_RTCP int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved); if(iTrackIndex == -1){ return; @@ -1206,21 +1205,19 @@ inline void RtspSession::sendRtpPacket(const RtpPacket::Ptr & pkt) { memcpy(&counter.timeStamp, pkt->payload + 8 , 4); sendSenderReport(_rtpType == Rtsp::RTP_TCP,iTrackIndex); } -#endif } -#ifdef RTSP_SEND_RTCP inline void RtspSession::sendSenderReport(bool overTcp,int iTrackIndex) { - static const char server_name[] = "ZLMediaKitRtsp"; - uint8_t aui8Rtcp[4 + 28 + 10 + sizeof(server_name) + 1] = {0}; + static const char s_cname[] = "ZLMediaKitRtsp"; + uint8_t aui8Rtcp[4 + 28 + 10 + sizeof(s_cname) + 1] = {0}; uint8_t *pui8Rtcp_SR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_SR + 28; auto &track = _aTrackInfo[iTrackIndex]; auto &counter = _aRtcpCnt[iTrackIndex]; aui8Rtcp[0] = '$'; aui8Rtcp[1] = track->_interleaved + 1; - aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) / 256; - aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) % 256; + aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >> 8; + aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF; pui8Rtcp_SR[0] = 0x80; pui8Rtcp_SR[1] = 0xC8; @@ -1260,8 +1257,8 @@ inline void RtspSession::sendSenderReport(bool overTcp,int iTrackIndex) { pui8Rtcp_SDES[8] = 0x01; pui8Rtcp_SDES[9] = 0x0f; - memcpy(&pui8Rtcp_SDES[10], server_name, sizeof(server_name)); - pui8Rtcp_SDES[10 + sizeof(server_name)] = 0x00; + memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname)); + pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00; if(overTcp){ send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp))); @@ -1269,7 +1266,6 @@ inline void RtspSession::sendSenderReport(bool overTcp,int iTrackIndex) { _apRtcpSock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); } } -#endif } /* namespace mediakit */ diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 5cc04d68..137ddb22 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -46,8 +46,6 @@ using namespace std; using namespace toolkit; -#define RTSP_SEND_RTCP - namespace mediakit { class RtspSession; @@ -200,11 +198,9 @@ private: //rtsp推流相关 RtspToRtmpMediaSource::Ptr _pushSrc; -#ifdef RTSP_SEND_RTCP RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标 Ticker _aRtcpTicker[2]; //rtcp发送时间,trackid idx 为数组下标 inline void sendSenderReport(bool overTcp,int iTrackIndex); -#endif }; /**