RtcpFCI.cpp 14.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 13
#include "Util/logger.h"
using namespace toolkit;
xiongziliang committed
14 15 16

namespace mediakit {

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

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

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

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

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

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

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

xia-chu committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63
void FCI_FIR::check(size_t size){
    CHECK(size == kSize);
}

uint32_t FCI_FIR::getSSRC() const{
    return ntohl(ssrc);
}

uint8_t FCI_FIR::getSeq() const{
    return seq_number;
}

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

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

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
73 74 75
    this->reserved[0] = (reserved >> 16) & 0xFF;
    this->reserved[1] = (reserved >> 8) & 0xFF;
    this->reserved[2] = reserved & 0xFF;
xia-chu committed
76 77 78 79
}

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

xia-chu committed
80 81 82 83 84 85 86 87 88 89 90
static const char kRembMagic[] = "REMB";

void FCI_REMB::check(size_t size){
    CHECK(size >= kSize);
    CHECK(memcmp(magic, kRembMagic, sizeof(magic)) == 0);
    auto num_ssrc = bitrate[0];
    auto expect_size = kSize + 4 * num_ssrc;
    CHECK(size == expect_size);
}

string FCI_REMB::create(const vector<uint32_t> &ssrcs, uint32_t bitrate) {
xia-chu committed
91 92 93 94
    CHECK(ssrcs.size() > 0 && ssrcs.size() <= 0xFF);
    string ret;
    ret.resize(kSize + ssrcs.size() * 4);
    FCI_REMB *thiz = (FCI_REMB *) ret.data();
xia-chu committed
95
    memcpy(thiz->magic, kRembMagic, sizeof(magic));
xia-chu committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

    /* bitrate --> BR Exp/BR Mantissa */
    uint8_t b = 0;
    uint8_t exp = 0;
    uint32_t mantissa = 0;
    for (b = 0; b < 32; b++) {
        if (bitrate <= ((uint32_t) 0x3FFFF << b)) {
            exp = b;
            break;
        }
    }
    if (b > 31) {
        b = 31;
    }
    mantissa = bitrate >> b;
    //Num SSRC (8 bits)
    thiz->bitrate[0] = ssrcs.size() & 0xFF;
    //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列表
    auto ptr = thiz->ssrc_feedback;
xia-chu committed
122
    for (auto ssrc : ssrcs) {
xia-chu committed
123 124 125 126 127 128 129 130 131 132 133 134 135
        *(ptr++) = htonl(ssrc);
    }
    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
136 137
vector<uint32_t> FCI_REMB::getSSRC() {
    vector<uint32_t> ret;
xia-chu committed
138 139 140
    auto num_ssrc = bitrate[0];
    auto ptr = ssrc_feedback;
    while (num_ssrc--) {
xia-chu committed
141
        ret.emplace_back(ntohl(*ptr++));
xia-chu committed
142 143 144 145 146 147 148 149
    }
    return ret;
}

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

xia-chu committed
155 156
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
FCI_NACK::FCI_NACK(uint16_t pid_h, const vector<bool> &type) {
    uint16_t blp_h = 0;
    int i = kBitSize;
    for (auto item : type) {
        --i;
        if (item) {
            blp_h |= (1 << i);
        }
    }
    blp = htons(blp_h);
    pid = htons(pid_h);
}

void FCI_NACK::check(size_t size){
    CHECK(size == kSize);
}

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

uint16_t FCI_NACK::getBlp() const {
    return ntohs(blp);
xia-chu committed
180 181 182 183 184
}

vector<bool> FCI_NACK::getBitArray() const {
    vector<bool> ret;
    ret.resize(kBitSize);
xia-chu committed
185
    auto blp_h = getBlp();
xia-chu committed
186
    for (size_t i = 0; i < kBitSize; ++i) {
xia-chu committed
187
        ret[i] = blp_h & (1 << (kBitSize - i - 1));
xia-chu committed
188 189 190 191 192 193
    }
    return ret;
}

string FCI_NACK::dumpString() const {
    _StrPrinter printer;
xia-chu committed
194
    printer << "pid:" << getPid() << ",blp:";
xia-chu committed
195
    for (auto flag : getBitArray()) {
xia-chu committed
196 197 198 199 200
        printer << flag << " ";
    }
    return std::move(printer);
}

xia-chu committed
201 202
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

xia-chu committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
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
    uint16_t type: 1;
    uint16_t symbol: 2;
    uint16_t run_length_high: 5;
#else
    // Run Length 高5位
    uint16_t run_length_high: 5;
    //参考SymbolStatus定义
    uint16_t symbol: 2;
    //固定为0
    uint16_t type: 1;
#endif
    // Run Length 低8位
    uint16_t run_length_low: 8;

    //获取Run Length
    uint16_t getRunLength() const;
    //构造函数
    RunLengthChunk(SymbolStatus status, uint16_t run_length);
    //打印本对象
    string dumpString() const;
} PACKED;

