RtcpFCI.cpp 16.5 KB
Newer Older
xiongziliang committed
1 2 3 4 5 6 7 8 9 10
/*
 * Copyright (c) 2016 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.
 */

11
#include "RtcpFCI.h"
xia-chu committed
12
#include "Util/logger.h"
夏楚 committed
13 14

using namespace std;
xia-chu committed
15
using namespace toolkit;
xiongziliang committed
16 17 18

namespace mediakit {

19
void FCI_SLI::check(size_t size) {
xia-chu committed
20
    CHECK(size >= kSize);
xia-chu committed
21 22
}

xia-chu committed
23
FCI_SLI::FCI_SLI(uint16_t first, uint16_t number, uint8_t pic_id) {
24
    // 13 bits
xia-chu committed
25
    first &= 0x1FFF;
26
    // 13 bits
xia-chu committed
27
    number &= 0x1FFF;
28
    // 6 bits
xia-chu committed
29 30 31 32 33 34
    pic_id &= 0x3F;
    data = (first << 19) | (number << 6) | pic_id;
    data = htonl(data);
}

uint16_t FCI_SLI::getFirst() const {
xia-chu committed
35
    return ntohl(data) >> 19;
xia-chu committed
36 37 38
}

uint16_t FCI_SLI::getNumber() const {
xia-chu committed
39
    return (ntohl(data) >> 6) & 0x1FFF;
xia-chu committed
40 41 42
}

uint8_t FCI_SLI::getPicID() const {
xia-chu committed
43
    return ntohl(data) & 0x3F;
xia-chu committed
44 45 46 47 48 49 50 51
}

string FCI_SLI::dumpString() const {
    return StrPrinter << "First:" << getFirst() << ", Number:" << getNumber() << ", PictureID:" << (int)getPicID();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

52
void FCI_FIR::check(size_t size) {
xia-chu committed
53
    CHECK(size >= kSize);
xia-chu committed
54 55
}

56
uint32_t FCI_FIR::getSSRC() const {
xia-chu committed
57 58 59
    return ntohl(ssrc);
}

60
uint8_t FCI_FIR::getSeq() const {
xia-chu committed
61 62 63
    return seq_number;
}

64
uint32_t FCI_FIR::getReserved() const {
xia-chu committed
65
    return (reserved[0] << 16) | (reserved[1] << 8) | reserved[2];
xia-chu committed
66 67 68
}

string FCI_FIR::dumpString() const {
xia-chu committed
69
    return StrPrinter << "ssrc:" << getSSRC() << ", seq_number:" << (int)getSeq() << ", reserved:" << getReserved();
xia-chu committed
70 71 72 73 74
}

FCI_FIR::FCI_FIR(uint32_t ssrc, uint8_t seq_number, uint32_t reserved) {
    this->ssrc = htonl(ssrc);
    this->seq_number = seq_number;
xia-chu committed
75 76 77
    this->reserved[0] = (reserved >> 16) & 0xFF;
    this->reserved[1] = (reserved >> 8) & 0xFF;
    this->reserved[2] = reserved & 0xFF;
xia-chu committed
78 79 80 81
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
82 83
static const char kRembMagic[] = "REMB";

84
void FCI_REMB::check(size_t size) {
xia-chu committed
85 86 87 88
    CHECK(size >= kSize);
    CHECK(memcmp(magic, kRembMagic, sizeof(magic)) == 0);
    auto num_ssrc = bitrate[0];
    auto expect_size = kSize + 4 * num_ssrc;
xia-chu committed
89
    CHECK(size >= expect_size);
xia-chu committed
90 91 92
}

string FCI_REMB::create(const vector<uint32_t> &ssrcs, uint32_t bitrate) {
xia-chu committed
93 94 95
    CHECK(ssrcs.size() > 0 && ssrcs.size() <= 0xFF);
    string ret;
    ret.resize(kSize + ssrcs.size() * 4);
96
    FCI_REMB *thiz = (FCI_REMB *)ret.data();
xia-chu committed
97
    memcpy(thiz->magic, kRembMagic, sizeof(magic));
xia-chu committed
98 99 100 101 102 103

    /* bitrate --> BR Exp/BR Mantissa */
    uint8_t b = 0;
    uint8_t exp = 0;
    uint32_t mantissa = 0;
    for (b = 0; b < 32; b++) {
104
        if (bitrate <= ((uint32_t)0x3FFFF << b)) {
xia-chu committed
105 106 107 108 109 110 111 112
            exp = b;
            break;
        }
    }
    if (b > 31) {
        b = 31;
    }
    mantissa = bitrate >> b;
113
    // Num SSRC (8 bits)
xia-chu committed
114
    thiz->bitrate[0] = ssrcs.size() & 0xFF;
115 116 117 118 119 120 121 122
    // BR Exp (6 bits)/BR Mantissa (18 bits)
    thiz->bitrate[1] = (uint8_t)((exp << 2) + ((mantissa >> 16) & 0x03));
    // BR Mantissa (18 bits)
    thiz->bitrate[2] = (uint8_t)(mantissa >> 8);
    // BR Mantissa (18 bits)
    thiz->bitrate[3] = (uint8_t)(mantissa);

    // 设置ssrc列表
ziyue committed
123
    int i = 0;
xia-chu committed
124
    for (auto ssrc : ssrcs) {
ziyue committed
125
        thiz->ssrc_feedback[i++] = htonl(ssrc);
xia-chu committed
126 127 128 129 130 131 132 133 134 135 136 137
    }
    return ret;
}

uint32_t FCI_REMB::getBitRate() const {
    uint8_t exp = (bitrate[1] >> 2) & 0x3F;
    uint32_t mantissa = (bitrate[1] & 0x03) << 16;
    mantissa += (bitrate[2] << 8);
    mantissa += (bitrate[3]);
    return mantissa << exp;
}

xia-chu committed
138 139
vector<uint32_t> FCI_REMB::getSSRC() {
    vector<uint32_t> ret;
xia-chu committed
140
    auto num_ssrc = bitrate[0];
ziyue committed
141
    int i = 0;
xia-chu committed
142
    while (num_ssrc--) {
ziyue committed
143 144
        ret.emplace_back(ntohl(ssrc_feedback[i]));
        ++i;
xia-chu committed
145 146 147 148 149 150 151
    }
    return ret;
}

string FCI_REMB::dumpString() const {
    _StrPrinter printer;
    printer << "bitrate:" << getBitRate() << ", ssrc:";
152
    for (auto &ssrc : ((FCI_REMB *)this)->getSSRC()) {
xia-chu committed
153
        printer << ssrc << " ";
xia-chu committed
154
    }
155
    return std::move(printer);
xia-chu committed
156
}
xiongziliang committed
157

xia-chu committed
158 159
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
160
FCI_NACK::FCI_NACK(uint16_t pid_h, const vector<bool> &type) {
ziyue committed
161
    assert(type.size() <= kBitSize);
xia-chu committed
162
    uint16_t blp_h = 0;
ziyue committed
163
    int i = 0;
xia-chu committed
164 165 166 167
    for (auto item : type) {
        if (item) {
            blp_h |= (1 << i);
        }
ziyue committed
168
        ++i;
xia-chu committed
169 170 171 172 173
    }
    blp = htons(blp_h);
    pid = htons(pid_h);
}

174
void FCI_NACK::check(size_t size) {
xia-chu committed
175
    CHECK(size >= kSize);
xia-chu committed
176 177 178 179 180 181 182 183
}

uint16_t FCI_NACK::getPid() const {
    return ntohs(pid);
}

uint16_t FCI_NACK::getBlp() const {
    return ntohs(blp);
xia-chu committed
184 185 186 187
}

vector<bool> FCI_NACK::getBitArray() const {
    vector<bool> ret;
188
    ret.resize(kBitSize + 1);
189
    // nack第一个包丢包
xia-chu committed
190
    ret[0] = true;
191

xia-chu committed
192
    auto blp_h = getBlp();
xia-chu committed
193
    for (size_t i = 0; i < kBitSize; ++i) {
ziyue committed
194
        ret[i + 1] = blp_h & (1 << i);
xia-chu committed
195 196 197 198 199 200
    }
    return ret;
}

string FCI_NACK::dumpString() const {
    _StrPrinter printer;
201 202
    auto pid = getPid();
    printer << "pid:" << pid << ",blp:" << getBlp() << ",dropped rtp seq:";
xia-chu committed
203
    for (auto flag : getBitArray()) {
204 205 206 207
        if (flag) {
            printer << pid << " ";
        }
        ++pid;
xia-chu committed
208 209 210 211
    }
    return std::move(printer);
}

xia-chu committed
212 213
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
214 215 216 217 218 219 220 221 222
class RunLengthChunk {
public:
    static size_t constexpr kSize = 2;
    //  0                   1
    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    // |T| S |       Run Length        |
    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#if __BYTE_ORDER == __BIG_ENDIAN
223 224 225
    uint16_t type : 1;
    uint16_t symbol : 2;
    uint16_t run_length_high : 5;
xia-chu committed
226 227
#else
    // Run Length 高5位
228 229 230 231 232
    uint16_t run_length_high : 5;
    // 参考SymbolStatus定义
    uint16_t symbol : 2;
    // 固定为0
    uint16_t type : 1;
xia-chu committed
233 234
#endif
    // Run Length 低8位
235
    uint16_t run_length_low : 8;
xia-chu committed
236

237
    // 获取Run Length
xia-chu committed
238
    uint16_t getRunLength() const;
239
    // 构造函数
xia-chu committed
240
    RunLengthChunk(SymbolStatus status, uint16_t run_length);
241
    // 打印本对象
xia-chu committed
242 243 244
    string dumpString() const;
} PACKED;

xia-chu committed
245 246 247 248 249 250 251 252 253 254 255 256
RunLengthChunk::RunLengthChunk(SymbolStatus status, uint16_t run_length) {
    type = 0;
    symbol = (uint8_t)status & 0x03;
    run_length_high = (run_length >> 8) & 0x1F;
    run_length_low = run_length & 0xFF;
}

uint16_t RunLengthChunk::getRunLength() const {
    CHECK(type == 0);
    return run_length_high << 8 | run_length_low;
}

257
string RunLengthChunk::dumpString() const {
xia-chu committed
258 259 260 261 262 263 264
    _StrPrinter printer;
    printer << "run length chunk, symbol:" << (int)symbol << ", run length:" << getRunLength();
    return std::move(printer);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
265 266 267 268 269 270 271 272 273
class StatusVecChunk {
public:
    static size_t constexpr kSize = 2;
    // 0                   1
    // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    // |T|S|       symbol list         |
    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#if __BYTE_ORDER == __BIG_ENDIAN
274 275 276
    uint16_t type : 1;
    uint16_t symbol : 1;
    uint16_t symbol_list_high : 6;
xia-chu committed
277 278
#else
    // symbol_list 高6位
279 280 281 282 283
    uint16_t symbol_list_high : 6;
    // symbol_list中元素是1个还是2个bit
    uint16_t symbol : 1;
    // 固定为1
    uint16_t type : 1;
xia-chu committed
284 285
#endif
    // symbol_list 低8位
286
    uint16_t symbol_list_low : 8;
xia-chu committed
287

288
    // 获取symbollist
xia-chu committed
289
    vector<SymbolStatus> getSymbolList() const;
290
    // 构造函数
291
    StatusVecChunk(bool symbol_bit, const vector<SymbolStatus> &status);
292
    // 打印本对象
xia-chu committed
293 294 295
    string dumpString() const;
} PACKED;

296
StatusVecChunk::StatusVecChunk(bool symbol_bit, const vector<SymbolStatus> &status) {
297
    CHECK(status.size() << symbol_bit <= 14);
xia-chu committed
298 299
    uint16_t value = 0;
    type = 1;
300
    symbol = symbol_bit;
xia-chu committed
301 302 303 304 305
    int i = 13;
    for (auto &item : status) {
        CHECK(item <= SymbolStatus::reserved);
        if (!symbol) {
            CHECK(item <= SymbolStatus::small_delta);
306
            value |= (int)item << i;
xia-chu committed
307 308
            --i;
        } else {
309
            value |= (int)item << (i - 1);
xia-chu committed
310 311 312 313
            i -= 2;
        }
    }
    symbol_list_low = value & 0xFF;
314
    symbol_list_high = (value >> 8) & 0x3F;
xia-chu committed
315 316 317 318 319
}

vector<SymbolStatus> StatusVecChunk::getSymbolList() const {
    CHECK(type == 1);
    vector<SymbolStatus> ret;
320
    auto thiz = ntohs(*((uint16_t *)this));
xia-chu committed
321
    if (symbol == 0) {
322
        // s = 0 时,表示symbollist的每一个bit能表示一个数据包的到达状态
xia-chu committed
323
        for (int i = 13; i >= 0; --i) {
324
            SymbolStatus status = (SymbolStatus)((bool)(thiz & (1 << i)));
xia-chu committed
325 326 327
            ret.emplace_back(status);
        }
    } else {
328
        // s = 1 时,表示symbollist每两个bit表示一个数据包的状态
xia-chu committed
329
        for (int i = 12; i >= 0; i -= 2) {
330
            SymbolStatus status = (SymbolStatus)((thiz & (3 << i)) >> i);
xia-chu committed
331 332 333 334 335 336 337 338
            ret.emplace_back(status);
        }
    }
    return ret;
}

string StatusVecChunk::dumpString() const {
    _StrPrinter printer;
339
    printer << "status vector chunk, symbol:" << (int)symbol << ", symbol list:";
xia-chu committed
340 341
    auto vec = getSymbolList();
    for (auto &item : vec) {
342
        printer << (int)item << " ";
xia-chu committed
343 344 345
    }
    return std::move(printer);
}
xia-chu committed
346

xia-chu committed
347 348
///////////////////////////////////////////////////////

349
void FCI_TWCC::check(size_t size) {
xia-chu committed
350 351 352 353 354 355 356 357 358
    CHECK(size >= kSize);
}

uint16_t FCI_TWCC::getBaseSeq() const {
    return ntohs(base_seq);
}

uint16_t FCI_TWCC::getPacketCount() const {
    return ntohs(pkt_status_count);
xia-chu committed
359 360 361 362 363 364 365 366 367
}

uint32_t FCI_TWCC::getReferenceTime() const {
    uint32_t ret = 0;
    ret |= ref_time[0] << 16;
    ret |= ref_time[1] << 8;
    ret |= ref_time[2];
    return ret;
}
368
// 3.1.5.  Receive Delta
xia-chu committed
369
//
370
//    Deltas are represented as multiples of 250us:
xia-chu committed
371
//
372 373 374 375
//    o  If the "Packet received, small delta" symbol has been appended to
//       the status list, an 8-bit unsigned receive delta will be appended
//       to recv delta list, representing a delta in the range [0, 63.75]
//       ms.
xia-chu committed
376
//
377 378 379 380
//    o  If the "Packet received, large or negative delta" symbol has been
//       appended to the status list, a 16-bit signed receive delta will be
//       appended to recv delta list, representing a delta in the range
//       [-8192.0, 8191.75] ms.
xia-chu committed
381
//
382 383 384
//    o  If the delta exceeds even the larger limits, a new feedback
//       message must be used, where the 24-bit base receive delta can
//       cover very large gaps.
xia-chu committed
385
//
386 387 388 389
//    The smaller receive delta upper bound of 63.75 ms means that this is
//    only viable at about 1000/25.5 ~= 16 packets per second and above.
//    With a packet size of 1200 bytes/packet that amounts to a bitrate of
//    about 150 kbit/s.
xia-chu committed
390
//
391 392 393
//    The 0.25 ms resolution means that up to 4000 packets per second can
//    be represented.  With a 1200 bytes/packet payload, that amounts to
//    38.4 Mbit/s payload bandwidth.
xia-chu committed
394

395
static int16_t getRecvDelta(SymbolStatus status, uint8_t *&ptr, const uint8_t *end) {
xia-chu committed
396
    int16_t delta = 0;
xia-chu committed
397
    switch (status) {
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    case SymbolStatus::not_received: {
        // 丢包, recv delta为0个字节
        break;
    }
    case SymbolStatus::small_delta: {
        CHECK(ptr + 1 <= end);
        // 时间戳增量小于256, recv delta为1个字节
        delta = *ptr;
        ptr += 1;
        break;
    }
    case SymbolStatus::large_delta: {
        CHECK(ptr + 2 <= end);
        // 时间戳增量256~65535间,recv delta为2个字节
        delta = *ptr << 8 | *(ptr + 1);
        ptr += 2;
        break;
    }
    case SymbolStatus::reserved: {
        // 没有时间戳
        break;
    }
    default:
        // 这个逻辑分支不可达到
        CHECK(0);
        break;
xia-chu committed
424 425 426 427
    }
    return delta;
}

428 429
FCI_TWCC::TwccPacketStatus FCI_TWCC::getPacketChunkList(size_t total_size) const {
    TwccPacketStatus ret;
430 431
    auto ptr = (uint8_t *)this + kSize;
    auto end = (uint8_t *)this + total_size;
xia-chu committed
432
    CHECK(ptr < end);
xia-chu committed
433
    auto seq = getBaseSeq();
xia-chu committed
434 435
    auto rtp_count = getPacketCount();
    for (uint8_t i = 0; i < rtp_count;) {
xia-chu committed
436
        CHECK(ptr + RunLengthChunk::kSize <= end);
437
        RunLengthChunk *chunk = (RunLengthChunk *)ptr;
xia-chu committed
438
        if (!chunk->type) {
439
            // RunLengthChunk
xia-chu committed
440
            for (auto j = 0; j < chunk->getRunLength(); ++j) {
441
                ret.emplace(seq++, std::make_pair((SymbolStatus)chunk->symbol, 0));
xia-chu committed
442 443 444
                if (++i >= rtp_count) {
                    break;
                }
xia-chu committed
445 446
            }
        } else {
447 448
            // StatusVecChunk
            StatusVecChunk *chunk = (StatusVecChunk *)ptr;
xia-chu committed
449
            for (auto &symbol : chunk->getSymbolList()) {
xia-chu committed
450
                ret.emplace(seq++, std::make_pair(symbol, 0));
xia-chu committed
451 452 453
                if (++i >= rtp_count) {
                    break;
                }
xia-chu committed
454 455
            }
        }
xia-chu committed
456 457 458
        ptr += 2;
    }
    for (auto &pr : ret) {
xia-chu committed
459
        CHECK(ptr <= end);
460
        pr.second.second = getRecvDelta(pr.second.first, ptr, end);
xia-chu committed
461 462 463 464 465 466 467
    }
    return ret;
}

string FCI_TWCC::dumpString(size_t total_size) const {
    _StrPrinter printer;
    auto map = getPacketChunkList(total_size);
468 469
    printer << "twcc fci, base_seq:" << getBaseSeq() << ", pkt_status_count:" << getPacketCount()
            << ", ref time:" << getReferenceTime() << ", fb count:" << (int)fb_pkt_count << "\n";
xia-chu committed
470
    for (auto &pr : map) {
471 472
        printer << "rtp seq:" << pr.first << ", packet status:" << (int)(pr.second.first)
                << ", delta:" << pr.second.second << "\n";
xia-chu committed
473 474 475 476
    }
    return std::move(printer);
}

477
static void appendDeltaString(string &delta_str, FCI_TWCC::TwccPacketStatus &status, int count) {
478 479
    for (auto it = status.begin(); it != status.end() && count--;) {
        switch (it->second.first) {
480 481 482 483 484 485 486 487 488
        // large delta模式先写高字节,再写低字节
        case SymbolStatus::large_delta:
            delta_str.push_back((it->second.second >> 8) & 0xFF);
            // small delta模式只写低字节
        case SymbolStatus::small_delta:
            delta_str.push_back(it->second.second & 0xFF);
            break;
        default:
            break;
489
        }
490
        // 移除已经处理过的数据
491
        it = status.erase(it);
xia-chu committed
492
    }
493
}
xia-chu committed
494

495 496 497
string FCI_TWCC::create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatus &status) {
    string fci;
    fci.resize(FCI_TWCC::kSize);
498
    FCI_TWCC *ptr = (FCI_TWCC *)(fci.data());
499 500 501 502 503 504 505 506 507 508
    ptr->base_seq = htons(status.begin()->first);
    ptr->pkt_status_count = htons(status.size());
    ptr->fb_pkt_count = fb_pkt_count;
    ptr->ref_time[0] = (ref_time >> 16) & 0xFF;
    ptr->ref_time[1] = (ref_time >> 8) & 0xFF;
    ptr->ref_time[2] = (ref_time >> 0) & 0xFF;

    string delta_str;
    while (!status.empty()) {
        {
509
            // 第一个rtp的状态
510 511 512 513
            auto symbol = status.begin()->second.first;
            int16_t count = 0;
            for (auto &pr : status) {
                if (pr.second.first != symbol) {
514
                    // 状态发送变更了,本chunk结束
515 516
                    break;
                }
517
                if (++count >= (0xFFFF >> 3)) {
518
                    // RunLengthChunk 13个bit表明rtp个数,最多可以表述0xFFFF >> 3个rtp状态
519 520
                    break;
                }
521 522
            }
            if (count >= 7) {
523
                // 连续状态相同个数大于6个时,使用RunLengthChunk模式比较节省带宽
524 525 526 527 528 529
                RunLengthChunk chunk(symbol, count);
                fci.append((char *)&chunk, RunLengthChunk::kSize);
                appendDeltaString(delta_str, status, count);
                continue;
            }
        }
xia-chu committed
530

531
        {
532 533
            // StatusVecChunk模式
            // symbol_list中元素是1个bit
534 535 536 537 538
            auto symbol = 0;
            vector<SymbolStatus> vec;
            for (auto &pr : status) {
                vec.push_back(pr.second.first);
                if (pr.second.first >= SymbolStatus::large_delta) {
539
                    // symbol_list中元素是2个bit
540 541
                    symbol = 1;
                }
542 543

                if (vec.size() << symbol >= 14) {
544 545
                    // symbol为0时,最多存放14个rtp的状态
                    // symbol为1时,最多存放7个rtp的状态
546 547 548
                    break;
                }
            }
ziyue committed
549
            vec.resize(MIN(vec.size(), (size_t)14 >> symbol));
550 551
            StatusVecChunk chunk(symbol, vec);
            fci.append((char *)&chunk, StatusVecChunk::kSize);
552
            appendDeltaString(delta_str, status, vec.size());
xia-chu committed
553 554
        }
    }
555

556
    // recv delta部分
557 558
    fci.append(delta_str);
    return fci;
xia-chu committed
559
}
560

561
} // namespace mediakit