diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc index 3a5a3038be..42c0cc63ec 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc @@ -10,6 +10,8 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include + #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/modules/include/module_common_types.h" @@ -25,7 +27,6 @@ namespace { // * 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 @@ -38,584 +39,7 @@ constexpr size_t kMaxSizeBytes = (1 << 16) * 4; 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* 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* 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* 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, 1); - 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, 1); - 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* 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* 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* 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(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(last_seq_))) { - if (delta < 0) - delta += (1 << 16); - } else if (delta > 0) { - delta -= (1 << 16); - } - - return last_seq_ + delta; -} - -void TransportFeedback::SetBase(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::SetFeedbackSequenceNumber(uint8_t feedback_sequence) { - feedback_seq_ = feedback_sequence; -} - -bool TransportFeedback::AddReceivedPacket(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(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(3)); -} - -uint16_t TransportFeedback::GetBaseSequence() const { - return base_seq_; -} - -int64_t TransportFeedback::GetBaseTimeUs() const { - return base_time_ * kBaseScaleFactor; -} - -std::vector -TransportFeedback::GetStatusVector() const { - std::vector 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 TransportFeedback::GetReceiveDeltas() const { - return receive_deltas_; -} - -std::vector TransportFeedback::GetReceiveDeltasUs() const { - if (receive_deltas_.empty()) - return std::vector(); - - std::vector us_deltas; - for (int16_t delta : receive_deltas_) - us_deltas.push_back(static_cast(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::WriteBigEndian(&packet[*position], base_seq_); - *position += 2; - - int64_t status_count = last_seq_ - base_seq_ + 1; - RTC_DCHECK_LE(status_count, 0xFFFF); - ByteWriter::WriteBigEndian(&packet[*position], status_count); - *position += 2; - - ByteWriter::WriteBigEndian(&packet[*position], - static_cast(base_time_)); - *position += 3; - - packet[(*position)++] = feedback_seq_; - - // TODO(sprang): Get rid of this cast. - const_cast(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::WriteBigEndian(&packet[*position], delta); - *position += 2; - } - } - - while ((*position % 4) != 0) - packet[(*position)++] = 0; - - RTC_DCHECK_EQ(*position, position_end); - return true; -} +constexpr int64_t kTimeWrapPeriodUs = (1ll << 24) * kBaseScaleFactor; // Message format // @@ -644,6 +68,342 @@ bool TransportFeedback::Create(uint8_t* packet, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | recv delta | recv delta | zero padding | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +} // namespace +constexpr uint8_t TransportFeedback::kFeedbackMessageType; +constexpr size_t TransportFeedback::kMaxReportedPackets; + +// Keep delta_sizes that can be encoded into single chunk if it is last chunk. +class TransportFeedback::LastChunk { + public: + using DeltaSize = TransportFeedback::DeltaSize; + + LastChunk(); + + bool Empty() const; + void Clear(); + // Return if delta sizes still can be encoded into single chunk with added + // |delta_size|. + bool CanAdd(DeltaSize delta_size) const; + // Add |delta_size|, assumes |CanAdd(delta_size)|, + void Add(DeltaSize delta_size); + + // Encode chunk as large as possible removing encoded delta sizes. + // Assume CanAdd() == false for some valid delta_size. + uint16_t Emit(); + // Encode all stored delta_sizes into single chunk, pad with 0s if needed. + uint16_t EncodeLast() const; + + // Decode up to |max_size| delta sizes from |chunk|. + void Decode(uint16_t chunk, size_t max_size); + // Appends content of the Lastchunk to |deltas|. + void AppendTo(std::vector* deltas) const; + + private: + static constexpr size_t kMaxRunLengthCapacity = 0x1fff; + static constexpr size_t kMaxOneBitCapacity = 14; + static constexpr size_t kMaxTwoBitCapacity = 7; + static constexpr size_t kMaxVectorCapacity = kMaxOneBitCapacity; + static constexpr DeltaSize kLarge = 2; + + uint16_t EncodeOneBit() const; + void DecodeOneBit(uint16_t chunk, size_t max_size); + + uint16_t EncodeTwoBit(size_t size) const; + void DecodeTwoBit(uint16_t chunk, size_t max_size); + + uint16_t EncodeRunLength() const; + void DecodeRunLength(uint16_t chunk, size_t max_size); + + DeltaSize delta_sizes_[kMaxVectorCapacity]; + uint16_t size_; + bool all_same_; + bool has_large_delta_; +}; +constexpr size_t TransportFeedback::LastChunk::kMaxRunLengthCapacity; +constexpr size_t TransportFeedback::LastChunk::kMaxOneBitCapacity; +constexpr size_t TransportFeedback::LastChunk::kMaxTwoBitCapacity; +constexpr size_t TransportFeedback::LastChunk::kMaxVectorCapacity; + +TransportFeedback::LastChunk::LastChunk() { + Clear(); +} + +bool TransportFeedback::LastChunk::Empty() const { + return size_ == 0; +} + +void TransportFeedback::LastChunk::Clear() { + size_ = 0; + all_same_ = true; + has_large_delta_ = false; +} + +bool TransportFeedback::LastChunk::CanAdd(DeltaSize delta_size) const { + RTC_DCHECK_LE(delta_size, 2); + if (size_ < kMaxTwoBitCapacity) + return true; + if (size_ < kMaxOneBitCapacity && !has_large_delta_ && delta_size != kLarge) + return true; + if (size_ < kMaxRunLengthCapacity && all_same_ && + delta_sizes_[0] == delta_size) + return true; + return false; +} + +void TransportFeedback::LastChunk::Add(DeltaSize delta_size) { + RTC_DCHECK(CanAdd(delta_size)); + if (size_ < kMaxVectorCapacity) + delta_sizes_[size_] = delta_size; + size_++; + all_same_ = all_same_ && delta_size == delta_sizes_[0]; + has_large_delta_ = has_large_delta_ || delta_size == kLarge; +} + +uint16_t TransportFeedback::LastChunk::Emit() { + RTC_DCHECK(!CanAdd(0) || !CanAdd(1) || !CanAdd(2)); + if (all_same_) { + uint16_t chunk = EncodeRunLength(); + Clear(); + return chunk; + } + if (size_ == kMaxOneBitCapacity) { + uint16_t chunk = EncodeOneBit(); + Clear(); + return chunk; + } + RTC_DCHECK_GE(size_, kMaxTwoBitCapacity); + uint16_t chunk = EncodeTwoBit(kMaxTwoBitCapacity); + // Remove |kMaxTwoBitCapacity| encoded delta sizes: + // Shift remaining delta sizes and recalculate all_same_ && has_large_delta_. + size_ -= kMaxTwoBitCapacity; + all_same_ = true; + has_large_delta_ = false; + for (size_t i = 0; i < size_; ++i) { + DeltaSize delta_size = delta_sizes_[kMaxTwoBitCapacity + i]; + delta_sizes_[i] = delta_size; + all_same_ = all_same_ && delta_size == delta_sizes_[0]; + has_large_delta_ = has_large_delta_ || delta_size == kLarge; + } + + return chunk; +} + +uint16_t TransportFeedback::LastChunk::EncodeLast() const { + RTC_DCHECK_GT(size_, 0); + if (all_same_) + return EncodeRunLength(); + if (size_ <= kMaxTwoBitCapacity) + return EncodeTwoBit(size_); + return EncodeOneBit(); +} + +// Appends content of the Lastchunk to |deltas|. +void TransportFeedback::LastChunk::AppendTo( + std::vector* deltas) const { + if (all_same_) { + deltas->insert(deltas->end(), size_, delta_sizes_[0]); + } else { + deltas->insert(deltas->end(), delta_sizes_, delta_sizes_ + size_); + } +} + +void TransportFeedback::LastChunk::Decode(uint16_t chunk, size_t max_size) { + if ((chunk & 0x8000) == 0) { + DecodeRunLength(chunk, max_size); + } else if ((chunk & 0x4000) == 0) { + DecodeOneBit(chunk, max_size); + } else { + DecodeTwoBit(chunk, max_size); + } +} + +// 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 1-byte delta. +uint16_t TransportFeedback::LastChunk::EncodeOneBit() const { + RTC_DCHECK(!has_large_delta_); + RTC_DCHECK_LE(size_, kMaxOneBitCapacity); + uint16_t chunk = 0x8000; + for (size_t i = 0; i < size_; ++i) + chunk |= delta_sizes_[i] << (kMaxOneBitCapacity - 1 - i); + return chunk; +} + +void TransportFeedback::LastChunk::DecodeOneBit(uint16_t chunk, + size_t max_size) { + RTC_DCHECK_EQ(chunk & 0xc000, 0x8000); + size_ = std::min(kMaxOneBitCapacity, max_size); + has_large_delta_ = false; + all_same_ = false; + for (size_t i = 0; i < size_; ++i) + delta_sizes_[i] = (chunk >> (kMaxOneBitCapacity - 1 - i)) & 0x01; +} + +// 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. +uint16_t TransportFeedback::LastChunk::EncodeTwoBit(size_t size) const { + RTC_DCHECK_LE(size, size_); + uint16_t chunk = 0xc000; + for (size_t i = 0; i < size; ++i) + chunk |= delta_sizes_[i] << 2 * (kMaxTwoBitCapacity - 1 - i); + return chunk; +} + +void TransportFeedback::LastChunk::DecodeTwoBit(uint16_t chunk, + size_t max_size) { + RTC_DCHECK_EQ(chunk & 0xc000, 0xc000); + size_ = std::min(kMaxTwoBitCapacity, max_size); + has_large_delta_ = true; + all_same_ = false; + for (size_t i = 0; i < size_; ++i) + delta_sizes_[i] = (chunk >> 2 * (kMaxTwoBitCapacity - 1 - i)) & 0x03; +} + +// Run Length 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 +// Run Length = Unsigned integer denoting the run length of the symbol +uint16_t TransportFeedback::LastChunk::EncodeRunLength() const { + RTC_DCHECK(all_same_); + RTC_DCHECK_LE(size_, kMaxRunLengthCapacity); + return (delta_sizes_[0] << 13) | size_; +} + +void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk, + size_t max_count) { + RTC_DCHECK_EQ(chunk & 0x8000, 0); + size_ = std::min(chunk & 0x1fff, max_count); + size_t delta_size = (chunk >> 13) & 0x03; + has_large_delta_ = delta_size >= kLarge; + all_same_ = true; + // To make it consistent with Add function, populate delta_sizes_ beyound 1st. + for (size_t i = 0; i < std::min(size_, kMaxVectorCapacity); ++i) + delta_sizes_[i] = delta_size; +} + +TransportFeedback::TransportFeedback() + : base_seq_no_(0), + num_seq_no_(0), + base_time_ticks_(0), + feedback_seq_(0), + last_timestamp_us_(0), + last_chunk_(new LastChunk()), + size_bytes_(kTransportFeedbackHeaderSizeBytes) {} + +TransportFeedback::~TransportFeedback() {} + +void TransportFeedback::SetBase(uint16_t base_sequence, + int64_t ref_timestamp_us) { + RTC_DCHECK_EQ(num_seq_no_, 0); + RTC_DCHECK_GE(ref_timestamp_us, 0); + base_seq_no_ = base_sequence; + base_time_ticks_ = (ref_timestamp_us % kTimeWrapPeriodUs) / kBaseScaleFactor; + last_timestamp_us_ = GetBaseTimeUs(); +} + +void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) { + feedback_seq_ = feedback_sequence; +} + +bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number, + int64_t timestamp_us) { + // Convert to ticks and round. + int64_t delta_full = (timestamp_us - last_timestamp_us_) % kTimeWrapPeriodUs; + if (delta_full > kTimeWrapPeriodUs / 2) + delta_full -= kTimeWrapPeriodUs; + delta_full += + delta_full < 0 ? -(kDeltaScaleFactor / 2) : kDeltaScaleFactor / 2; + delta_full /= kDeltaScaleFactor; + + int16_t delta = static_cast(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; + } + + uint16_t next_seq_no = base_seq_no_ + num_seq_no_; + if (sequence_number != next_seq_no) { + uint16_t last_seq_no = next_seq_no - 1; + if (!IsNewerSequenceNumber(sequence_number, last_seq_no)) + return false; + for (; next_seq_no != sequence_number; ++next_seq_no) + if (!AddDeltaSize(0)) + return false; + } + + DeltaSize delta_size = (delta >= 0 && delta <= 0xff) ? 1 : 2; + if (!AddDeltaSize(delta_size)) + return false; + + packets_.emplace_back(sequence_number, delta); + last_timestamp_us_ += delta * kDeltaScaleFactor; + size_bytes_ += delta_size; + return true; +} + +uint16_t TransportFeedback::GetBaseSequence() const { + return base_seq_no_; +} + +std::vector +TransportFeedback::GetStatusVector() const { + std::vector symbols; + uint16_t seq_no = GetBaseSequence(); + for (const auto& packet : packets_) { + for (; seq_no != packet.sequence_number; ++seq_no) + symbols.push_back(StatusSymbol::kNotReceived); + if (packet.delta_ticks >= 0x00 && packet.delta_ticks <= 0xff) { + symbols.push_back(StatusSymbol::kReceivedSmallDelta); + } else { + symbols.push_back(StatusSymbol::kReceivedLargeDelta); + } + ++seq_no; + } + return symbols; +} + +std::vector TransportFeedback::GetReceiveDeltas() const { + std::vector deltas; + for (const auto& packet : packets_) + deltas.push_back(packet.delta_ticks); + return deltas; +} + +int64_t TransportFeedback::GetBaseTimeUs() const { + return static_cast(base_time_ticks_) * kBaseScaleFactor; +} + +std::vector TransportFeedback::GetReceiveDeltasUs() const { + std::vector us_deltas; + for (const auto& packet : packets_) + us_deltas.push_back(packet.delta_ticks * kDeltaScaleFactor); + return us_deltas; +} // De-serialize packet. bool TransportFeedback::Parse(const CommonHeader& packet) { @@ -657,73 +417,79 @@ bool TransportFeedback::Parse(const CommonHeader& packet) { << 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::ReadBigEndian(&payload[8]); - uint16_t num_packets = ByteReader::ReadBigEndian(&payload[10]); - base_time_ = ByteReader::ReadBigEndian(&payload[12]); + base_seq_no_ = ByteReader::ReadBigEndian(&payload[8]); + size_t status_count = ByteReader::ReadBigEndian(&payload[10]); + base_time_ticks_ = ByteReader::ReadBigEndian(&payload[12]); feedback_seq_ = payload[15]; + Clear(); size_t index = 16; const size_t end_index = packet.payload_size_bytes(); - if (num_packets == 0) { + if (status_count == 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) { + std::vector delta_sizes; + delta_sizes.reserve(status_count); + while (delta_sizes.size() < status_count) { + if (index + kChunkSizeBytes > end_index) { LOG(LS_WARNING) << "Buffer overflow while parsing packet."; + Clear(); return false; } - PacketStatusChunk* chunk = - ParseChunk(&payload[index], num_packets - packets_read); - if (chunk == nullptr) + uint16_t chunk = ByteReader::ReadBigEndian(&payload[index]); + index += kChunkSizeBytes; + encoded_chunks_.push_back(chunk); + last_chunk_->Decode(chunk, status_count - delta_sizes.size()); + last_chunk_->AppendTo(&delta_sizes); + } + // Last chunk is stored in the |last_chunk_|. + encoded_chunks_.pop_back(); + RTC_DCHECK_EQ(delta_sizes.size(), status_count); + num_seq_no_ = status_count; + + uint16_t seq_no = base_seq_no_; + for (size_t delta_size : delta_sizes) { + if (index + delta_size > end_index) { + LOG(LS_WARNING) << "Buffer overflow while parsing packet."; + Clear(); return false; - - index += 2; - status_chunks_.push_back(chunk); - packets_read += chunk->NumSymbols(); - } - - std::vector 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::ReadBigEndian(&payload[index])); - index += 2; - break; - case StatusSymbol::kNotReceived: - continue; } + switch (delta_size) { + case 0: + break; + case 1: { + int16_t delta = payload[index]; + packets_.emplace_back(seq_no, delta); + last_timestamp_us_ += delta * kDeltaScaleFactor; + index += delta_size; + break; + } + case 2: { + int16_t delta = ByteReader::ReadBigEndian(&payload[index]); + packets_.emplace_back(seq_no, delta); + last_timestamp_us_ += delta * kDeltaScaleFactor; + index += delta_size; + break; + } + case 3: + Clear(); + LOG(LS_WARNING) << "Invalid delta_size for seq_no " << seq_no; + return false; + default: + RTC_NOTREACHED(); + break; + } + ++seq_no; } - + size_bytes_ = RtcpPacket::kHeaderLength + index; RTC_DCHECK_LE(index, end_index); - return true; } @@ -741,30 +507,160 @@ std::unique_ptr TransportFeedback::ParseFrom( 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); +bool TransportFeedback::IsConsistent() const { + size_t packet_size = kTransportFeedbackHeaderSizeBytes; + std::vector delta_sizes; + LastChunk chunk_decoder; + for (uint16_t chunk : encoded_chunks_) { + chunk_decoder.Decode(chunk, kMaxReportedPackets); + chunk_decoder.AppendTo(&delta_sizes); + packet_size += kChunkSizeBytes; + } + if (!last_chunk_->Empty()) { + last_chunk_->AppendTo(&delta_sizes); + packet_size += kChunkSizeBytes; + } + if (num_seq_no_ != delta_sizes.size()) { + LOG(LS_ERROR) << delta_sizes.size() << " packets encoded. Expected " + << num_seq_no_; + return false; + } + int64_t timestamp_us = base_time_ticks_ * kBaseScaleFactor; + auto packet_it = packets_.begin(); + uint16_t seq_no = base_seq_no_; + for (DeltaSize delta_size : delta_sizes) { + if (delta_size > 0) { + if (packet_it == packets_.end()) { + LOG(LS_ERROR) << "Failed to find delta for seq_no " << seq_no; + return false; + } + if (packet_it->sequence_number != seq_no) { + LOG(LS_ERROR) << "Expected to find delta for seq_no " << seq_no + << ". Next delta is for " << packet_it->sequence_number; + return false; + } + if (delta_size == 1 && + (packet_it->delta_ticks < 0 || packet_it->delta_ticks > 0xff)) { + LOG(LS_ERROR) << "Delta " << packet_it->delta_ticks << " for seq_no " + << seq_no << " doesn't fit into one byte"; + return false; + } + timestamp_us += packet_it->delta_ticks * kDeltaScaleFactor; + ++packet_it; } + packet_size += delta_size; + ++seq_no; + } + if (packet_it != packets_.end()) { + LOG(LS_ERROR) << "Unencoded delta for seq_no " + << packet_it->sequence_number; + return false; + } + if (timestamp_us != last_timestamp_us_) { + LOG(LS_ERROR) << "Last timestamp mismatch. Calculated: " << timestamp_us + << ". Saved: " << last_timestamp_us_; + return false; + } + if (size_bytes_ != packet_size) { + LOG(LS_ERROR) << "Rtcp packet size mismatch. Calculated: " << packet_size + << ". Saved: " << size_bytes_; + return false; + } + return true; +} - // Second bit not set => one bit per symbol vector. - return OneBitVectorChunk::ParseFrom(buffer); +// Serialize packet. +bool TransportFeedback::Create(uint8_t* packet, + size_t* position, + size_t max_length, + PacketReadyCallback* callback) const { + if (num_seq_no_ == 0) + 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; + + ByteWriter::WriteBigEndian(&packet[*position], base_seq_no_); + *position += 2; + + ByteWriter::WriteBigEndian(&packet[*position], num_seq_no_); + *position += 2; + + ByteWriter::WriteBigEndian(&packet[*position], base_time_ticks_); + *position += 3; + + packet[(*position)++] = feedback_seq_; + + for (uint16_t chunk : encoded_chunks_) { + ByteWriter::WriteBigEndian(&packet[*position], chunk); + *position += 2; + } + if (!last_chunk_->Empty()) { + uint16_t chunk = last_chunk_->EncodeLast(); + ByteWriter::WriteBigEndian(&packet[*position], chunk); + *position += 2; } - // 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; + for (const auto& received_packet : packets_) { + int16_t delta = received_packet.delta_ticks; + if (delta >= 0 && delta <= 0xFF) { + packet[(*position)++] = delta; + } else { + ByteWriter::WriteBigEndian(&packet[*position], delta); + *position += 2; + } } - return rle_chunk; + + while ((*position % 4) != 0) + packet[(*position)++] = 0; + + RTC_DCHECK_EQ(*position, position_end); + return true; +} + +size_t TransportFeedback::BlockLength() const { + // Round size_bytes_ up to multiple of 32bits. + return (size_bytes_ + 3) & (~static_cast(3)); +} + +void TransportFeedback::Clear() { + num_seq_no_ = 0; + last_timestamp_us_ = GetBaseTimeUs(); + packets_.clear(); + encoded_chunks_.clear(); + last_chunk_->Clear(); + size_bytes_ = kTransportFeedbackHeaderSizeBytes; +} + +bool TransportFeedback::AddDeltaSize(DeltaSize delta_size) { + if (num_seq_no_ == kMaxReportedPackets) + return false; + size_t add_chunk_size = last_chunk_->Empty() ? kChunkSizeBytes : 0; + if (size_bytes_ + delta_size + add_chunk_size > kMaxSizeBytes) + return false; + + if (last_chunk_->CanAdd(delta_size)) { + size_bytes_ += add_chunk_size; + last_chunk_->Add(delta_size); + ++num_seq_no_; + return true; + } + if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes) + return false; + + encoded_chunks_.push_back(last_chunk_->Emit()); + size_bytes_ += kChunkSizeBytes; + last_chunk_->Add(delta_size); + ++num_seq_no_; + return true; } } // namespace rtcp diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h index 03a1c4f728..a1208293bc 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h @@ -11,12 +11,10 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_ -#include #include #include #include "webrtc/base/constructormagic.h" -#include "webrtc/base/deprecation.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" namespace webrtc { @@ -25,11 +23,12 @@ class CommonHeader; class TransportFeedback : public Rtpfb { public: - class PacketStatusChunk; // TODO(sprang): IANA reg? static constexpr uint8_t kFeedbackMessageType = 15; // Convert to multiples of 0.25ms. static constexpr int kDeltaScaleFactor = 250; + // Maximum number of packets (including missing) TransportFeedback can report. + static constexpr size_t kMaxReportedPackets = 0xffff; TransportFeedback(); ~TransportFeedback() override; @@ -59,23 +58,9 @@ class TransportFeedback : public Rtpfb { bool Parse(const CommonHeader& packet); static std::unique_ptr ParseFrom(const uint8_t* buffer, size_t length); - - RTC_DEPRECATED - void WithPacketSenderSsrc(uint32_t ssrc) { SetSenderSsrc(ssrc); } - RTC_DEPRECATED - void WithMediaSourceSsrc(uint32_t ssrc) { SetMediaSsrc(ssrc); } - RTC_DEPRECATED - void WithBase(uint16_t base_sequence, int64_t ref_timestamp_us) { - SetBase(base_sequence, ref_timestamp_us); - } - RTC_DEPRECATED - void WithFeedbackSequenceNumber(uint8_t feedback_sequence) { - SetFeedbackSequenceNumber(feedback_sequence); - } - RTC_DEPRECATED - bool WithReceivedPacket(uint16_t sequence_number, int64_t timestamp_us) { - return AddReceivedPacket(sequence_number, timestamp_us); - } + // Pre and postcondition for all public methods. Should always return true. + // This function is for tests. + bool IsConsistent() const; protected: bool Create(uint8_t* packet, @@ -86,30 +71,34 @@ class TransportFeedback : public Rtpfb { size_t BlockLength() const override; private: - static PacketStatusChunk* ParseChunk(const uint8_t* buffer, size_t max_size); + // Size in bytes of a delta time in rtcp packet. + // Valid values are 0 (packet wasn't received), 1 or 2. + using DeltaSize = uint8_t; + // Keeps DeltaSizes that can be encoded into single chunk if it is last chunk. + class LastChunk; + struct ReceivedPacket { + ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks) + : sequence_number(sequence_number), delta_ticks(delta_ticks) {} + uint16_t sequence_number; + int16_t delta_ticks; + }; - int64_t Unwrap(uint16_t sequence_number); - bool AddSymbol(StatusSymbol symbol, int64_t seq); - bool Encode(StatusSymbol symbol); - bool HandleRleCandidate(StatusSymbol symbol, - int current_capacity, - int delta_size); - void EmitRemaining(); - void EmitVectorChunk(); - void EmitRunLengthChunk(); + // Reset packet to consistent empty state. + void Clear(); - int32_t base_seq_; - int64_t base_time_; + bool AddDeltaSize(DeltaSize delta_size); + + uint16_t base_seq_no_; + uint16_t num_seq_no_; + int32_t base_time_ticks_; uint8_t feedback_seq_; - std::vector status_chunks_; - std::vector receive_deltas_; - int64_t last_seq_; - int64_t last_timestamp_; - std::deque symbol_vec_; - uint16_t first_symbol_cardinality_; - bool vec_needs_two_bit_symbols_; - uint32_t size_bytes_; + int64_t last_timestamp_us_; + std::vector packets_; + // All but last encoded packet chunks. + std::vector encoded_chunks_; + const std::unique_ptr last_chunk_; + size_t size_bytes_; RTC_DISALLOW_COPY_AND_ASSIGN(TransportFeedback); }; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc index 1d42ad1b5d..fac17a2524 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc @@ -53,8 +53,9 @@ class FeedbackTester { expected_seq_.clear(); expected_deltas_.clear(); feedback_.reset(new TransportFeedback()); - feedback_->SetBase(received_seq[0], received_ts[0]); + ASSERT_TRUE(feedback_->IsConsistent()); + int64_t last_time = feedback_->GetBaseTimeUs(); for (int i = 0; i < length; ++i) { int64_t time = received_ts[i]; @@ -66,15 +67,18 @@ class FeedbackTester { } last_time = time; } + ASSERT_TRUE(feedback_->IsConsistent()); expected_seq_.insert(expected_seq_.begin(), &received_seq[0], &received_seq[length]); } void VerifyPacket() { + ASSERT_TRUE(feedback_->IsConsistent()); serialized_ = feedback_->Build(); VerifyInternal(); feedback_ = TransportFeedback::ParseFrom(serialized_.data(), serialized_.size()); + ASSERT_TRUE(feedback_->IsConsistent()); ASSERT_NE(nullptr, feedback_.get()); VerifyInternal(); } @@ -371,8 +375,8 @@ TEST(RtcpPacketTest, TransportFeedback_Limits) { packet->SetBase(0, 0); EXPECT_TRUE(packet->AddReceivedPacket(0x0, 0)); EXPECT_TRUE(packet->AddReceivedPacket(0x8000, 1000)); - EXPECT_TRUE(packet->AddReceivedPacket(0xFFFF, 2000)); - EXPECT_FALSE(packet->AddReceivedPacket(0, 3000)); + EXPECT_TRUE(packet->AddReceivedPacket(0xFFFE, 2000)); + EXPECT_FALSE(packet->AddReceivedPacket(0xFFFF, 3000)); // Too large delta. packet.reset(new TransportFeedback()); @@ -398,7 +402,7 @@ TEST(RtcpPacketTest, TransportFeedback_Limits) { ((1L << 23) - 1); packet.reset(new TransportFeedback()); packet->SetBase(0, kMaxBaseTime); - packet->AddReceivedPacket(0, kMaxBaseTime); + EXPECT_TRUE(packet->AddReceivedPacket(0, kMaxBaseTime)); // Serialize and de-serialize (verify 24bit parsing). rtc::Buffer raw_packet = packet->Build(); packet = TransportFeedback::ParseFrom(raw_packet.data(), raw_packet.size());