[feat] receive and parse congestion control feedback supported

This commit is contained in:
dijunkun
2025-01-13 17:12:28 +08:00
parent 63ed77e43a
commit ba268016e4
15 changed files with 1112 additions and 7 deletions

View File

@@ -1,5 +1,201 @@
#include "congestion_control.h"
#include <algorithm>
#include <numeric>
#include <vector>
#include "log.h"
constexpr int64_t kLossUpdateInterval = 1000;
// Pacing-rate relative to our target send rate.
// Multiplicative factor that is applied to the target bitrate to calculate
// the number of bytes that can be transmitted per interval.
// Increasing this factor will result in lower delays in cases of bitrate
// overshoots from the encoder.
constexpr float kDefaultPaceMultiplier = 2.5f;
// If the probe result is far below the current throughput estimate
// it's unlikely that the probe is accurate, so we don't want to drop too far.
// However, if we actually are overusing, we want to drop to something slightly
// below the current throughput estimate to drain the network queues.
constexpr double kProbeDropThroughputFraction = 0.85;
CongestionControl::CongestionControl() {}
CongestionControl::~CongestionControl() {}
CongestionControl::~CongestionControl() {}
NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
if (report.packet_feedbacks.empty()) {
// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
// against building very large network queues.
return NetworkControlUpdate();
}
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->UpdateOutstandingData(
report.data_in_flight);
}
int64_t max_feedback_rtt = std::numeric_limits<int64_t>::min();
int64_t min_propagation_rtt = std::numeric_limits<int64_t>::max();
int64_t max_recv_time = std::numeric_limits<int64_t>::min();
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
for (const auto& feedback : feedbacks)
max_recv_time = std::max(max_recv_time, feedback.receive_time);
for (const auto& feedback : feedbacks) {
int64_t feedback_rtt =
report.feedback_time - feedback.sent_packet.send_time;
int64_t min_pending_time = max_recv_time - feedback.receive_time;
int64_t propagation_rtt = feedback_rtt - min_pending_time;
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
}
if (max_feedback_rtt != std::numeric_limits<int64_t>::min() &&
min_propagation_rtt != std::numeric_limits<int64_t>::max()) {
feedback_max_rtts_.push_back(max_feedback_rtt);
const size_t kMaxFeedbackRttWindow = 32;
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
feedback_max_rtts_.pop_front();
// TODO(srte): Use time since last unacknowledged packet.
// bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time,
// min_propagation_rtt);
}
if (packet_feedback_only_) {
if (!feedback_max_rtts_.empty()) {
int64_t sum_rtt_ms =
std::accumulate(feedback_max_rtts_.begin(), feedback_max_rtts_.end(),
static_cast<int64_t>(0));
// int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
// if (delay_based_bwe_) delay_based_bwe_->OnRttUpdate(mean_rtt_ms);
}
int64_t feedback_min_rtt = std::numeric_limits<int64_t>::max();
for (const auto& packet_feedback : feedbacks) {
int64_t pending_time = max_recv_time - packet_feedback.receive_time;
int64_t rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
// Value used for predicting NACK round trip time in FEC controller.
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
}
if (feedback_min_rtt != std::numeric_limits<int64_t>::max() &&
feedback_min_rtt != std::numeric_limits<int64_t>::min()) {
// bandwidth_estimation_->UpdateRtt(feedback_min_rtt,
// report.feedback_time);
}
expected_packets_since_last_loss_update_ +=
report.PacketsWithFeedback().size();
for (const auto& packet_feedback : report.PacketsWithFeedback()) {
if (!packet_feedback.IsReceived())
lost_packets_since_last_loss_update_ += 1;
}
if (report.feedback_time > next_loss_update_) {
next_loss_update_ = report.feedback_time + kLossUpdateInterval;
// bandwidth_estimation_->UpdatePacketsLost(
// lost_packets_since_last_loss_update_,
// expected_packets_since_last_loss_update_, report.feedback_time);
expected_packets_since_last_loss_update_ = 0;
lost_packets_since_last_loss_update_ = 0;
}
}
// std::optional<int64_t> alr_start_time =
// alr_detector_->GetApplicationLimitedRegionStartTime();
// if (previously_in_alr_ && !alr_start_time.has_value()) {
// int64_t now_ms = report.feedback_time;
// acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
// probe_controller_->SetAlrEndedTimeMs(now_ms);
// }
// previously_in_alr_ = alr_start_time.has_value();
// acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
// report.SortedByReceiveTime());
// auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate();
// bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate,
// report.feedback_time);
for (const auto& feedback : report.SortedByReceiveTime()) {
if (feedback.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe) {
// probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback);
}
}
// if (network_estimator_) {
// network_estimator_->OnTransportPacketsFeedback(report);
// // SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
// }
// std::optional<int64_t> probe_bitrate =
// probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
// if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
// estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
// *probe_bitrate < estimate_->link_capacity_lower) {
// probe_bitrate.reset();
// }
// if (limit_probes_lower_than_throughput_estimate_ && probe_bitrate &&
// acknowledged_bitrate) {
// Limit the backoff to something slightly below the acknowledged
// bitrate. ("Slightly below" because we want to drain the queues
// if we are actually overusing.)
// The acknowledged bitrate shouldn't normally be higher than the delay
// based estimate, but it could happen e.g. due to packet bursts or
// encoder overshoot. We use std::min to ensure that a probe result
// below the current BWE never causes an increase.
// int64_t limit =
// std::min(delay_based_bwe_->last_estimate(),
// *acknowledged_bitrate * kProbeDropThroughputFraction);
// probe_bitrate = std::max(*probe_bitrate, limit);
// }
NetworkControlUpdate update;
bool recovered_from_overuse = false;
// DelayBasedBwe::Result result;
// result = delay_based_bwe_->IncomingPacketFeedbackVector(
// report, acknowledged_bitrate, probe_bitrate, estimate_,
// alr_start_time.has_value());
// if (result.updated) {
// if (result.probe) {
// bandwidth_estimation_->SetSendBitrate(result.target_bitrate,
// report.feedback_time);
// }
// Since SetSendBitrate now resets the delay-based estimate, we have to
// call UpdateDelayBasedEstimate after SetSendBitrate.
// bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time,
// result.target_bitrate);
// }
// bandwidth_estimation_->UpdateLossBasedEstimator(
// report, result.delay_detector_state, probe_bitrate,
// alr_start_time.has_value());
// if (result.updated) {
// // Update the estimate in the ProbeController, in case we want to probe.
// MaybeTriggerOnNetworkChanged(&update, report.feedback_time);
// }
// recovered_from_overuse = result.recovered_from_overuse;
// if (recovered_from_overuse) {
// probe_controller_->SetAlrStartTimeMs(alr_start_time);
// auto probes = probe_controller_->RequestProbe(report.feedback_time);
// update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
// probes.begin(), probes.end());
// }
// No valid RTT could be because send-side BWE isn't used, in which case
// we don't try to limit the outstanding packets.
// if (rate_control_settings_.UseCongestionWindow() &&
// max_feedback_rtt.IsFinite()) {
// UpdateCongestionWindowSize();
// }
if (congestion_window_pushback_controller_ && current_data_window_) {
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
update.congestion_window = current_data_window_;
}
return update;
}

