[feat] add rtp packet history module

This commit is contained in:
dijunkun
2025-02-14 17:30:12 +08:00
parent 7b4bba4166
commit 1ef7c536f1
27 changed files with 365 additions and 1161 deletions

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) 2018 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 "rtp_packet_to_send.h"
#include <cstdint>
namespace webrtc {
RtpPacketToSend::RtpPacketToSend() {}
RtpPacketToSend::RtpPacketToSend(size_t capacity) : RtpPacket(capacity) {}
RtpPacketToSend::RtpPacketToSend(const RtpPacketToSend& packet) = default;
RtpPacketToSend::RtpPacketToSend(RtpPacketToSend&& packet) = default;
RtpPacketToSend& RtpPacketToSend::operator=(const RtpPacketToSend& packet) =
default;
RtpPacketToSend& RtpPacketToSend::operator=(RtpPacketToSend&& packet) = default;
RtpPacketToSend::~RtpPacketToSend() = default;
void RtpPacketToSend::set_packet_type(RtpPacketMediaType type) {
if (packet_type_ == RtpPacketMediaType::kAudio) {
original_packet_type_ = OriginalType::kAudio;
} else if (packet_type_ == RtpPacketMediaType::kVideo) {
original_packet_type_ = OriginalType::kVideo;
}
packet_type_ = type;
}
} // namespace webrtc

View File

