diff --git a/src/Rtcp/RtcpFCI.cpp b/src/Rtcp/RtcpFCI.cpp index 725236d4..78877337 100644 --- a/src/Rtcp/RtcpFCI.cpp +++ b/src/Rtcp/RtcpFCI.cpp @@ -428,8 +428,8 @@ static int16_t getRecvDelta(SymbolStatus status, uint8_t *&ptr, const uint8_t *e return delta; } -map > FCI_TWCC::getPacketChunkList(size_t total_size) const { - map > ret; +map > FCI_TWCC::getPacketChunkList(size_t total_size) const { + map > ret; auto ptr = (uint8_t *) this + kSize; auto end = (uint8_t *) this + total_size; CHECK(ptr < end); diff --git a/src/Rtcp/RtcpFCI.h b/src/Rtcp/RtcpFCI.h index 071a3d1b..83ff255f 100644 --- a/src/Rtcp/RtcpFCI.h +++ b/src/Rtcp/RtcpFCI.h @@ -353,7 +353,7 @@ public: //单位64ms uint32_t getReferenceTime() const; uint16_t getPacketCount() const; - map > getPacketChunkList(size_t total_size) const; + map > getPacketChunkList(size_t total_size) const; private: //base sequence number,基础序号,本次反馈的第一个包的序号;也就是RTP扩展头的序列号 diff --git a/webrtc/RtpExt.cpp b/webrtc/RtpExt.cpp index ac4143d5..89e56b00 100644 --- a/webrtc/RtpExt.cpp +++ b/webrtc/RtpExt.cpp @@ -239,7 +239,7 @@ string RtpExt::dumpString() const { break; } case RtpExtType::transport_cc : { - printer << "twcc seq:" << getTransportCCSeq(); + printer << "twcc ext seq:" << getTransportCCSeq(); break; } case RtpExtType::sdes_mid : { @@ -546,6 +546,10 @@ RtpExtType RtpExt::getType() const { return _type; } +RtpExt::operator bool() const { + return _ext != nullptr; +} + RtpExtContext::RtpExtContext(const RtcMedia &m){ for (auto &ext : m.extmap) { auto ext_type = RtpExt::getExtType(ext.ext); @@ -566,8 +570,9 @@ void RtpExtContext::setRid(uint32_t ssrc, const string &rid) { _ssrc_to_rid[ssrc] = rid; } -void RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, string *rid_ptr) { +RtpExt RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, string *rid_ptr, RtpExtType type) { string rid, repaired_rid; + RtpExt ret; auto ext_map = RtpExt::getExtValue(header); for (auto &pr : ext_map) { if (is_recv) { @@ -596,10 +601,13 @@ void RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, string //重新赋值ext id为客户端sdp声明的类型 pr.second.setExtId(it->second); } + if (pr.second.getType() == type) { + ret = pr.second; + } } if (!is_recv) { - return; + return ret; } if (rid.empty()) { rid = repaired_rid; @@ -619,6 +627,7 @@ void RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, string if (rid_ptr) { *rid_ptr = rid; } + return ret; } void RtpExtContext::setOnGetRtp(OnGetRtp cb) { diff --git a/webrtc/RtpExt.h b/webrtc/RtpExt.h index d53a4a9b..b05a8021 100644 --- a/webrtc/RtpExt.h +++ b/webrtc/RtpExt.h @@ -52,7 +52,7 @@ class RtpExt { public: template friend void appendExt(map &ret, uint8_t *ptr, const uint8_t *end); - + friend class RtpExtContext; ~RtpExt() = default; static map getExtValue(const RtpHeader *header); @@ -94,8 +94,10 @@ public: void setExtId(uint8_t ext_id); void clearExt(); + operator bool () const; private: + RtpExt() = default; RtpExt(void *ptr, bool one_byte_ext, const char *str, size_t size); const char *data() const; size_t size() const; @@ -122,7 +124,7 @@ public: void setOnGetRtp(OnGetRtp cb); string getRid(uint32_t ssrc) const; void setRid(uint32_t ssrc, const string &rid); - void changeRtpExtId(const RtpHeader *header, bool is_recv, string *rid_ptr = nullptr); + RtpExt changeRtpExtId(const RtpHeader *header, bool is_recv, string *rid_ptr = nullptr, RtpExtType type = RtpExtType::padding); private: void onGetRtp(uint8_t pt, uint32_t ssrc, const string &rid); diff --git a/webrtc/TwccContext.cpp b/webrtc/TwccContext.cpp new file mode 100644 index 00000000..4bba225a --- /dev/null +++ b/webrtc/TwccContext.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "TwccContext.h" + +enum class ExtSeqStatus : int { + normal = 0, + looped, + jumped, +}; + +void TwccContext::onRtp(uint16_t twcc_ext_seq) { + switch ((ExtSeqStatus) checkSeqStatus(twcc_ext_seq)) { + case ExtSeqStatus::jumped: /*回环后,收到回环前的大ext seq包,过滤掉*/ return; + case ExtSeqStatus::looped: /*回环,触发发送twcc rtcp*/ onSendTwcc(); break; + case ExtSeqStatus::normal: break; + default: /*不可达*/assert(0); break; + } + + auto result = _rtp_recv_status.emplace(twcc_ext_seq, _ticker.createdTime()); + if (!result.second) { + WarnL << "recv same twcc ext seq:" << twcc_ext_seq; + return; + } + + _max_stamp = result.first->second; + if (!_min_stamp) { + _min_stamp = _max_stamp; + } + + if (checkIfNeedSendTwcc()) { + //其他匹配条件立即发送twcc + onSendTwcc(); + } +} + +bool TwccContext::checkIfNeedSendTwcc() const { + auto size = _rtp_recv_status.size(); + if (!size) { + return false; + } + if (size >= kMaxSeqDelta) { + return true; + } + auto delta_ms = _max_stamp - _min_stamp; + if (delta_ms >= kMaxTimeDelta) { + return true; + } + return false; +} + +int TwccContext::checkSeqStatus(uint16_t twcc_ext_seq) const { + if (_rtp_recv_status.empty()) { + return (int) ExtSeqStatus::normal; + } + auto max = _rtp_recv_status.rbegin()->first; + if (max > 0xFF00 && twcc_ext_seq < 0xFF) { + //发生回环了 + TraceL << "rtp twcc ext seq looped:" << max << " -> " << twcc_ext_seq; + return (int) ExtSeqStatus::looped; + } + if (twcc_ext_seq - max > 0xFFFF / 2) { + TraceL << "rtp twcc ext seq jumped:" << max << " -> " << twcc_ext_seq; + return (int) ExtSeqStatus::jumped; + } + return (int) ExtSeqStatus::normal; +} + +void TwccContext::onSendTwcc() { + auto max = _rtp_recv_status.rbegin()->first; + auto begin = _rtp_recv_status.begin(); + auto min = begin->first; + auto ref_time = begin->second; + DebugL << "base_seq:" << min << ",pkt_status_count:" << max + 1 - min << ",ref time:" << ref_time / 64 + << ", fb count:" << (int) (_twcc_pkt_count++); + for (auto i = min; i <= max; ++i) { + auto it = _rtp_recv_status.find(i); + if (it == _rtp_recv_status.end()) { + DebugL << "rtp seq:" << i << ",packet status:" << 0 /*not recved*/ << ",delta_ms:" << 0; + } else { + auto delta_ms = it->second - ref_time; + DebugL << "rtp seq:" << i << ",packet status:" << 1 /*recved*/ << ",delta_ms:" << delta_ms; + } + } + clearStatus(); +} + +void TwccContext::clearStatus() { + _rtp_recv_status.clear(); + _min_stamp = 0; +} diff --git a/webrtc/TwccContext.h b/webrtc/TwccContext.h new file mode 100644 index 00000000..0c73c36e --- /dev/null +++ b/webrtc/TwccContext.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TWCCCONTEXT_H +#define ZLMEDIAKIT_TWCCCONTEXT_H + +#include +#include +#include "Util/TimeTicker.h" +using namespace toolkit; + +class TwccContext { +public: + //每个twcc rtcp包最多表明的rtp ext seq增量 + static constexpr size_t kMaxSeqDelta = 20; + //每个twcc rtcp包发送的最大时间间隔,单位毫秒 + static constexpr size_t kMaxTimeDelta = 64; + + TwccContext() = default; + ~TwccContext() = default; + + void onRtp(uint16_t twcc_ext_seq); + +private: + void onSendTwcc(); + bool checkIfNeedSendTwcc() const; + int checkSeqStatus(uint16_t twcc_ext_seq) const; + void clearStatus(); + +private: + Ticker _ticker; + uint64_t _min_stamp = 0; + uint64_t _max_stamp; + std::map _rtp_recv_status; + uint8_t _twcc_pkt_count = 0; +}; + + +#endif //ZLMEDIAKIT_TWCCCONTEXT_H diff --git a/webrtc/WebRtcTransport.cpp b/webrtc/WebRtcTransport.cpp index 7e5e52d2..a4fa3e40 100644 --- a/webrtc/WebRtcTransport.cpp +++ b/webrtc/WebRtcTransport.cpp @@ -808,7 +808,10 @@ void WebRtcTransportImp::onRtp(const char *buf, size_t len) { //修改ext id至统一 string rid; - track->rtp_ext_ctx->changeRtpExtId(rtp, true, &rid); + auto twcc_ext = track->rtp_ext_ctx->changeRtpExtId(rtp, true, &rid, RtpExtType::transport_cc); + if (twcc_ext && !is_rtx) { + _twcc_ctx.onRtp(twcc_ext.getTransportCCSeq()); + } auto &ref = track->rtp_channel[rid]; if (!ref) { diff --git a/webrtc/WebRtcTransport.h b/webrtc/WebRtcTransport.h index 61837beb..ba76b7b9 100644 --- a/webrtc/WebRtcTransport.h +++ b/webrtc/WebRtcTransport.h @@ -24,6 +24,7 @@ #include "Rtcp/RtcpFCI.h" #include "Nack.h" #include "Network/Session.h" +#include "TwccContext.h" using namespace toolkit; using namespace mediakit; @@ -251,4 +252,5 @@ private: unordered_map > _pt_to_track; //根据rtcp的ssrc获取相关信息,收发rtp和rtx的ssrc都会记录 unordered_map _ssrc_to_track; + TwccContext _twcc_ctx; }; \ No newline at end of file