View File

@@ -1,12 +1,35 @@
#ifndef _CONGESTION_CONTROL_H_
#define _CONGESTION_CONTROL_H_
#include <deque>
#include <memory>
#include "congestion_window_pushback_controller.h"
#include "network_types.h"
class CongestionControl {
public:
CongestionControl();
~CongestionControl();
public:
NetworkControlUpdate OnTransportPacketsFeedback(
TransportPacketsFeedback report);
private:
const std::unique_ptr<CongestionWindowPushbackController>
congestion_window_pushback_controller_;
private:
std::deque<int64_t> feedback_max_rtts_;
// std::unique_ptr<SendSideBandwidthEstimation> bandwidth_estimation_;
int expected_packets_since_last_loss_update_ = 0;
int lost_packets_since_last_loss_update_ = 0;
int64_t next_loss_update_ = std::numeric_limits<int64_t>::min();
const bool packet_feedback_only_ = false;
bool previously_in_alr_ = false;
// const bool limit_probes_lower_than_throughput_estimate_;
std::optional<int64_t> current_data_window_;
};
#endif

View File

@@ -0,0 +1,57 @@
/*
* 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 "congestion_window_pushback_controller.h"
#include <algorithm>
#include <cstdint>
CongestionWindowPushbackController::CongestionWindowPushbackController() {}
void CongestionWindowPushbackController::UpdateOutstandingData(
int64_t outstanding_bytes) {
outstanding_bytes_ = outstanding_bytes;
}
void CongestionWindowPushbackController::UpdatePacingQueue(
int64_t pacing_bytes) {
pacing_bytes_ = pacing_bytes;
}
void CongestionWindowPushbackController::SetDataWindow(int64_t data_window) {
current_data_window_ = data_window;
}
uint32_t CongestionWindowPushbackController::UpdateTargetBitrate(
uint32_t bitrate_bps) {
if (!current_data_window_ || current_data_window_ == 0) return bitrate_bps;
int64_t total_bytes = outstanding_bytes_;
if (add_pacing_) total_bytes += pacing_bytes_;
double fill_ratio =
total_bytes / static_cast<double>(current_data_window_.value());
if (fill_ratio > 1.5) {
encoding_rate_ratio_ *= 0.9;
} else if (fill_ratio > 1) {
encoding_rate_ratio_ *= 0.95;
} else if (fill_ratio < 0.1) {
encoding_rate_ratio_ = 1.0;
} else {
encoding_rate_ratio_ *= 1.05;
encoding_rate_ratio_ = std::min(encoding_rate_ratio_, 1.0);
}
uint32_t adjusted_target_bitrate_bps =
static_cast<uint32_t>(bitrate_bps * encoding_rate_ratio_);
// Do not adjust below the minimum pushback bitrate but do obey if the
// original estimate is below it.
bitrate_bps = adjusted_target_bitrate_bps < min_pushback_target_bitrate_bps_
? std::min(bitrate_bps, min_pushback_target_bitrate_bps_)
: adjusted_target_bitrate_bps;
return bitrate_bps;
}

View File

@@ -0,0 +1,36 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-13
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#define _CONGESTION_WINDOW_PUSHBACK_CONTROLLER_H_
#include <stdint.h>
#include <optional>
// This class enables pushback from congestion window directly to video encoder.
// When the congestion window is filling up, the video encoder target bitrate
// will be reduced accordingly to accommodate the network changes. To avoid
// pausing video too frequently, a minimum encoder target bitrate threshold is
// used to prevent video pause due to a full congestion window.
class CongestionWindowPushbackController {
public:
explicit CongestionWindowPushbackController();
void UpdateOutstandingData(int64_t outstanding_bytes);
void UpdatePacingQueue(int64_t pacing_bytes);
uint32_t UpdateTargetBitrate(uint32_t bitrate_bps);
void SetDataWindow(int64_t data_window);
private:
const bool add_pacing_ = true;
const uint32_t min_pushback_target_bitrate_bps_ = 10000;
std::optional<int64_t> current_data_window_ = std::nullopt;
int64_t outstanding_bytes_ = 0;
int64_t pacing_bytes_ = 0;
double encoding_rate_ratio_ = 1.0;
};
#endif

View File

@@ -0,0 +1,229 @@
/*
* 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_adapter.h"
#include <stdlib.h>
#include <algorithm>
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include "log.h"
constexpr int64_t kSendTimeHistoryWindow = 60;
void InFlightBytesTracker::AddInFlightPacketBytes(
const PacketFeedback& packet) {
auto it = in_flight_data_.find(packet.network_route);
if (it != in_flight_data_.end()) {
it->second += packet.sent.size;
} else {
in_flight_data_.insert({packet.network_route, packet.sent.size});
}
}
void InFlightBytesTracker::RemoveInFlightPacketBytes(
const PacketFeedback& packet) {
if (packet.sent.send_time == std::numeric_limits<int64_t>::max() ||
packet.sent.send_time == std::numeric_limits<int64_t>::min())
return;
auto it = in_flight_data_.find(packet.network_route);
if (it != in_flight_data_.end()) {
it->second -= packet.sent.size;
if (it->second == 0) in_flight_data_.erase(it);
}
}
int64_t InFlightBytesTracker::GetOutstandingData(
const NetworkRoute& network_route) const {
auto it = in_flight_data_.find(network_route);
if (it != in_flight_data_.end()) {
return it->second;
} else {
return 0;
}
}
// Comparator for consistent map with NetworkRoute as key.
bool InFlightBytesTracker::NetworkRouteComparator::operator()(
const NetworkRoute& a, const NetworkRoute& b) const {
if (a.local.network_id() != b.local.network_id())
return a.local.network_id() < b.local.network_id();
if (a.remote.network_id() != b.remote.network_id())
return a.remote.network_id() < b.remote.network_id();
if (a.local.adapter_id() != b.local.adapter_id())
return a.local.adapter_id() < b.local.adapter_id();
if (a.remote.adapter_id() != b.remote.adapter_id())
return a.remote.adapter_id() < b.remote.adapter_id();
if (a.local.uses_turn() != b.local.uses_turn())
return a.local.uses_turn() < b.local.uses_turn();
if (a.remote.uses_turn() != b.remote.uses_turn())
return a.remote.uses_turn() < b.remote.uses_turn();
return a.connected < b.connected;
}
TransportFeedbackAdapter::TransportFeedbackAdapter() = default;
std::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ProcessCongestionControlFeedback(
const CongestionControlFeedback& feedback, int64_t feedback_receive_time) {
if (feedback.packets().empty()) {
LOG_INFO("Empty congestion control feedback packet received");
return std::nullopt;
}
if (current_offset_ == std::numeric_limits<int64_t>::max() ||
current_offset_ == std::numeric_limits<int64_t>::min()) {
current_offset_ = feedback_receive_time;
}
int64_t feedback_delta = last_feedback_compact_ntp_time_
? (feedback.report_timestamp_compact_ntp() -
*last_feedback_compact_ntp_time_)
: 0;
last_feedback_compact_ntp_time_ = feedback.report_timestamp_compact_ntp();
if (feedback_delta < 0) {
LOG_WARN("Unexpected feedback ntp time delta {}", feedback_delta);
current_offset_ = feedback_receive_time;
} else {
current_offset_ += feedback_delta;
}
int ignored_packets = 0;
int failed_lookups = 0;
bool supports_ecn = true;
std::vector<PacketResult> packet_result_vector;
for (const CongestionControlFeedback::PacketInfo& packet_info :
feedback.packets()) {
std::optional<PacketFeedback> packet_feedback =
RetrievePacketFeedback({packet_info.ssrc, packet_info.sequence_number},
/*received=*/packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::min() &&
packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::max());
if (!packet_feedback) {
++failed_lookups;
continue;
}
if (packet_feedback->network_route != network_route_) {
++ignored_packets;
continue;
}
PacketResult result;
result.sent_packet = packet_feedback->sent;
if (packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::min() &&
packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::max()) {
result.receive_time = current_offset_ - packet_info.arrival_time_offset;
supports_ecn &= packet_info.ecn != EcnMarking::kNotEct;
}
result.ecn = packet_info.ecn;
packet_result_vector.push_back(result);
}
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_packets > 0) {
LOG_INFO("Ignoring {} packets because they were sent on a different route",
ignored_packets);
}
// Feedback is expected to be sorted in send order.
std::sort(packet_result_vector.begin(), packet_result_vector.end(),
[](const PacketResult& lhs, const PacketResult& rhs) {
return lhs.sent_packet.sequence_number <
rhs.sent_packet.sequence_number;
});
return ToTransportFeedback(std::move(packet_result_vector),
feedback_receive_time, supports_ecn);
}
std::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ToTransportFeedback(
std::vector<PacketResult> packet_results, int64_t feedback_receive_time,
bool supports_ecn) {
TransportPacketsFeedback msg;
msg.feedback_time = feedback_receive_time;
if (packet_results.empty()) {
return std::nullopt;
}
msg.packet_feedbacks = std::move(packet_results);
msg.data_in_flight = in_flight_.GetOutstandingData(network_route_);
msg.transport_supports_ecn = supports_ecn;
return msg;
}
void TransportFeedbackAdapter::SetNetworkRoute(
const NetworkRoute& network_route) {
network_route_ = network_route;
}
int64_t TransportFeedbackAdapter::GetOutstandingData() const {
return in_flight_.GetOutstandingData(network_route_);
}
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
const SsrcAndRtpSequencenumber& key, bool received) {
auto it = rtp_to_transport_sequence_number_.find(key);
if (it == rtp_to_transport_sequence_number_.end()) {
return std::nullopt;
}
return RetrievePacketFeedback(it->second, received);
}
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
int64_t transport_seq_num, bool received) {
if (transport_seq_num > last_ack_seq_num_) {
// Starts at history_.begin() if last_ack_seq_num_ < 0, since any
// valid sequence number is >= 0.
for (auto it = history_.upper_bound(last_ack_seq_num_);
it != history_.upper_bound(transport_seq_num); ++it) {
in_flight_.RemoveInFlightPacketBytes(it->second);
}
last_ack_seq_num_ = transport_seq_num;
}
auto it = history_.find(transport_seq_num);
if (it == history_.end()) {
LOG_WARN(
"Failed to lookup send time for packet with {}. Send time history too "
"small?",
transport_seq_num);
return std::nullopt;
}
if (it->second.sent.send_time == std::numeric_limits<int64_t>::max() ||
it->second.sent.send_time == std::numeric_limits<int64_t>::min()) {
// TODO(srte): Fix the tests that makes this happen and make this a
// DCHECK.
LOG_ERROR("Received feedback before packet was indicated as sent");
return std::nullopt;
}
PacketFeedback packet_feedback = it->second;
if (received) {
// Note: Lost packets are not removed from history because they might
// be reported as received by a later feedback.
rtp_to_transport_sequence_number_.erase(
{packet_feedback.ssrc, packet_feedback.rtp_sequence_number});
history_.erase(it);
}
return packet_feedback;
}

