From 18b7e45906af05a014e441af8ddca36c955881a7 Mon Sep 17 00:00:00 2001 From: ziyue <1213642868@qq.com> Date: Fri, 25 Jun 2021 15:43:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86nack=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webrtc/Nack.cpp | 139 ++++++++++++++++++++++++++++++++++++ webrtc/Nack.h | 57 +++++++++++++++ webrtc/WebRtcTransport.h | 148 +-------------------------------------- 3 files changed, 198 insertions(+), 146 deletions(-) create mode 100644 webrtc/Nack.cpp create mode 100644 webrtc/Nack.h diff --git a/webrtc/Nack.cpp b/webrtc/Nack.cpp new file mode 100644 index 00000000..8d4da792 --- /dev/null +++ b/webrtc/Nack.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/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 "Nack.h" + +static constexpr uint32_t kMaxNackMS = 10 * 1000; + +void NackList::push_back(RtpPacket::Ptr rtp) { + auto seq = rtp->getSeq(); + _nack_cache_seq.emplace_back(seq); + _nack_cache_pkt.emplace(seq, std::move(rtp)); + while (get_cache_ms() > kMaxNackMS) { + //需要清除部分nack缓存 + pop_front(); + } +} + +void NackList::for_each_nack(const FCI_NACK &nack, const function &func) { + auto seq = nack.getPid(); + for (auto bit : nack.getBitArray()) { + if (bit) { + //丢包 + RtpPacket::Ptr *ptr = get_rtp(seq); + if (ptr) { + func(*ptr); + } + } + ++seq; + } +} + +void NackList::pop_front() { + if (_nack_cache_seq.empty()) { + return; + } + _nack_cache_pkt.erase(_nack_cache_seq.front()); + _nack_cache_seq.pop_front(); +} + +RtpPacket::Ptr *NackList::get_rtp(uint16_t seq) { + auto it = _nack_cache_pkt.find(seq); + if (it == _nack_cache_pkt.end()) { + return nullptr; + } + return &it->second; +} + +uint32_t NackList::get_cache_ms() { + if (_nack_cache_seq.size() < 2) { + return 0; + } + uint32_t back = _nack_cache_pkt[_nack_cache_seq.back()]->getStampMS(); + uint32_t front = _nack_cache_pkt[_nack_cache_seq.front()]->getStampMS(); + if (back > front) { + return back - front; + } + //很有可能回环了 + return back + (UINT32_MAX - front); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +void NackContext::received(uint16_t seq) { + if (!_last_max_seq && _seq.empty()) { + _last_max_seq = seq - 1; + } + _seq.emplace(seq); + auto max_seq = *_seq.rbegin(); + auto min_seq = *_seq.begin(); + auto diff = max_seq - min_seq; + if (!diff) { + return; + } + + if (diff > UINT32_MAX / 2) { + //回环 + _seq.clear(); + _last_max_seq = min_seq; + return; + } + + if (_seq.size() == diff + 1 && _last_max_seq + 1 == min_seq) { + //都是连续的seq,未丢包 + _seq.clear(); + _last_max_seq = max_seq; + } else { + //seq不连续,有丢包 + if (min_seq == _last_max_seq + 1) { + //前面部分seq是连续的,未丢包,移除之 + eraseFrontSeq(); + } + + //有丢包,丢包从_last_max_seq开始 + if (max_seq - _last_max_seq > FCI_NACK::kBitSize) { + vector vec; + vec.resize(FCI_NACK::kBitSize); + for (auto i = 0; i < FCI_NACK::kBitSize; ++i) { + vec[i] = _seq.find(_last_max_seq + i + 2) == _seq.end(); + } + doNack(FCI_NACK(_last_max_seq + 1, vec)); + _last_max_seq += FCI_NACK::kBitSize + 1; + if (_last_max_seq >= max_seq) { + _seq.clear(); + } else { + auto it = _seq.emplace_hint(_seq.begin(), _last_max_seq); + _seq.erase(_seq.begin(), it); + } + } + } +} + +void NackContext::setOnNack(onNack cb) { + _cb = std::move(cb); +} + +void NackContext::doNack(const FCI_NACK &nack) { + if (_cb) { + _cb(nack); + } +} + +void NackContext::eraseFrontSeq() { + //前面部分seq是连续的,未丢包,移除之 + for (auto it = _seq.begin(); it != _seq.end();) { + if (*it != _last_max_seq + 1) { + //seq不连续,丢包了 + break; + } + _last_max_seq = *it; + it = _seq.erase(it); + } +} \ No newline at end of file diff --git a/webrtc/Nack.h b/webrtc/Nack.h new file mode 100644 index 00000000..e872f3f9 --- /dev/null +++ b/webrtc/Nack.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/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_NACK_H +#define ZLMEDIAKIT_NACK_H + +#include "Rtsp/Rtsp.h" +#include "Rtcp/RtcpFCI.h" + +using namespace mediakit; + +class NackList { +public: + NackList() = default; + ~NackList() = default; + + void push_back(RtpPacket::Ptr rtp); + void for_each_nack(const FCI_NACK &nack, const function &cb); + +private: + void pop_front(); + uint32_t get_cache_ms(); + RtpPacket::Ptr *get_rtp(uint16_t seq); + +private: + deque _nack_cache_seq; + unordered_map _nack_cache_pkt; +}; + +class NackContext { +public: + using onNack = function; + + NackContext() = default; + ~NackContext() = default; + + void received(uint16_t seq); + void setOnNack(onNack cb); + +private: + void eraseFrontSeq(); + void doNack(const FCI_NACK &nack); + +private: + onNack _cb; + set _seq; + uint16_t _last_max_seq = 0; +}; + +#endif //ZLMEDIAKIT_NACK_H diff --git a/webrtc/WebRtcTransport.h b/webrtc/WebRtcTransport.h index 8fc54c15..443c528a 100644 --- a/webrtc/WebRtcTransport.h +++ b/webrtc/WebRtcTransport.h @@ -22,6 +22,8 @@ #include "Rtsp/RtspMediaSourceImp.h" #include "Rtcp/RtcpContext.h" #include "Rtcp/RtcpFCI.h" +#include "Nack.h" + using namespace toolkit; using namespace mediakit; @@ -126,152 +128,6 @@ private: }; class RtpChannel; - -class NackList { -public: - void push_back(RtpPacket::Ptr rtp) { - auto seq = rtp->getSeq(); - _nack_cache_seq.emplace_back(seq); - _nack_cache_pkt.emplace(seq, std::move(rtp)); - while (get_cache_ms() > kMaxNackMS) { - //需要清除部分nack缓存 - pop_front(); - } - } - - template - void for_each_nack(const FCI_NACK &nack, const FUNC &func) { - auto seq = nack.getPid(); - for (auto bit : nack.getBitArray()) { - if (bit) { - //丢包 - RtpPacket::Ptr *ptr = get_rtp(seq); - if (ptr) { - func(*ptr); - } - } - ++seq; - } - } - -private: - void pop_front() { - if (_nack_cache_seq.empty()) { - return; - } - _nack_cache_pkt.erase(_nack_cache_seq.front()); - _nack_cache_seq.pop_front(); - } - - RtpPacket::Ptr *get_rtp(uint16_t seq) { - auto it = _nack_cache_pkt.find(seq); - if (it == _nack_cache_pkt.end()) { - return nullptr; - } - return &it->second; - } - - uint32_t get_cache_ms() { - if (_nack_cache_seq.size() < 2) { - return 0; - } - uint32_t back = _nack_cache_pkt[_nack_cache_seq.back()]->getStampMS(); - uint32_t front = _nack_cache_pkt[_nack_cache_seq.front()]->getStampMS(); - if (back > front) { - return back - front; - } - //很有可能回环了 - return back + (UINT32_MAX - front); - } - -private: - static constexpr uint32_t kMaxNackMS = 10 * 1000; - deque _nack_cache_seq; - unordered_map _nack_cache_pkt; -}; - -class NackContext { -public: - using onNack = function; - - void received(uint16_t seq) { - if (!_last_max_seq && _seq.empty()) { - _last_max_seq = seq - 1; - } - _seq.emplace(seq); - auto max_seq = *_seq.rbegin(); - auto min_seq = *_seq.begin(); - auto diff = max_seq - min_seq; - if (!diff) { - return; - } - - if (diff > UINT32_MAX / 2) { - //回环 - _seq.clear(); - _last_max_seq = min_seq; - return; - } - - if (_seq.size() == diff + 1 && _last_max_seq + 1 == min_seq) { - //都是连续的seq,未丢包 - _seq.clear(); - _last_max_seq = max_seq; - } else { - //seq不连续,有丢包 - if (min_seq == _last_max_seq + 1) { - //前面部分seq是连续的,未丢包,移除之 - eraseFrontSeq(); - } - - //有丢包,丢包从_last_max_seq开始 - if (max_seq - _last_max_seq > FCI_NACK::kBitSize) { - vector vec; - vec.resize(FCI_NACK::kBitSize); - for (auto i = 0; i < FCI_NACK::kBitSize; ++i) { - vec[i] = _seq.find(_last_max_seq + i + 2) == _seq.end(); - } - doNack(FCI_NACK(_last_max_seq + 1, vec)); - _last_max_seq += FCI_NACK::kBitSize + 1; - if (_last_max_seq >= max_seq) { - _seq.clear(); - } else { - auto it = _seq.emplace_hint(_seq.begin(), _last_max_seq); - _seq.erase(_seq.begin(), it); - } - } - } - } - - void setOnNack(onNack cb) { - _cb = std::move(cb); - } - -private: - void doNack(const FCI_NACK &nack) { - if (_cb) { - _cb(nack); - } - } - - void eraseFrontSeq(){ - //前面部分seq是连续的,未丢包,移除之 - for (auto it = _seq.begin(); it != _seq.end();) { - if (*it != _last_max_seq + 1) { - //seq不连续,丢包了 - break; - } - _last_max_seq = *it; - it = _seq.erase(it); - } - } - -private: - onNack _cb; - set _seq; - uint16_t _last_max_seq = 0; -}; - class MediaTrack { public: using Ptr = std::shared_ptr;