xia-chu committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
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;
}

string RunLengthChunk::dumpString() const{
    _StrPrinter printer;
    printer << "run length chunk, symbol:" << (int)symbol << ", run length:" << getRunLength();
    return std::move(printer);
}

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

xia-chu committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
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
    uint16_t type: 1;
    uint16_t symbol: 1;
    uint16_t symbol_list_high: 6;
#else
    // symbol_list 高6位
    uint16_t symbol_list_high: 6;
    //symbol_list中元素是1个还是2个bit
    uint16_t symbol: 1;
    //固定为1
    uint16_t type: 1;
#endif
    // symbol_list 低8位
    uint16_t symbol_list_low: 8;

    //获取symbollist
    vector<SymbolStatus> getSymbolList() const;
    //构造函数
    StatusVecChunk(const vector<SymbolStatus> &status);
    //打印本对象
    string dumpString() const;
} PACKED;

xia-chu committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
StatusVecChunk::StatusVecChunk(const vector<SymbolStatus> &status) {
    uint16_t value = 0;
    type = 1;
    if (status.size() == 14) {
        symbol = 0;
    } else if (status.size() == 7) {
        symbol = 1;
    } else {
        //非法
        CHECK(0);
    }
    int i = 13;
    for (auto &item : status) {
        CHECK(item <= SymbolStatus::reserved);
        if (!symbol) {
            CHECK(item <= SymbolStatus::small_delta);
            value |= (int) item << i;
            --i;
        } else {
            value |= (int) item << (i - 1);
            i -= 2;
        }
    }
    symbol_list_low = value & 0xFF;
    symbol_list_high = (value >> 8 ) & 0x1F;
}

vector<SymbolStatus> StatusVecChunk::getSymbolList() const {
    CHECK(type == 1);
    vector<SymbolStatus> ret;
    auto thiz = ntohs(*((uint16_t *) this));
    if (symbol == 0) {
        //s = 0 时,表示symbollist的每一个bit能表示一个数据包的到达状态
        for (int i = 13; i >= 0; --i) {
            SymbolStatus status = (SymbolStatus) ((bool) (thiz & (1 << i)));
            ret.emplace_back(status);
        }
    } else {
        //s = 1 时,表示symbollist每两个bit表示一个数据包的状态
        for (int i = 12; i >= 0; i -= 2) {
            SymbolStatus status = (SymbolStatus) ((thiz & (3 << i)) >> i);
            ret.emplace_back(status);
        }
    }
    return ret;
}

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

xia-chu committed
342 343
///////////////////////////////////////////////////////

xia-chu committed
344 345 346 347 348 349 350 351 352 353
void FCI_TWCC::check(size_t size){
    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
354 355 356 357 358 359 360 361 362
}

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;
}
xia-chu committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
//3.1.5.  Receive Delta
//
//   Deltas are represented as multiples of 250us:
//
//   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.
//
//   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.
//
//   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.
//
//   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.
//
//   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.

static int16_t getRecvDelta(SymbolStatus status, uint8_t *&ptr, const uint8_t *end){
    int16_t delta = 0;
xia-chu committed
392 393 394 395 396 397 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 424
    switch (status) {
        case SymbolStatus::not_received : {
            //丢包, recv delta为0个字节
            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;
        }
        default:
            //这个逻辑分支不可达到
            CHECK(0);
            break;
    }
    return delta;
}

