blob: a4f0f6bbdc56a50a25da45e955b9a1bde5cf589a [file] [log] [blame]
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h"
namespace webrtc {
namespace rtcp {
namespace {
// Header size:
// * 4 bytes Common RTCP Packet Header
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header
constexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;
constexpr size_t kChunkSizeBytes = 2;
constexpr size_t kRunLengthCapacity = 0x1FFF;
// TODO(sprang): Add support for dynamic max size for easier fragmentation,
// eg. set it to what's left in the buffer or IP_PACKET_SIZE.
// Size constraint imposed by RTCP common header: 16bit size field interpreted
// as number of four byte words minus the first header word.
constexpr size_t kMaxSizeBytes = (1 << 16) * 4;
// Payload size:
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header.
// * 2 bytes for one chunk.
constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
constexpr size_t kBaseScaleFactor =
TransportFeedback::kDeltaScaleFactor * (1 << 8);
uint8_t EncodeSymbol(TransportFeedback::StatusSymbol symbol) {
switch (symbol) {
case TransportFeedback::StatusSymbol::kNotReceived:
return 0;
case TransportFeedback::StatusSymbol::kReceivedSmallDelta:
return 1;
case TransportFeedback::StatusSymbol::kReceivedLargeDelta:
return 2;
}
RTC_NOTREACHED();
return 0;
}
TransportFeedback::StatusSymbol DecodeSymbol(uint8_t value) {
switch (value) {
case 0:
return TransportFeedback::StatusSymbol::kNotReceived;
case 1:
return TransportFeedback::StatusSymbol::kReceivedSmallDelta;
case 2:
return TransportFeedback::StatusSymbol::kReceivedLargeDelta;
case 3:
// It is invalid, but |value| comes from network, so can be any.
return TransportFeedback::StatusSymbol::kNotReceived;
default:
// Caller should pass 2 bits max.
RTC_NOTREACHED();
return TransportFeedback::StatusSymbol::kNotReceived;
}
}
} // namespace
constexpr uint8_t TransportFeedback::kFeedbackMessageType;
class TransportFeedback::PacketStatusChunk {
public:
virtual ~PacketStatusChunk() {}
virtual uint16_t NumSymbols() const = 0;
virtual void AppendSymbolsTo(
std::vector<TransportFeedback::StatusSymbol>* vec) const = 0;
virtual void WriteTo(uint8_t* buffer) const = 0;
};
TransportFeedback::TransportFeedback()
: base_seq_(-1),
base_time_(-1),
feedback_seq_(0),
last_seq_(-1),
last_timestamp_(-1),
first_symbol_cardinality_(0),
vec_needs_two_bit_symbols_(false),
size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
TransportFeedback::~TransportFeedback() {
for (PacketStatusChunk* chunk : status_chunks_)
delete chunk;
}
// One Bit Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T|S| symbol list |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 1
// S = 0
// symbol list = 14 entries where 0 = not received, 1 = received
class OneBitVectorChunk : public TransportFeedback::PacketStatusChunk {
public:
static constexpr size_t kCapacity = 14;
explicit OneBitVectorChunk(
std::deque<TransportFeedback::StatusSymbol>* symbols) {
size_t input_size = symbols->size();
for (size_t i = 0; i < kCapacity; ++i) {
if (i < input_size) {
symbols_[i] = symbols->front();
symbols->pop_front();
} else {
symbols_[i] = TransportFeedback::StatusSymbol::kNotReceived;
}
}
}
~OneBitVectorChunk() override {}
uint16_t NumSymbols() const override { return kCapacity; }
void AppendSymbolsTo(
std::vector<TransportFeedback::StatusSymbol>* vec) const override {
vec->insert(vec->end(), &symbols_[0], &symbols_[kCapacity]);
}
void WriteTo(uint8_t* buffer) const override {
constexpr int kSymbolsInFirstByte = 6;
constexpr int kSymbolsInSecondByte = 8;
buffer[0] = 0x80u;
for (int i = 0; i < kSymbolsInFirstByte; ++i) {
uint8_t encoded_symbol = EncodeSymbol(symbols_[i]);
RTC_DCHECK_LE(encoded_symbol, 1u);
buffer[0] |= encoded_symbol << (kSymbolsInFirstByte - (i + 1));
}
buffer[1] = 0x00u;
for (int i = 0; i < kSymbolsInSecondByte; ++i) {
uint8_t encoded_symbol = EncodeSymbol(symbols_[i + kSymbolsInFirstByte]);
RTC_DCHECK_LE(encoded_symbol, 1u);
buffer[1] |= encoded_symbol << (kSymbolsInSecondByte - (i + 1));
}
}
static OneBitVectorChunk* ParseFrom(const uint8_t* data) {
OneBitVectorChunk* chunk = new OneBitVectorChunk();
size_t index = 0;
for (int i = 5; i >= 0; --i) // Last 5 bits from first byte.
chunk->symbols_[index++] = DecodeSymbol((data[0] >> i) & 0x01);
for (int i = 7; i >= 0; --i) // 8 bits from the last byte.
chunk->symbols_[index++] = DecodeSymbol((data[1] >> i) & 0x01);
return chunk;
}
private:
OneBitVectorChunk() {}
TransportFeedback::StatusSymbol symbols_[kCapacity];
};
// Two Bit Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T|S| symbol list |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 1
// S = 1
// symbol list = 7 entries of two bits each, see (Encode|Decode)Symbol
class TwoBitVectorChunk : public TransportFeedback::PacketStatusChunk {
public:
static constexpr size_t kCapacity = 7;
explicit TwoBitVectorChunk(
std::deque<TransportFeedback::StatusSymbol>* symbols) {
size_t input_size = symbols->size();
for (size_t i = 0; i < kCapacity; ++i) {
if (i < input_size) {
symbols_[i] = symbols->front();
symbols->pop_front();
} else {
symbols_[i] = TransportFeedback::StatusSymbol::kNotReceived;
}
}
}
~TwoBitVectorChunk() override {}
uint16_t NumSymbols() const override { return kCapacity; }
void AppendSymbolsTo(
std::vector<TransportFeedback::StatusSymbol>* vec) const override {
vec->insert(vec->end(), &symbols_[0], &symbols_[kCapacity]);
}
void WriteTo(uint8_t* buffer) const override {
buffer[0] = 0xC0;
buffer[0] |= EncodeSymbol(symbols_[0]) << 4;
buffer[0] |= EncodeSymbol(symbols_[1]) << 2;
buffer[0] |= EncodeSymbol(symbols_[2]);
buffer[1] = EncodeSymbol(symbols_[3]) << 6;
buffer[1] |= EncodeSymbol(symbols_[4]) << 4;
buffer[1] |= EncodeSymbol(symbols_[5]) << 2;
buffer[1] |= EncodeSymbol(symbols_[6]);
}
static TwoBitVectorChunk* ParseFrom(const uint8_t* buffer) {
TwoBitVectorChunk* chunk = new TwoBitVectorChunk();
chunk->symbols_[0] = DecodeSymbol((buffer[0] >> 4) & 0x03);
chunk->symbols_[1] = DecodeSymbol((buffer[0] >> 2) & 0x03);
chunk->symbols_[2] = DecodeSymbol(buffer[0] & 0x03);
chunk->symbols_[3] = DecodeSymbol((buffer[1] >> 6) & 0x03);
chunk->symbols_[4] = DecodeSymbol((buffer[1] >> 4) & 0x03);
chunk->symbols_[5] = DecodeSymbol((buffer[1] >> 2) & 0x03);
chunk->symbols_[6] = DecodeSymbol(buffer[1] & 0x03);
return chunk;
}
private:
TwoBitVectorChunk() {}
TransportFeedback::StatusSymbol symbols_[kCapacity];
};
// Two Bit Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T| S | Run Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 0
// S = symbol, see (Encode|Decode)Symbol
// Run Length = Unsigned integer denoting the run length of the symbol
class RunLengthChunk : public TransportFeedback::PacketStatusChunk {
public:
RunLengthChunk(TransportFeedback::StatusSymbol symbol, size_t size)
: symbol_(symbol), size_(size) {
RTC_DCHECK_LE(size, 0x1FFFu);
}
~RunLengthChunk() override {}
uint16_t NumSymbols() const override { return size_; }
void AppendSymbolsTo(
std::vector<TransportFeedback::StatusSymbol>* vec) const override {
vec->insert(vec->end(), size_, symbol_);
}
void WriteTo(uint8_t* buffer) const override {
buffer[0] = EncodeSymbol(symbol_) << 5; // Write S (T = 0 implicitly)
buffer[0] |= (size_ >> 8) & 0x1F; // 5 most significant bits of run length.
buffer[1] = size_ & 0xFF; // 8 least significant bits of run length.
}
static RunLengthChunk* ParseFrom(const uint8_t* buffer) {
RTC_DCHECK_EQ(0, buffer[0] & 0x80);
TransportFeedback::StatusSymbol symbol =
DecodeSymbol((buffer[0] >> 5) & 0x03);
uint16_t count = (static_cast<uint16_t>(buffer[0] & 0x1F) << 8) | buffer[1];
return new RunLengthChunk(symbol, count);
}
private:
const TransportFeedback::StatusSymbol symbol_;
const size_t size_;
};
// Unwrap to a larger type, for easier handling of wraps.
int64_t TransportFeedback::Unwrap(uint16_t sequence_number) {
if (last_seq_ == -1)
return sequence_number;
int64_t delta = sequence_number - last_seq_;
if (IsNewerSequenceNumber(sequence_number,
static_cast<uint16_t>(last_seq_))) {
if (delta < 0)
delta += (1 << 16);
} else if (delta > 0) {
delta -= (1 << 16);
}
return last_seq_ + delta;
}
void TransportFeedback::WithBase(uint16_t base_sequence,
int64_t ref_timestamp_us) {
RTC_DCHECK_EQ(-1, base_seq_);
RTC_DCHECK_NE(-1, ref_timestamp_us);
base_seq_ = base_sequence;
// last_seq_ is the sequence number of the last packed added _before_ a call
// to WithReceivedPacket(). Since the first sequence to be added is
// base_sequence, we need this to be one lower in order for potential missing
// packets to be populated properly.
last_seq_ = base_sequence - 1;
base_time_ = ref_timestamp_us / kBaseScaleFactor;
last_timestamp_ = base_time_ * kBaseScaleFactor;
}
void TransportFeedback::WithFeedbackSequenceNumber(uint8_t feedback_sequence) {
feedback_seq_ = feedback_sequence;
}
bool TransportFeedback::WithReceivedPacket(uint16_t sequence_number,
int64_t timestamp) {
RTC_DCHECK_NE(-1, base_seq_);
int64_t seq = Unwrap(sequence_number);
if (seq != base_seq_ && seq <= last_seq_)
return false;
// Convert to ticks and round.
int64_t delta_full = timestamp - last_timestamp_;
delta_full +=
delta_full < 0 ? -(kDeltaScaleFactor / 2) : kDeltaScaleFactor / 2;
delta_full /= kDeltaScaleFactor;
int16_t delta = static_cast<int16_t>(delta_full);
// If larger than 16bit signed, we can't represent it - need new fb packet.
if (delta != delta_full) {
LOG(LS_WARNING) << "Delta value too large ( >= 2^16 ticks )";
return false;
}
StatusSymbol symbol;
if (delta >= 0 && delta <= 0xFF) {
symbol = StatusSymbol::kReceivedSmallDelta;
} else {
symbol = StatusSymbol::kReceivedLargeDelta;
}
if (!AddSymbol(symbol, seq))
return false;
receive_deltas_.push_back(delta);
last_timestamp_ += delta * kDeltaScaleFactor;
return true;
}
// Add a symbol for a received packet, with the given sequence number. This
// method will add any "packet not received" symbols needed before this one.
bool TransportFeedback::AddSymbol(StatusSymbol symbol, int64_t seq) {
while (last_seq_ < seq - 1) {
if (!Encode(StatusSymbol::kNotReceived))
return false;
++last_seq_;
}
if (!Encode(symbol))
return false;
last_seq_ = seq;
return true;
}
// Append a symbol to the internal symbol vector. If the new state cannot be
// represented using a single status chunk, a chunk will first be emitted and
// the associated symbols removed from the internal symbol vector.
bool TransportFeedback::Encode(StatusSymbol symbol) {
if (last_seq_ - base_seq_ + 1 > 0xFFFF) {
LOG(LS_WARNING) << "Packet status count too large ( >= 2^16 )";
return false;
}
bool is_two_bit = false;
int delta_size = -1;
switch (symbol) {
case StatusSymbol::kReceivedSmallDelta:
delta_size = 1;
is_two_bit = false;
break;
case StatusSymbol::kReceivedLargeDelta:
delta_size = 2;
is_two_bit = true;
break;
case StatusSymbol::kNotReceived:
is_two_bit = false;
delta_size = 0;
break;
}
RTC_DCHECK_GE(delta_size, 0);
if (symbol_vec_.empty()) {
if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes)
return false;
symbol_vec_.push_back(symbol);
vec_needs_two_bit_symbols_ = is_two_bit;
first_symbol_cardinality_ = 1;
size_bytes_ += delta_size + kChunkSizeBytes;
return true;
}
if (size_bytes_ + delta_size > kMaxSizeBytes)
return false;
// Capacity, in number of symbols, that a vector chunk could hold.
size_t capacity = vec_needs_two_bit_symbols_ ? TwoBitVectorChunk::kCapacity
: OneBitVectorChunk::kCapacity;
// first_symbol_cardinality_ is the number of times the first symbol in
// symbol_vec is repeated. So if that is equal to the size of symbol_vec,
// there is only one kind of symbol - we can potentially RLE encode it.
// If we have less than (capacity) symbols in symbol_vec, we can't know
// for certain this will be RLE-encoded; if a different symbol is added
// these symbols will be needed to emit a vector chunk instead. However,
// if first_symbol_cardinality_ > capacity, then we cannot encode the
// current state as a vector chunk - we must first emit symbol_vec as an
// RLE-chunk and then add the new symbol.
bool rle_candidate = symbol_vec_.size() == first_symbol_cardinality_ ||
first_symbol_cardinality_ > capacity;
if (rle_candidate) {
if (symbol_vec_.back() == symbol) {
++first_symbol_cardinality_;
if (first_symbol_cardinality_ <= capacity) {
symbol_vec_.push_back(symbol);
} else if (first_symbol_cardinality_ == kRunLengthCapacity) {
// Max length for an RLE-chunk reached.
EmitRunLengthChunk();
}
size_bytes_ += delta_size;
return true;
} else {
// New symbol does not match what's already in symbol_vec.
if (first_symbol_cardinality_ >= capacity) {
// Symbols in symbol_vec can only be RLE-encoded. Emit the RLE-chunk
// and re-add input. symbol_vec is then guaranteed to have room for the
// symbol, so recursion cannot continue.
EmitRunLengthChunk();
return Encode(symbol);
}
// Fall through and treat state as non RLE-candidate.
}
}
// If this code point is reached, symbols in symbol_vec cannot be RLE-encoded.
if (is_two_bit && !vec_needs_two_bit_symbols_) {
// If the symbols in symbol_vec can be encoded using a one-bit chunk but
// the input symbol cannot, first check if we can simply change target type.
vec_needs_two_bit_symbols_ = true;
if (symbol_vec_.size() >= TwoBitVectorChunk::kCapacity) {
// symbol_vec contains more symbols than we can encode in a single
// two-bit chunk. Emit a new vector append to the remains, if any.
if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes)
return false;
EmitVectorChunk();
// If symbol_vec isn't empty after emitting a vector chunk, we need to
// account for chunk size (otherwise handled by Encode method).
if (!symbol_vec_.empty())
size_bytes_ += kChunkSizeBytes;
return Encode(symbol);
}
// symbol_vec symbols fit within a single two-bit vector chunk.
capacity = TwoBitVectorChunk::kCapacity;
}
symbol_vec_.push_back(symbol);
if (symbol_vec_.size() == capacity)
EmitVectorChunk();
size_bytes_ += delta_size;
return true;
}
// Upon packet completion, emit any remaining symbols in symbol_vec that have
// not yet been emitted in a status chunk.
void TransportFeedback::EmitRemaining() {
if (symbol_vec_.empty())
return;
size_t capacity = vec_needs_two_bit_symbols_ ? TwoBitVectorChunk::kCapacity
: OneBitVectorChunk::kCapacity;
if (first_symbol_cardinality_ > capacity) {
EmitRunLengthChunk();
} else {
EmitVectorChunk();
}
}
void TransportFeedback::EmitVectorChunk() {
if (vec_needs_two_bit_symbols_) {
status_chunks_.push_back(new TwoBitVectorChunk(&symbol_vec_));
} else {
status_chunks_.push_back(new OneBitVectorChunk(&symbol_vec_));
}
// Update first symbol cardinality to match what is potentially left in in
// symbol_vec.
first_symbol_cardinality_ = 1;
for (size_t i = 1; i < symbol_vec_.size(); ++i) {
if (symbol_vec_[i] != symbol_vec_[0])
break;
++first_symbol_cardinality_;
}
}
void TransportFeedback::EmitRunLengthChunk() {
RTC_DCHECK_GE(first_symbol_cardinality_, symbol_vec_.size());
status_chunks_.push_back(
new RunLengthChunk(symbol_vec_.front(), first_symbol_cardinality_));
symbol_vec_.clear();
}
size_t TransportFeedback::BlockLength() const {
// Round size_bytes_ up to multiple of 32bits.
return (size_bytes_ + 3) & (~static_cast<size_t>(3));
}
uint16_t TransportFeedback::GetBaseSequence() const {
return base_seq_;
}
int64_t TransportFeedback::GetBaseTimeUs() const {
return base_time_ * kBaseScaleFactor;
}
std::vector<TransportFeedback::StatusSymbol>
TransportFeedback::GetStatusVector() const {
std::vector<TransportFeedback::StatusSymbol> symbols;
for (PacketStatusChunk* chunk : status_chunks_)
chunk->AppendSymbolsTo(&symbols);
int64_t status_count = last_seq_ - base_seq_ + 1;
// If packet ends with a vector chunk, it may contain extraneous "packet not
// received"-symbols at the end. Crop any such symbols.
symbols.erase(symbols.begin() + status_count, symbols.end());
return symbols;
}
std::vector<int16_t> TransportFeedback::GetReceiveDeltas() const {
return receive_deltas_;
}
std::vector<int64_t> TransportFeedback::GetReceiveDeltasUs() const {
if (receive_deltas_.empty())
return std::vector<int64_t>();
std::vector<int64_t> us_deltas;
for (int16_t delta : receive_deltas_)
us_deltas.push_back(static_cast<int64_t>(delta) * kDeltaScaleFactor);
return us_deltas;
}
// Serialize packet.
bool TransportFeedback::Create(uint8_t* packet,
size_t* position,
size_t max_length,
PacketReadyCallback* callback) const {
if (base_seq_ == -1)
return false;
while (*position + BlockLength() > max_length) {
if (!OnBufferFull(packet, position, callback))
return false;
}
const size_t position_end = *position + BlockLength();
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
position);
CreateCommonFeedback(packet + *position);
*position += kCommonFeedbackLength;
RTC_DCHECK_LE(base_seq_, 0xFFFF);
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], base_seq_);
*position += 2;
int64_t status_count = last_seq_ - base_seq_ + 1;
RTC_DCHECK_LE(status_count, 0xFFFF);
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], status_count);
*position += 2;
ByteWriter<int32_t, 3>::WriteBigEndian(&packet[*position],
static_cast<int32_t>(base_time_));
*position += 3;
packet[(*position)++] = feedback_seq_;
// TODO(sprang): Get rid of this cast.
const_cast<TransportFeedback*>(this)->EmitRemaining();
for (PacketStatusChunk* chunk : status_chunks_) {
chunk->WriteTo(&packet[*position]);
*position += 2;
}
for (int16_t delta : receive_deltas_) {
if (delta >= 0 && delta <= 0xFF) {
packet[(*position)++] = delta;
} else {
ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
*position += 2;
}
}
while ((*position % 4) != 0)
packet[(*position)++] = 0;
RTC_DCHECK_EQ(*position, position_end);
return true;
}
// Message format
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=15 | PT=205 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | base sequence number | packet status count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | reference time | fb pkt. count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | packet chunk | packet chunk |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | packet chunk | recv delta | recv delta |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | recv delta | recv delta | zero padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// De-serialize packet.
bool TransportFeedback::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() < kMinPayloadSizeBytes) {
LOG(LS_WARNING) << "Buffer too small (" << packet.payload_size_bytes()
<< " bytes) to fit a "
"FeedbackPacket. Minimum size = "
<< kMinPayloadSizeBytes;
return false;
}
// TODO(danilchap): Make parse work correctly with not new objects.
RTC_DCHECK(status_chunks_.empty()) << "Parse expects object to be new.";
const uint8_t* const payload = packet.payload();
ParseCommonFeedback(payload);
base_seq_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
uint16_t num_packets = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
base_time_ = ByteReader<int32_t, 3>::ReadBigEndian(&payload[12]);
feedback_seq_ = payload[15];
size_t index = 16;
const size_t end_index = packet.payload_size_bytes();
if (num_packets == 0) {
LOG(LS_WARNING) << "Empty feedback messages not allowed.";
return false;
}
last_seq_ = base_seq_ + num_packets - 1;
size_t packets_read = 0;
while (packets_read < num_packets) {
if (index + 2 > end_index) {
LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
return false;
}
PacketStatusChunk* chunk =
ParseChunk(&payload[index], num_packets - packets_read);
if (chunk == nullptr)
return false;
index += 2;
status_chunks_.push_back(chunk);
packets_read += chunk->NumSymbols();
}
std::vector<StatusSymbol> symbols = GetStatusVector();
RTC_DCHECK_EQ(num_packets, symbols.size());
for (StatusSymbol symbol : symbols) {
switch (symbol) {
case StatusSymbol::kReceivedSmallDelta:
if (index + 1 > end_index) {
LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
return false;
}
receive_deltas_.push_back(payload[index]);
++index;
break;
case StatusSymbol::kReceivedLargeDelta:
if (index + 2 > end_index) {
LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
return false;
}
receive_deltas_.push_back(
ByteReader<int16_t>::ReadBigEndian(&payload[index]));
index += 2;
break;
case StatusSymbol::kNotReceived:
continue;
}
}
RTC_DCHECK_LE(index, end_index);
return true;
}
std::unique_ptr<TransportFeedback> TransportFeedback::ParseFrom(
const uint8_t* buffer,
size_t length) {
CommonHeader header;
if (!header.Parse(buffer, length))
return nullptr;
if (header.type() != kPacketType || header.fmt() != kFeedbackMessageType)
return nullptr;
std::unique_ptr<TransportFeedback> parsed(new TransportFeedback);
if (!parsed->Parse(header))
return nullptr;
return parsed;
}
TransportFeedback::PacketStatusChunk* TransportFeedback::ParseChunk(
const uint8_t* buffer,
size_t max_size) {
if (buffer[0] & 0x80) {
// First bit set => vector chunk.
if (buffer[0] & 0x40) {
// Second bit set => two bits per symbol vector.
return TwoBitVectorChunk::ParseFrom(buffer);
}
// Second bit not set => one bit per symbol vector.
return OneBitVectorChunk::ParseFrom(buffer);
}
// First bit not set => RLE chunk.
RunLengthChunk* rle_chunk = RunLengthChunk::ParseFrom(buffer);
if (rle_chunk->NumSymbols() > max_size) {
LOG(LS_WARNING) << "Header/body mismatch. "
"RLE block of size " << rle_chunk->NumSymbols()
<< " but only " << max_size << " left to read.";
delete rle_chunk;
return nullptr;
}
return rle_chunk;
}
} // namespace rtcp
} // namespace webrtc