@@ -1,176 +0,0 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKET_TO_SEND_H_
#define MODULES_RTP_RTCP_SOURCE_RTP_PACKET_TO_SEND_H_
#include <stddef.h>
#include <stdint.h>
#include <optional>
#include <utility>
#include "api/array_view.h"
#include "api/ref_counted_base.h"
#include "api/scoped_refptr.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "api/video/video_timing.h"
#include "rtp_packet.h"
#include "rtp_rtcp_defines.h"
// Forward declare the RtpPacket class since it is not in the webrtc namespace.
class RtpPacket;
namespace webrtc {
// Class to hold rtp packet with metadata for sender side.
// The metadata is not send over the wire, but packet sender may use it to
// create rtp header extensions or other data that is sent over the wire.
class RtpPacketToSend : public ::RtpPacket {
public:
// RtpPacketToSend::Type is deprecated. Use RtpPacketMediaType directly.
using Type = RtpPacketMediaType;
explicit RtpPacketToSend();
RtpPacketToSend(size_t capacity);
RtpPacketToSend(const RtpPacketToSend& packet);
RtpPacketToSend(RtpPacketToSend&& packet);
RtpPacketToSend& operator=(const RtpPacketToSend& packet);
RtpPacketToSend& operator=(RtpPacketToSend&& packet);
~RtpPacketToSend();
// Time in local time base as close as it can to frame capture time.
webrtc::Timestamp capture_time() const { return capture_time_; }
void set_capture_time(webrtc::Timestamp time) { capture_time_ = time; }
void set_packet_type(RtpPacketMediaType type);
std::optional<RtpPacketMediaType> packet_type() const { return packet_type_; }
enum class OriginalType { kAudio, kVideo };
// Original type does not change if packet type is changed to kRetransmission.
std::optional<OriginalType> original_packet_type() const {
return original_packet_type_;
}
// If this is a retransmission, indicates the sequence number of the original
// media packet that this packet represents. If RTX is used this will likely
// be different from SequenceNumber().
void set_retransmitted_sequence_number(uint16_t sequence_number) {
retransmitted_sequence_number_ = sequence_number;
}
std::optional<uint16_t> retransmitted_sequence_number() const {
return retransmitted_sequence_number_;
}
// If this is a retransmission, indicates the SSRC of the original
// media packet that this packet represents.
void set_original_ssrc(uint32_t ssrc) { original_ssrc_ = ssrc; }
std::optional<uint32_t> original_ssrc() const { return original_ssrc_; }
void set_allow_retransmission(bool allow_retransmission) {
allow_retransmission_ = allow_retransmission;
}
bool allow_retransmission() const { return allow_retransmission_; }
// An application can attach arbitrary data to an RTP packet using
// `additional_data`. The additional data does not affect WebRTC processing.
rtc::scoped_refptr<rtc::RefCountedBase> additional_data() const {
return additional_data_;
}
void set_additional_data(rtc::scoped_refptr<rtc::RefCountedBase> data) {
additional_data_ = std::move(data);
}
void set_packetization_finish_time(webrtc::Timestamp time) {
// SetExtension<VideoTimingExtension>(
// VideoSendTiming::GetDeltaCappedMs(time - capture_time_),
// VideoTimingExtension::kPacketizationFinishDeltaOffset);
}
void set_pacer_exit_time(webrtc::Timestamp time) {
// SetExtension<VideoTimingExtension>(
// VideoSendTiming::GetDeltaCappedMs(time - capture_time_),
// VideoTimingExtension::kPacerExitDeltaOffset);
}
void set_network_time(webrtc::Timestamp time) {
// SetExtension<VideoTimingExtension>(
// VideoSendTiming::GetDeltaCappedMs(time - capture_time_),
// VideoTimingExtension::kNetworkTimestampDeltaOffset);
}
void set_network2_time(webrtc::Timestamp time) {
// SetExtension<VideoTimingExtension>(
// VideoSendTiming::GetDeltaCappedMs(time - capture_time_),
// VideoTimingExtension::kNetwork2TimestampDeltaOffset);
}
// Indicates if packet is the first packet of a video frame.
void set_first_packet_of_frame(bool is_first_packet) {
is_first_packet_of_frame_ = is_first_packet;
}
bool is_first_packet_of_frame() const { return is_first_packet_of_frame_; }
// Indicates if packet contains payload for a video key-frame.
void set_is_key_frame(bool is_key_frame) { is_key_frame_ = is_key_frame; }
bool is_key_frame() const { return is_key_frame_; }
// Indicates if packets should be protected by FEC (Forward Error Correction).
void set_fec_protect_packet(bool protect) { fec_protect_packet_ = protect; }
bool fec_protect_packet() const { return fec_protect_packet_; }
// Indicates if packet is using RED encapsulation, in accordance with
// https://tools.ietf.org/html/rfc2198
void set_is_red(bool is_red) { is_red_ = is_red; }
bool is_red() const { return is_red_; }
// The amount of time spent in the send queue, used for totalPacketSendDelay.
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
void set_time_in_send_queue(TimeDelta time_in_send_queue) {
time_in_send_queue_ = time_in_send_queue;
}
std::optional<TimeDelta> time_in_send_queue() const {
return time_in_send_queue_;
}
// A sequence number guaranteed to be monotically increasing by one for all
// packets where transport feedback is expected.
std::optional<int64_t> transport_sequence_number() const {
return transport_sequence_number_;
}
void set_transport_sequence_number(int64_t transport_sequence_number) {
transport_sequence_number_ = transport_sequence_number;
}
// Transport is capable of handling explicit congestion notification and the
// RTP packet should be sent as ect(1)
// https://www.rfc-editor.org/rfc/rfc9331.html
bool send_as_ect1() const { return send_as_ect1_; }
void set_send_as_ect1() { send_as_ect1_ = true; }
private:
webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero();
std::optional<RtpPacketMediaType> packet_type_;
std::optional<OriginalType> original_packet_type_;
std::optional<uint32_t> original_ssrc_;
std::optional<int64_t> transport_sequence_number_;
bool allow_retransmission_ = false;
std::optional<uint16_t> retransmitted_sequence_number_;
rtc::scoped_refptr<rtc::RefCountedBase> additional_data_;
bool is_first_packet_of_frame_ = false;
bool is_key_frame_ = false;
bool fec_protect_packet_ = false;
bool is_red_ = false;
bool send_as_ect1_ = false;
std::optional<TimeDelta> time_in_send_queue_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTP_PACKET_TO_SEND_H_

View File

@@ -23,6 +23,7 @@
#include <vector>
#include "api/array_view.h"
#include "api/rtp_rtcp/rtp_rtcp_typedef.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
@@ -30,97 +31,10 @@
#include "congestion_control_feedback.h"
#include "report_block_data.h"
#define RTCP_CNAME_SIZE 256 // RFC 3550 page 44, including null termination
#define IP_PACKET_SIZE 1500 // we assume ethernet
class RtpPacket;
namespace webrtc {
class RtpPacketToSend;
namespace rtcp {
class TransportFeedback;
}
const int kVideoPayloadTypeFrequency = 90000;
// TODO(bugs.webrtc.org/6458): Remove this when all the depending projects are
// updated to correctly set rtp rate for RtcpSender.
const int kBogusRtpRateForAudioRtcp = 8000;
// Minimum RTP header size in bytes.
const uint8_t kRtpHeaderSize = 12;
// This enum must not have any gaps, i.e., all integers between
// kRtpExtensionNone and kRtpExtensionNumberOfExtensions must be valid enum
// entries.
enum RTPExtensionType : int {
kRtpExtensionNone,
kRtpExtensionTransmissionTimeOffset,
kRtpExtensionAudioLevel,
kRtpExtensionCsrcAudioLevel,
kRtpExtensionInbandComfortNoise,
kRtpExtensionAbsoluteSendTime,
kRtpExtensionAbsoluteCaptureTime,
kRtpExtensionVideoRotation,
kRtpExtensionTransportSequenceNumber,
kRtpExtensionTransportSequenceNumber02,
kRtpExtensionPlayoutDelay,
kRtpExtensionVideoContentType,
kRtpExtensionVideoLayersAllocation,
kRtpExtensionVideoTiming,
kRtpExtensionRtpStreamId,
kRtpExtensionRepairedRtpStreamId,
kRtpExtensionMid,
kRtpExtensionGenericFrameDescriptor,
kRtpExtensionGenericFrameDescriptor00 [[deprecated]] =
kRtpExtensionGenericFrameDescriptor,
kRtpExtensionDependencyDescriptor,
kRtpExtensionGenericFrameDescriptor02 [[deprecated]] =
kRtpExtensionDependencyDescriptor,
kRtpExtensionColorSpace,
kRtpExtensionVideoFrameTrackingId,
kRtpExtensionCorruptionDetection,
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
};
enum RTCPAppSubTypes { kAppSubtypeBwe = 0x00 };
// TODO(sprang): Make this an enum class once rtcp_receiver has been cleaned up.
enum RTCPPacketType : uint32_t {
kRtcpReport = 0x0001,
kRtcpSr = 0x0002,
kRtcpRr = 0x0004,
kRtcpSdes = 0x0008,
kRtcpBye = 0x0010,
kRtcpPli = 0x0020,
kRtcpNack = 0x0040,
kRtcpFir = 0x0080,
kRtcpTmmbr = 0x0100,
kRtcpTmmbn = 0x0200,
kRtcpSrReq = 0x0400,
kRtcpLossNotification = 0x2000,
kRtcpRemb = 0x10000,
kRtcpTransmissionTimeOffset = 0x20000,
kRtcpXrReceiverReferenceTime = 0x40000,
kRtcpXrDlrrReportBlock = 0x80000,
kRtcpTransportFeedback = 0x100000,
kRtcpXrTargetBitrate = 0x200000,
};
enum class KeyFrameReqMethod : uint8_t {
kNone, // Don't request keyframes.
kPliRtcp, // Request keyframes through Picture Loss Indication.
kFirRtcp // Request keyframes through Full Intra-frame Request.
};
enum RtxMode {
kRtxOff = 0x0,
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
// instead of padding.
};
const size_t kRtxHeaderSize = 2;
struct RtpState {
uint16_t sequence_number = 0;
@@ -159,9 +73,6 @@ class NetworkLinkRtcpObserver {
public:
virtual ~NetworkLinkRtcpObserver() = default;
virtual void OnTransportFeedback(
Timestamp /* receive_time */,
const rtcp::TransportFeedback& /* feedback */) {}
// RFC 8888 congestion control feedback.
virtual void OnCongestionControlFeedback(
Timestamp /* receive_time */,
@@ -177,17 +88,6 @@ class NetworkLinkRtcpObserver {
virtual void OnRttUpdate(Timestamp /* receive_time */, TimeDelta /* rtt */) {}
};
// NOTE! `kNumMediaTypes` must be kept in sync with RtpPacketMediaType!
static constexpr size_t kNumMediaTypes = 5;
enum class RtpPacketMediaType : size_t {
kAudio, // Audio media packets.
kVideo, // Video media packets.
kRetransmission, // Retransmisions, sent as response to NACK.
kForwardErrorCorrection, // FEC packets.
kPadding = kNumMediaTypes - 1, // RTX or plain padding sent to maintain BWE.
// Again, don't forget to update `kNumMediaTypes` if you add another value!
};
struct RtpPacketSendInfo {
static RtpPacketSendInfo From(const RtpPacketToSend& rtp_packet_to_send,
const PacedPacketInfo& pacing_info);

View File

@@ -1,672 +0,0 @@
/*
* 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 "transport_feedback.h"
#include <algorithm>
#include <cstdint>
#include <numeric>
#include <utility>
#include "byte_io.h"
#include "common_header.h"
#include "log.h"
#include "module_common_types_public.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;
// 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 TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
constexpr TimeDelta kTimeWrapPeriod = kBaseTimeTick * (1 << 24);
// 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 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
} // namespace
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 {
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) {
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;
}
void TransportFeedback::LastChunk::AddMissingPackets(size_t num_missing) {
std::fill(delta_sizes_.begin(), delta_sizes_.end(), DeltaSize(0));
size_ = num_missing;
}
uint16_t TransportFeedback::LastChunk::Emit() {
if (all_same_) {
uint16_t chunk = EncodeRunLength();
Clear();
return chunk;
}
if (size_ == kMaxOneBitCapacity) {
uint16_t chunk = EncodeOneBit();
Clear();
return chunk;
}
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 {
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<DeltaSize>* deltas) const {
if (all_same_) {
deltas->insert(deltas->end(), size_, delta_sizes_[0]);
} else {
deltas->insert(deltas->end(), delta_sizes_.begin(),
delta_sizes_.begin() + 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 {
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) {
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 {
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) {
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 {
return (delta_sizes_[0] << 13) | static_cast<uint16_t>(size_);
}
void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk,
size_t max_count) {
size_ = std::min<size_t>(chunk & 0x1fff, max_count);
DeltaSize delta_size = (chunk >> 13) & 0x03;
has_large_delta_ = delta_size >= kLarge;
all_same_ = true;
// To make it consistent with Add function, populate delta_sizes_ beyond 1st.
for (size_t i = 0; i < std::min<size_t>(size_, kMaxVectorCapacity); ++i)
delta_sizes_[i] = delta_size;
}
TransportFeedback::TransportFeedback()
: TransportFeedback(/*include_timestamps=*/true) {}
TransportFeedback::TransportFeedback(bool include_timestamps)
: base_seq_no_(0),
num_seq_no_(0),
base_time_ticks_(0),
feedback_seq_(0),
include_timestamps_(include_timestamps),
last_timestamp_(Timestamp::Zero()),
size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
TransportFeedback::TransportFeedback(const TransportFeedback&) = default;
TransportFeedback::TransportFeedback(TransportFeedback&& other)
: base_seq_no_(other.base_seq_no_),
num_seq_no_(other.num_seq_no_),
base_time_ticks_(other.base_time_ticks_),
feedback_seq_(other.feedback_seq_),
include_timestamps_(other.include_timestamps_),
last_timestamp_(other.last_timestamp_),
received_packets_(std::move(other.received_packets_)),
all_packets_(std::move(other.all_packets_)),
encoded_chunks_(std::move(other.encoded_chunks_)),
last_chunk_(other.last_chunk_),
size_bytes_(other.size_bytes_) {
other.Clear();
}
TransportFeedback::~TransportFeedback() {}
void TransportFeedback::SetBase(uint16_t base_sequence,
Timestamp ref_timestamp) {
base_seq_no_ = base_sequence;
base_time_ticks_ =
(ref_timestamp.us() % kTimeWrapPeriod.us()) / kBaseTimeTick.us();
last_timestamp_ = BaseTime();
}
void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) {
feedback_seq_ = feedback_sequence;
}
bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
Timestamp timestamp) {
// Set delta to zero if timestamps are not included, this will simplify the
// encoding process.
int16_t delta = 0;
if (include_timestamps_) {
// Convert to ticks and round.
if (last_timestamp_ > timestamp) {
timestamp += (last_timestamp_ - timestamp).RoundUpTo(kTimeWrapPeriod);
}
int64_t delta_full =
(timestamp - last_timestamp_).us() % kTimeWrapPeriod.us();
if (delta_full > kTimeWrapPeriod.us() / 2) {
delta_full -= kTimeWrapPeriod.us();
delta_full -= kDeltaTick.us() / 2;
} else {
delta_full += kDeltaTick.us() / 2;
}
delta_full /= kDeltaTick.us();
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_WARN("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;
uint16_t num_missing_packets = sequence_number - next_seq_no;
if (!AddMissingPackets(num_missing_packets)) return false;
}
DeltaSize delta_size = (delta >= 0 && delta <= 0xff) ? 1 : 2;
if (!AddDeltaSize(delta_size)) return false;
received_packets_.emplace_back(sequence_number, delta);
last_timestamp_ += delta * kDeltaTick;
if (include_timestamps_) {
size_bytes_ += delta_size;
}
return true;
}
const std::vector<TransportFeedback::ReceivedPacket>&
TransportFeedback::GetReceivedPackets() const {
return received_packets_;
}
void TransportFeedback::ForAllPackets(
rtc::FunctionView<void(uint16_t, TimeDelta)> handler) const {
TimeDelta delta_since_base = TimeDelta::Zero();
auto received_it = received_packets_.begin();
const uint16_t last_seq_num = base_seq_no_ + num_seq_no_;
for (uint16_t seq_num = base_seq_no_; seq_num != last_seq_num; ++seq_num) {
if (received_it != received_packets_.end() &&
received_it->sequence_number() == seq_num) {
delta_since_base += received_it->delta();
handler(seq_num, delta_since_base);
++received_it;
} else {
handler(seq_num, TimeDelta::PlusInfinity());
}
}
}
uint16_t TransportFeedback::GetBaseSequence() const { return base_seq_no_; }
Timestamp TransportFeedback::BaseTime() const {
// Add an extra kTimeWrapPeriod to allow add received packets arrived earlier
// than the first added packet (and thus allow to record negative deltas)
// even when base_time_ticks_ == 0.
return Timestamp::Zero() + kTimeWrapPeriod +
int64_t{base_time_ticks_} * kBaseTimeTick;
}
TimeDelta TransportFeedback::GetBaseDelta(Timestamp prev_timestamp) const {
TimeDelta delta = BaseTime() - prev_timestamp;
// Compensate for wrap around.
if ((delta - kTimeWrapPeriod).Abs() < delta.Abs()) {
delta -= kTimeWrapPeriod; // Wrap backwards.
} else if ((delta + kTimeWrapPeriod).Abs() < delta.Abs()) {
delta += kTimeWrapPeriod; // Wrap forwards.
}
return delta;
}
// De-serialize packet.
bool TransportFeedback::Parse(const CommonHeader& packet) {
if (packet.payload_size_bytes() < kMinPayloadSizeBytes) {
LOG_WARN(
"Buffer too small ({} bytes) to fit a FeedbackPacket. Minimum size = "
"{}",
packet.payload_size_bytes(), kMinPayloadSizeBytes);
return false;
}
const uint8_t* const payload = packet.payload();
ParseCommonFeedback(payload);
base_seq_no_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
uint16_t status_count = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
base_time_ticks_ = ByteReader<uint32_t, 3>::ReadBigEndian(&payload[12]);
feedback_seq_ = payload[15];
Clear();
size_t index = 16;
const size_t end_index = packet.payload_size_bytes();
if (status_count == 0) {
LOG_WARN("Empty feedback messages not allowed.");
return false;
}
std::vector<uint8_t> delta_sizes;
delta_sizes.reserve(status_count);
while (delta_sizes.size() < status_count) {
if (index + kChunkSizeBytes > end_index) {
LOG_WARN("Buffer overflow while parsing packet.");
Clear();
return false;
}
uint16_t chunk = ByteReader<uint16_t>::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();
num_seq_no_ = status_count;
uint16_t seq_no = base_seq_no_;
size_t recv_delta_size =
std::accumulate(delta_sizes.begin(), delta_sizes.end(), 0);
// Determine if timestamps, that is, recv_delta are included in the packet.
if (end_index >= index + recv_delta_size) {
for (size_t delta_size : delta_sizes) {
switch (delta_size) {
case 0:
break;
case 1: {
int16_t delta = payload[index];
received_packets_.emplace_back(seq_no, delta);
last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
case 2: {
int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
received_packets_.emplace_back(seq_no, delta);
last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
case 3:
Clear();
LOG_WARN("Invalid delta_size for seq_no {}", seq_no);
return false;
default:
break;
}
++seq_no;
}
} else {
// The packet does not contain receive deltas.
include_timestamps_ = false;
for (size_t delta_size : delta_sizes) {
// Use delta sizes to detect if packet was received.
if (delta_size > 0) {
received_packets_.emplace_back(seq_no, 0);
}
++seq_no;
}
}
size_bytes_ = RtcpPacket::kHeaderLength + 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;
}
bool TransportFeedback::IsConsistent() const {
size_t packet_size = kTransportFeedbackHeaderSizeBytes;
std::vector<DeltaSize> 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_ERROR("{} packets encoded. Expected {}", delta_sizes.size(),
num_seq_no_);
return false;
}
Timestamp timestamp = BaseTime();
auto packet_it = received_packets_.begin();
uint16_t seq_no = base_seq_no_;
for (DeltaSize delta_size : delta_sizes) {
if (delta_size > 0) {
if (packet_it == received_packets_.end()) {
LOG_ERROR("Failed to find delta for seq_no {}", seq_no);
return false;
}
if (packet_it->sequence_number() != seq_no) {
LOG_ERROR("Expected to find delta for seq_no {}. Next delta is for {}",
seq_no, packet_it->sequence_number());
return false;
}
if (delta_size == 1 &&
(packet_it->delta_ticks() < 0 || packet_it->delta_ticks() > 0xff)) {
LOG_ERROR("Delta {} for seq_no {} doesn't fit into one byte",
packet_it->delta_ticks(), seq_no);
return false;
}
timestamp += packet_it->delta();
++packet_it;
}
if (include_timestamps_) {
packet_size += delta_size;
}
++seq_no;
}
if (packet_it != received_packets_.end()) {
LOG_ERROR("Unencoded delta for seq_no {}", packet_it->sequence_number());
return false;
}
if (timestamp != last_timestamp_) {
LOG_ERROR("Last timestamp mismatch. Calculated: {}. Saved: {}",
ToString(timestamp), ToString(last_timestamp_));
return false;
}
if (size_bytes_ != packet_size) {
LOG_ERROR("Rtcp packet size mismatch. Calculated: {}. Saved: {}",
packet_size, size_bytes_);
return false;
}
return true;
}
size_t TransportFeedback::BlockLength() const {
// Round size_bytes_ up to multiple of 32bits.
return (size_bytes_ + 3) & (~static_cast<size_t>(3));
}
size_t TransportFeedback::PaddingLength() const {
return BlockLength() - size_bytes_;
}
// 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();
const size_t padding_length = PaddingLength();
bool has_padding = padding_length > 0;
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), has_padding,
packet, position);
CreateCommonFeedback(packet + *position);
*position += kCommonFeedbackLength;
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], base_seq_no_);
*position += 2;
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], num_seq_no_);
*position += 2;
ByteWriter<uint32_t, 3>::WriteBigEndian(&packet[*position], base_time_ticks_);
*position += 3;
packet[(*position)++] = feedback_seq_;
for (uint16_t chunk : encoded_chunks_) {
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
*position += 2;
}
if (!last_chunk_.Empty()) {
uint16_t chunk = last_chunk_.EncodeLast();
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
*position += 2;
}
if (include_timestamps_) {
for (const auto& received_packet : received_packets_) {
int16_t delta = received_packet.delta_ticks();
if (delta >= 0 && delta <= 0xFF) {
packet[(*position)++] = delta;
} else {
ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
*position += 2;
}
}
}
if (padding_length > 0) {
for (size_t i = 0; i < padding_length - 1; ++i) {
packet[(*position)++] = 0;
}
packet[(*position)++] = padding_length;
}
return true;
}
void TransportFeedback::Clear() {
num_seq_no_ = 0;
last_timestamp_ = BaseTime();
received_packets_.clear();
all_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;
}
bool TransportFeedback::AddMissingPackets(size_t num_missing_packets) {
size_t new_num_seq_no = num_seq_no_ + num_missing_packets;
if (new_num_seq_no > kMaxReportedPackets) {
return false;
}
if (!last_chunk_.Empty()) {
while (num_missing_packets > 0 && last_chunk_.CanAdd(0)) {
last_chunk_.Add(0);
--num_missing_packets;
}
if (num_missing_packets == 0) {
num_seq_no_ = new_num_seq_no;
return true;
}
encoded_chunks_.push_back(last_chunk_.Emit());
}
size_t full_chunks = num_missing_packets / LastChunk::kMaxRunLengthCapacity;
size_t partial_chunk = num_missing_packets % LastChunk::kMaxRunLengthCapacity;
size_t num_chunks = full_chunks + (partial_chunk > 0 ? 1 : 0);
if (size_bytes_ + kChunkSizeBytes * num_chunks > kMaxSizeBytes) {
num_seq_no_ = (new_num_seq_no - num_missing_packets);
return false;
}
size_bytes_ += kChunkSizeBytes * num_chunks;
// T = 0, S = 0, run length = kMaxRunLengthCapacity, see EncodeRunLength().
encoded_chunks_.insert(encoded_chunks_.end(), full_chunks,
LastChunk::kMaxRunLengthCapacity);
last_chunk_.AddMissingPackets(partial_chunk);
num_seq_no_ = new_num_seq_no;
return true;
}
} // namespace rtcp
} // namespace webrtc