map<uint16_t, std::pair<SymbolStatus, uint32_t/*stamp*/> > FCI_TWCC::getPacketChunkList(size_t total_size) const {
    map<uint16_t, std::pair<SymbolStatus, uint32_t> > ret;
    auto ptr = (uint8_t *) this + kSize;
    auto end = (uint8_t *) this + total_size;
    CHECK(ptr < end);
xia-chu committed
425
    auto seq = getBaseSeq();
xia-chu committed
426

xia-chu committed
427 428
    for (uint8_t i = 0; i < getPacketCount();) {
        CHECK(ptr + RunLengthChunk::kSize <= end);
xia-chu committed
429 430 431 432
        RunLengthChunk *chunk = (RunLengthChunk *) ptr;
        if (!chunk->type) {
            //RunLengthChunk
            for (auto j = 0; j < chunk->getRunLength(); ++j) {
xia-chu committed
433
                ret.emplace(seq++, std::make_pair((SymbolStatus) chunk->symbol, 0));
xia-chu committed
434 435 436 437 438 439
                ++i;
            }
        } else {
            //StatusVecChunk
            StatusVecChunk *chunk = (StatusVecChunk *) ptr;
            for (auto &symbol : chunk->getSymbolList()) {
xia-chu committed
440
                ret.emplace(seq++, std::make_pair(symbol, 0));
xia-chu committed
441 442 443
                ++i;
            }
        }
xia-chu committed
444 445 446
        ptr += 2;
    }
    for (auto &pr : ret) {
xia-chu committed
447
        CHECK(ptr <= end);
xia-chu committed
448
        pr.second.second = 250 * getRecvDelta(pr.second.first, ptr, end);
xia-chu committed
449 450 451 452 453 454 455
    }
    return ret;
}

string FCI_TWCC::dumpString(size_t total_size) const {
    _StrPrinter printer;
    auto map = getPacketChunkList(total_size);
xia-chu committed
456
    printer << "twcc fci, base_seq:" << getBaseSeq() << ", pkt_status_count:" << getPacketCount() << ", ref time:" << getReferenceTime() << ", fb count:" << (int)fb_pkt_count << "\n";
xia-chu committed
457
    for (auto &pr : map) {
xia-chu committed
458
        printer << "rtp seq:" << pr.first <<", packet status:" << (int)(pr.second.first) << ", delta:" << pr.second.second << "\n";
xia-chu committed
459 460 461 462
    }
    return std::move(printer);
}

xia-chu committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
}//namespace mediakit

#if 1
using namespace mediakit;
void testFCI() {
    {
        FCI_SLI fci(8191, 0, 63);
        InfoL << hexdump(&fci, FCI_SLI::kSize) << fci.dumpString();
    }
    {
        FCI_FIR fci(123456, 139, 456789);
        InfoL << hexdump(&fci, FCI_FIR::kSize) << fci.dumpString();
    }
    {
        auto str = FCI_REMB::create({1234, 2345, 5678}, 4 * 1024 * 1024);
        FCI_REMB *ptr = (FCI_REMB *) str.data();
        InfoL << hexdump(str.data(), str.size()) << ptr->dumpString();
    }
    {
        FCI_NACK nack(1234, vector<bool>({1, 0, 0, 0, 1, 0, 1, 0, 1, 0}));
        InfoL << hexdump(&nack, FCI_NACK::kSize) << nack.dumpString();
    }

    {
        RunLengthChunk chunk(SymbolStatus::large_delta, 8024);
        InfoL << hexdump(&chunk, RunLengthChunk::kSize) << chunk.dumpString();
    }

    auto lam = [](const initializer_list<int> &lst) {
        vector<SymbolStatus> ret;
        for (auto &num : lst) {
            ret.emplace_back((SymbolStatus) num);
        }
        return ret;
    };
    {
        StatusVecChunk chunk(lam({0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1}));
        InfoL << hexdump(&chunk, StatusVecChunk::kSize) << chunk.dumpString();
    }
    {
        StatusVecChunk chunk(lam({0, 1, 2, 2, 0, 1, 2}));
        InfoL <<  hexdump(&chunk, StatusVecChunk::kSize) << chunk.dumpString();
    }
}
#endif