View File

@@ -0,0 +1,115 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-01-13
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _TRANSPORT_FEEDBACK_ADAPTER_H_
#define _TRANSPORT_FEEDBACK_ADAPTER_H_
#include <cstddef>
#include <cstdint>
#include <map>
#include <optional>
#include <tuple>
#include <vector>
#include "congestion_control_feedback.h"
#include "network_route.h"
#include "network_types.h"
#include "sequence_number_unwrapper.h"
struct PacketFeedback {
PacketFeedback() = default;
// Time corresponding to when this object was created.
int64_t creation_time = std::numeric_limits<int64_t>::min();
SentPacket sent;
// Time corresponding to when the packet was received. Timestamped with the
// receiver's clock. For unreceived packet,
// std::numeric_limits<int64_t>::max() is used.
int64_t receive_time = std::numeric_limits<int64_t>::max();
// The network route that this packet is associated with.
NetworkRoute network_route;
uint32_t ssrc = 0;
uint16_t rtp_sequence_number = 0;
};
class InFlightBytesTracker {
public:
void AddInFlightPacketBytes(const PacketFeedback& packet);
void RemoveInFlightPacketBytes(const PacketFeedback& packet);
int64_t GetOutstandingData(const NetworkRoute& network_route) const;
private:
struct NetworkRouteComparator {
bool operator()(const NetworkRoute& a, const NetworkRoute& b) const;
};
std::map<NetworkRoute, int64_t, NetworkRouteComparator> in_flight_data_;
};
// TransportFeedbackAdapter converts RTCP feedback packets to RTCP agnostic per
// packet send/receive information.
// It supports rtcp::CongestionControlFeedback according to RFC 8888 and
// rtcp::TransportFeedback according to
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
class TransportFeedbackAdapter {
public:
TransportFeedbackAdapter();
std::optional<TransportPacketsFeedback> ProcessCongestionControlFeedback(
const CongestionControlFeedback& feedback, int64_t feedback_receive_time);
void SetNetworkRoute(const NetworkRoute& network_route);
int64_t GetOutstandingData() const;
private:
enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate };
struct SsrcAndRtpSequencenumber {
uint32_t ssrc;
uint16_t rtp_sequence_number;
bool operator<(const SsrcAndRtpSequencenumber& other) const {
return std::tie(ssrc, rtp_sequence_number) <
std::tie(other.ssrc, other.rtp_sequence_number);
}
};
std::optional<PacketFeedback> RetrievePacketFeedback(
int64_t transport_seq_num, bool received);
std::optional<PacketFeedback> RetrievePacketFeedback(
const SsrcAndRtpSequencenumber& key, bool received);
std::optional<TransportPacketsFeedback> ToTransportFeedback(
std::vector<PacketResult> packet_results, int64_t feedback_receive_time,
bool supports_ecn);
int64_t pending_untracked_size_ = 0;
int64_t last_send_time_ = std::numeric_limits<int64_t>::min();
int64_t last_untracked_send_time_ = std::numeric_limits<int64_t>::min();
RtpSequenceNumberUnwrapper seq_num_unwrapper_;
// Sequence numbers are never negative, using -1 as it always < a real
// sequence number.
int64_t last_ack_seq_num_ = -1;
InFlightBytesTracker in_flight_;
NetworkRoute network_route_;
int64_t current_offset_ = std::numeric_limits<int64_t>::min();
// `last_transport_feedback_base_time` is only used for transport feedback to
// track base time.
int64_t last_transport_feedback_base_time_ =
std::numeric_limits<int64_t>::min();
// Used by RFC 8888 congestion control feedback to track base time.
std::optional<uint32_t> last_feedback_compact_ntp_time_;
// Map SSRC and RTP sequence number to transport sequence number.
std::map<SsrcAndRtpSequencenumber, int64_t /*transport_sequence_number*/>
rtp_to_transport_sequence_number_;
std::map<int64_t, PacketFeedback> history_;
};
#endif