TwccContext.cpp 3.94 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11
 * 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"
12 13
#include "Rtcp/RtcpFCI.h"

14
namespace mediakit {
15 16 17 18 19 20 21

enum class ExtSeqStatus : int {
    normal = 0,
    looped,
    jumped,
};

22
void TwccContext::onRtp(uint32_t ssrc, uint16_t twcc_ext_seq, uint64_t stamp_ms) {
23
    switch ((ExtSeqStatus) checkSeqStatus(twcc_ext_seq)) {
24
        case ExtSeqStatus::jumped: /*seq异常,过滤掉*/ return;
25
        case ExtSeqStatus::looped: /*回环,触发发送twcc rtcp*/ onSendTwcc(ssrc); break;
26 27 28 29
        case ExtSeqStatus::normal: break;
        default: /*不可达*/assert(0); break;
    }

30
    auto result = _rtp_recv_status.emplace(twcc_ext_seq, stamp_ms);
31 32 33 34 35 36 37 38 39 40
    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;
    }

41
    if (needSendTwcc()) {
42
        //其他匹配条件立即发送twcc
43
        onSendTwcc(ssrc);
44 45 46
    }
}

47 48
bool TwccContext::needSendTwcc() const {
    if (_rtp_recv_status.empty()) {
49 50
        return false;
    }
51
    return (_rtp_recv_status.size() >= kMaxSeqSize) || (_max_stamp - _min_stamp >= kMaxTimeDelta);
52 53 54 55 56 57 58
}

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;
59 60 61 62 63 64 65
    auto delta = (int32_t) twcc_ext_seq - (int32_t) max;
    if (delta > 0 && delta < 0xFFFF / 2) {
        //正常增长
        return (int) ExtSeqStatus::normal;
    }
    if (delta < -0xFF00) {
        //回环
66 67 68
        TraceL << "rtp twcc ext seq looped:" << max << " -> " << twcc_ext_seq;
        return (int) ExtSeqStatus::looped;
    }
69 70 71
    if (delta > 0xFF00) {
        //回环后收到前面大的乱序的包,无法处理,丢弃
        TraceL << "rtp twcc ext seq jumped after looped:" << max << " -> " << twcc_ext_seq;
72 73
        return (int) ExtSeqStatus::jumped;
    }
74 75 76 77 78 79 80 81
    auto min = _rtp_recv_status.begin()->first;
    if (min <= twcc_ext_seq || twcc_ext_seq <= max) {
        //正常回退
        return (int) ExtSeqStatus::normal;
    }
    //seq莫名的大幅增加或减少,无法处理,丢弃
    TraceL << "rtp twcc ext seq jumped:" << max << " -> " << twcc_ext_seq;
    return (int) ExtSeqStatus::jumped;
82 83
}

84
void TwccContext::onSendTwcc(uint32_t ssrc) {
85 86 87
    auto max = _rtp_recv_status.rbegin()->first;
    auto begin = _rtp_recv_status.begin();
    auto min = begin->first;
88
    //参考时间戳的最小单位是64ms
89
    auto ref_time = begin->second >> 6;
90
    //还原基准时间戳
91
    auto last_time = ref_time << 6;
92 93 94 95 96 97 98
    FCI_TWCC::TwccPacketStatus status;
    for (auto seq = min; seq <= max; ++seq) {
        int16_t delta = 0;
        SymbolStatus symbol = SymbolStatus::not_received;
        auto it = _rtp_recv_status.find(seq);
        if (it != _rtp_recv_status.end()) {
            //recv delta,单位为250us,1ms等于4x250us
99
            delta = (int16_t) (4 * ((int64_t) it->second - (int64_t) last_time));
100 101 102 103 104
            if (delta < 0 || delta > 0xFF) {
                symbol = SymbolStatus::large_delta;
            } else {
                symbol = SymbolStatus::small_delta;
            }
105
            last_time = it->second;
106
        }
107
        status.emplace(seq, std::make_pair(symbol, delta));
108
    }
109 110 111 112
    auto fci = FCI_TWCC::create(ref_time, _twcc_pkt_count++, status);
    if (_cb) {
        _cb(ssrc, std::move(fci));
    }
113 114 115 116 117 118 119
    clearStatus();
}

void TwccContext::clearStatus() {
    _rtp_recv_status.clear();
    _min_stamp = 0;
}
120 121 122 123

void TwccContext::setOnSendTwccCB(TwccContext::onSendTwccCB cb) {
    _cb = std::move(cb);
}
124 125

}// namespace mediakit