View File

@@ -1,182 +0,0 @@
/*
* 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.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
#include <array>
#include <memory>
#include <vector>
#include "api/function_view.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtpfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class TransportFeedback : public Rtpfb {
public:
class ReceivedPacket {
public:
ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)
: sequence_number_(sequence_number), delta_ticks_(delta_ticks) {}
ReceivedPacket(const ReceivedPacket&) = default;
ReceivedPacket& operator=(const ReceivedPacket&) = default;
uint16_t sequence_number() const { return sequence_number_; }
int16_t delta_ticks() const { return delta_ticks_; }
TimeDelta delta() const { return delta_ticks_ * kDeltaTick; }
private:
uint16_t sequence_number_;
int16_t delta_ticks_;
};
// TODO(sprang): IANA reg?
static constexpr uint8_t kFeedbackMessageType = 15;
// Convert to multiples of 0.25ms.
static constexpr TimeDelta kDeltaTick = TimeDelta::Micros(250);
// Maximum number of packets (including missing) TransportFeedback can report.
static constexpr size_t kMaxReportedPackets = 0xffff;
TransportFeedback();
// If `include_timestamps` is set to false, the created packet will not
// contain the receive delta block.
explicit TransportFeedback(bool include_timestamps);
TransportFeedback(const TransportFeedback&);
TransportFeedback(TransportFeedback&&);
~TransportFeedback() override;
void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
Timestamp ref_timestamp); // Reference timestamp for this msg.
void SetFeedbackSequenceNumber(uint8_t feedback_sequence);
// NOTE: This method requires increasing sequence numbers (excepting wraps).
bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp);
const std::vector<ReceivedPacket>& GetReceivedPackets() const;
// Calls `handler` for all packets this feedback describes.
// For received packets pass receieve time as `delta_since_base` since the
// `BaseTime()`. For missed packets calls `handler` with `delta_since_base =
// PlusInfinity()`.
void ForAllPackets(rtc::FunctionView<void(uint16_t sequence_number,
TimeDelta delta_since_base)>
handler) const;
uint16_t GetBaseSequence() const;
// Returns number of packets (including missing) this feedback describes.
size_t GetPacketStatusCount() const { return num_seq_no_; }
// Get the reference time including any precision loss.
Timestamp BaseTime() const;
// Get the unwrapped delta between current base time and `prev_timestamp`.
TimeDelta GetBaseDelta(Timestamp prev_timestamp) const;
// Does the feedback packet contain timestamp information?
bool IncludeTimestamps() const { return include_timestamps_; }
bool Parse(const CommonHeader& packet);
static std::unique_ptr<TransportFeedback> ParseFrom(const uint8_t* buffer,
size_t length);
// Pre and postcondition for all public methods. Should always return true.
// This function is for tests.
bool IsConsistent() const;
size_t BlockLength() const override;
size_t PaddingLength() const;
bool Create(uint8_t* packet, size_t* position, size_t max_length,
PacketReadyCallback callback) const override;
private:
// 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 {
public:
using DeltaSize = TransportFeedback::DeltaSize;
static constexpr size_t kMaxRunLengthCapacity = 0x1fff;
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);
// Equivalent to calling Add(0) `num_missing` times. Assumes `Empty()`.
void AddMissingPackets(size_t num_missing);
// 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<DeltaSize>* deltas) const;
private:
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);
std::array<DeltaSize, kMaxVectorCapacity> delta_sizes_;
size_t size_;
bool all_same_;
bool has_large_delta_;
};
// Reset packet to consistent empty state.
void Clear();
bool AddDeltaSize(DeltaSize delta_size);
// Adds `num_missing_packets` deltas of size 0.
bool AddMissingPackets(size_t num_missing_packets);
uint16_t base_seq_no_;
uint16_t num_seq_no_;
uint32_t base_time_ticks_;
uint8_t feedback_seq_;
bool include_timestamps_;
Timestamp last_timestamp_;
std::vector<ReceivedPacket> received_packets_;
std::vector<ReceivedPacket> all_packets_;
// All but last encoded packet chunks.
std::vector<uint16_t> encoded_chunks_;
LastChunk last_chunk_;
size_t size_bytes_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_

View File

@@ -24,7 +24,6 @@
#include "log.h"
#include "ntp_time_util.h"
#include "rtp_packet_to_send.h"
#include "transport_feedback.h"
namespace webrtc {
@@ -163,77 +162,6 @@ std::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
return std::nullopt;
}
std::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ProcessTransportFeedback(
const rtcp::TransportFeedback& feedback, Timestamp feedback_receive_time) {
if (feedback.GetPacketStatusCount() == 0) {
LOG_INFO("Empty transport feedback packet received.");
return std::nullopt;
}
// Add timestamp deltas to a local time base selected on first packet arrival.
// This won't be the true time base, but makes it easier to manually inspect
// time stamps.
if (last_transport_feedback_base_time_.IsInfinite()) {
current_offset_ = feedback_receive_time;
} else {
// TODO(srte): We shouldn't need to do rounding here.
const TimeDelta delta =
feedback.GetBaseDelta(last_transport_feedback_base_time_)
.RoundDownTo(TimeDelta::Millis(1));
// Protect against assigning current_offset_ negative value.
if (delta < Timestamp::Zero() - current_offset_) {
LOG_WARN("Unexpected feedback timestamp received.");
current_offset_ = feedback_receive_time;
} else {
current_offset_ += delta;
}
}
last_transport_feedback_base_time_ = feedback.BaseTime();
std::vector<PacketResult> packet_result_vector;
packet_result_vector.reserve(feedback.GetPacketStatusCount());
size_t failed_lookups = 0;
size_t ignored = 0;
feedback.ForAllPackets([&](uint16_t sequence_number,
TimeDelta delta_since_base) {
int64_t seq_num = seq_num_unwrapper_.Unwrap(sequence_number);
std::optional<PacketFeedback> packet_feedback = RetrievePacketFeedback(
seq_num, /*received=*/delta_since_base.IsFinite());
if (!packet_feedback) {
++failed_lookups;
return;
}
if (delta_since_base.IsFinite()) {
packet_feedback->receive_time =
current_offset_ + delta_since_base.RoundDownTo(TimeDelta::Millis(1));
}
if (packet_feedback->network_route == network_route_) {
PacketResult result;
result.sent_packet = packet_feedback->sent;
result.receive_time = packet_feedback->receive_time;
packet_result_vector.push_back(result);
} else {
++ignored;
}
});
if (failed_lookups > 0) {
LOG_WARN(
"Failed to lookup send time for {} packet {}. Packets reordered or "
"send time history too small?",
failed_lookups, (failed_lookups > 1 ? "s" : ""));
}
if (ignored > 0) {
LOG_INFO("Ignoring packets because they were sent on a different route.",
ignored);
}
return ToTransportFeedback(std::move(packet_result_vector),
feedback_receive_time, /*suports_ecn=*/false);
}
std::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ProcessCongestionControlFeedback(
const rtcp::CongestionControlFeedback& feedback,

View File

@@ -76,9 +76,6 @@ class TransportFeedbackAdapter {
std::optional<SentPacket> ProcessSentPacket(
const rtc::SentPacket& sent_packet);
std::optional<TransportPacketsFeedback> ProcessTransportFeedback(
const rtcp::TransportFeedback& feedback, Timestamp feedback_receive_time);
std::optional<TransportPacketsFeedback> ProcessCongestionControlFeedback(
const rtcp::CongestionControlFeedback& feedback,
Timestamp feedback_receive_time);