Implement support for receiving feedback according to RFC 8888
Bug: webrtc:42225697
Change-Id: Ieb270b44da223436d2fd3fa353dc857f378ee88d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/365700
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43305}
diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn
index 5d34065..7769526 100644
--- a/api/transport/BUILD.gn
+++ b/api/transport/BUILD.gn
@@ -47,6 +47,7 @@
]
deps = [
+ ":ecn_marking",
"../../api:field_trials_view",
"../../rtc_base/system:rtc_export",
"../environment",
diff --git a/api/transport/network_types.h b/api/transport/network_types.h
index 57fa657..432b1fc 100644
--- a/api/transport/network_types.h
+++ b/api/transport/network_types.h
@@ -16,6 +16,7 @@
#include <optional>
#include <vector>
+#include "api/transport/ecn_marking.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
@@ -168,6 +169,7 @@
SentPacket sent_packet;
Timestamp receive_time = Timestamp::PlusInfinity();
+ EcnMarking ecn = EcnMarking::kNotEct;
};
struct RTC_EXPORT TransportPacketsFeedback {
diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc
index 08b17c2..81754f9 100644
--- a/call/rtp_transport_controller_send.cc
+++ b/call/rtp_transport_controller_send.cc
@@ -48,7 +48,9 @@
#include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
+#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
@@ -650,6 +652,25 @@
}
}
+void RtpTransportControllerSend::OnCongestionControlFeedback(
+ Timestamp receive_time,
+ const rtcp::CongestionControlFeedback& feedback) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ // TODO: bugs.webrtc.org/42225697 - update feedback demuxer for RFC 8888.
+ // Suggest feedback_demuxer_.OnTransportFeedback use TransportPacketFeedback
+ // instead. See usage in OnTransportFeedback.
+ std::optional<TransportPacketsFeedback> feedback_msg =
+ transport_feedback_adapter_.ProcessCongestionControlFeedback(
+ feedback, receive_time);
+ if (feedback_msg) {
+ if (controller_)
+ PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg));
+
+ // Only update outstanding data if any packet is first time acked.
+ UpdateCongestedState();
+ }
+}
+
void RtpTransportControllerSend::OnRemoteNetworkEstimate(
NetworkStateEstimate estimate) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h
index 4725b38..51f8fae 100644
--- a/call/rtp_transport_controller_send.h
+++ b/call/rtp_transport_controller_send.h
@@ -48,6 +48,7 @@
#include "modules/pacing/task_queue_paced_sender.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/network_route.h"
#include "rtc_base/rate_limiter.h"
@@ -129,6 +130,9 @@
void OnRttUpdate(Timestamp receive_time, TimeDelta rtt) override;
void OnTransportFeedback(Timestamp receive_time,
const rtcp::TransportFeedback& feedback) override;
+ void OnCongestionControlFeedback(
+ Timestamp receive_time,
+ const rtcp::CongestionControlFeedback& feedback) override;
// Implements NetworkStateEstimateObserver interface
void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override;
diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn
index 600b156..9df7fc9 100644
--- a/modules/congestion_controller/rtp/BUILD.gn
+++ b/modules/congestion_controller/rtp/BUILD.gn
@@ -43,6 +43,7 @@
"../../../api:sequence_checker",
"../../../api/transport:network_control",
"../../../api/units:data_size",
+ "../../../api/units:time_delta",
"../../../api/units:timestamp",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
@@ -54,6 +55,7 @@
"../../../rtc_base/system:no_unique_address",
"../../../system_wrappers",
"../../../system_wrappers:field_trial",
+ "../../rtp_rtcp:ntp_time_util",
"../../rtp_rtcp:rtp_rtcp_format",
"//third_party/abseil-cpp/absl/algorithm:container",
]
@@ -71,6 +73,7 @@
":transport_feedback",
"../:congestion_controller",
"../../../api:array_view",
+ "../../../api/transport:ecn_marking",
"../../../api/transport:network_control",
"../../../api/units:data_size",
"../../../api/units:time_delta",
@@ -78,6 +81,7 @@
"../../../logging:mocks",
"../../../rtc_base:buffer",
"../../../rtc_base:checks",
+ "../../../rtc_base:logging",
"../../../rtc_base:safe_conversions",
"../../../rtc_base/network:sent_packet",
"../../../system_wrappers",
@@ -85,6 +89,7 @@
"../../../test:test_support",
"../../pacing",
"../../remote_bitrate_estimator",
+ "../../rtp_rtcp:ntp_time_util",
"../../rtp_rtcp:rtp_rtcp_format",
"//testing/gmock",
]
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc
index 553cf3f..2219645 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc
@@ -13,17 +13,25 @@
#include <stdlib.h>
#include <algorithm>
+#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
+#include "absl/algorithm/container.h"
#include "api/transport/network_types.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/ntp_time_util.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "rtc_base/network/sent_packet.h"
+#include "rtc_base/network_route.h"
namespace webrtc {
@@ -114,9 +122,21 @@
// TODO(sprang): Warn if erasing (too many) old items?
if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_)
in_flight_.RemoveInFlightPacketBytes(history_.begin()->second);
+
+ const PacketFeedback& packet = history_.begin()->second;
+ rtp_to_transport_sequence_number_.erase(
+ {.ssrc = packet.ssrc,
+ .rtp_sequence_number = packet.rtp_sequence_number});
history_.erase(history_.begin());
}
- history_.insert(std::make_pair(feedback.sent.sequence_number, feedback));
+ // Note that it can happen that the same SSRC and sequence number is sent
+ // again. e.g, audio retransmission.
+ rtp_to_transport_sequence_number_.emplace(
+ SsrcAndRtpSequencenumber(
+ {.ssrc = feedback.ssrc,
+ .rtp_sequence_number = feedback.rtp_sequence_number}),
+ feedback.sent.sequence_number);
+ history_.emplace(feedback.sent.sequence_number, feedback);
}
std::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
@@ -170,12 +190,13 @@
// 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_timestamp_.IsInfinite()) {
+ 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_timestamp_)
- .RoundDownTo(TimeDelta::Millis(1));
+ 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_) {
RTC_LOG(LS_WARNING) << "Unexpected feedback timestamp received.";
@@ -184,7 +205,7 @@
current_offset_ += delta;
}
}
- last_timestamp_ = feedback.BaseTime();
+ last_transport_feedback_base_time_ = feedback.BaseTime();
std::vector<PacketResult> packet_result_vector;
packet_result_vector.reserve(feedback.GetPacketStatusCount());
@@ -230,6 +251,77 @@
}
std::optional<TransportPacketsFeedback>
+TransportFeedbackAdapter::ProcessCongestionControlFeedback(
+ const rtcp::CongestionControlFeedback& feedback,
+ Timestamp feedback_receive_time) {
+ if (feedback.packets().empty()) {
+ RTC_LOG(LS_INFO) << "Empty congestion control feedback packet received.";
+ return std::nullopt;
+ }
+ if (current_offset_.IsInfinite()) {
+ current_offset_ = feedback_receive_time;
+ }
+ TimeDelta feedback_delta = last_feedback_compact_ntp_time_
+ ? CompactNtpIntervalToTimeDelta(
+ feedback.report_timestamp_compact_ntp() -
+ *last_feedback_compact_ntp_time_)
+ : TimeDelta::Zero();
+ last_feedback_compact_ntp_time_ = feedback.report_timestamp_compact_ntp();
+ if (feedback_delta < TimeDelta::Zero()) {
+ RTC_LOG(LS_WARNING) << "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;
+ std::vector<PacketResult> packet_result_vector;
+ for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info :
+ feedback.packets()) {
+ std::optional<PacketFeedback> packet_feedback = RetrievePacketFeedback(
+ {.ssrc = packet_info.ssrc,
+ .rtp_sequence_number = packet_info.sequence_number},
+ /*received=*/packet_info.arrival_time_offset.IsFinite());
+ 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.IsFinite()) {
+ result.receive_time = current_offset_ - packet_info.arrival_time_offset;
+ }
+ result.ecn = packet_info.ecn;
+ packet_result_vector.push_back(result);
+ }
+
+ if (failed_lookups > 0) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to lookup send time for " << failed_lookups << " packet"
+ << (failed_lookups > 1 ? "s" : "")
+ << ". Packets reordered or send time history too small?";
+ }
+ if (ignored_packets > 0) {
+ RTC_LOG(LS_INFO) << "Ignoring " << ignored_packets
+ << " packets because they were sent on a different route.";
+ }
+
+ // Feedback is expected to be sorted in send order.
+ absl::c_sort(packet_result_vector, [](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);
+}
+
+std::optional<TransportPacketsFeedback>
TransportFeedbackAdapter::ToTransportFeedback(
std::vector<PacketResult> packet_results,
Timestamp feedback_receive_time) {
@@ -254,22 +346,33 @@
}
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
- int64_t seq_num,
+ const SsrcAndRtpSequencenumber& key,
bool received) {
- if (seq_num > last_ack_seq_num_) {
+ 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(seq_num); ++it) {
+ it != history_.upper_bound(transport_seq_num); ++it) {
in_flight_.RemoveInFlightPacketBytes(it->second);
}
- last_ack_seq_num_ = seq_num;
+ last_ack_seq_num_ = transport_seq_num;
}
- auto it = history_.find(seq_num);
+ auto it = history_.find(transport_seq_num);
if (it == history_.end()) {
RTC_LOG(LS_WARNING) << "Failed to lookup send time for packet with "
- << seq_num << ". Send time history too small?";
+ << transport_seq_num
+ << ". Send time history too small?";
return std::nullopt;
}
@@ -285,6 +388,9 @@
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(
+ {.ssrc = packet_feedback.ssrc,
+ .rtp_sequence_number = packet_feedback.rtp_sequence_number});
history_.erase(it);
}
return packet_feedback;
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.h b/modules/congestion_controller/rtp/transport_feedback_adapter.h
index 4976a53..970c0f1 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter.h
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter.h
@@ -11,13 +11,19 @@
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
#define MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
+#include <cstddef>
+#include <cstdint>
#include <map>
+#include <optional>
+#include <tuple>
#include <vector>
-#include "api/sequence_checker.h"
#include "api/transport/network_types.h"
+#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
+#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/network_route.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
@@ -55,6 +61,11 @@
std::map<rtc::NetworkRoute, DataSize, 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();
@@ -63,6 +74,7 @@
const PacedPacketInfo& pacing_info,
size_t overhead_bytes,
Timestamp creation_time);
+
std::optional<SentPacket> ProcessSentPacket(
const rtc::SentPacket& sent_packet);
@@ -70,6 +82,10 @@
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time);
+ std::optional<TransportPacketsFeedback> ProcessCongestionControlFeedback(
+ const rtcp::CongestionControlFeedback& feedback,
+ Timestamp feedback_receive_time);
+
void SetNetworkRoute(const rtc::NetworkRoute& network_route);
DataSize GetOutstandingData() const;
@@ -77,12 +93,22 @@
private:
enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate };
- std::vector<PacketResult> ProcessTransportFeedbackInner(
- const rtcp::TransportFeedback& feedback,
- Timestamp feedback_receive_time);
+ struct SsrcAndRtpSequencenumber {
+ uint32_t ssrc;
+ uint16_t rtp_sequence_number;
- std::optional<PacketFeedback> RetrievePacketFeedback(int64_t seq_num,
- bool received);
+ 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,
Timestamp feedback_receive_time);
@@ -92,17 +118,24 @@
Timestamp last_untracked_send_time_ = Timestamp::MinusInfinity();
RtpSequenceNumberUnwrapper seq_num_unwrapper_;
- std::map<int64_t, PacketFeedback> history_;
-
// Sequence numbers are never negative, using -1 as it always < a real
// sequence number.
int64_t last_ack_seq_num_ = -1;
InFlightBytesTracker in_flight_;
+ rtc::NetworkRoute network_route_;
Timestamp current_offset_ = Timestamp::MinusInfinity();
- Timestamp last_timestamp_ = Timestamp::MinusInfinity();
- rtc::NetworkRoute network_route_;
+ // `last_transport_feedback_base_time` is only used for transport feedback to
+ // track base time.
+ Timestamp last_transport_feedback_base_time_ = Timestamp::MinusInfinity();
+ // 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_;
};
} // namespace webrtc
diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
index e003506..ca7ebc3 100644
--- a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
+++ b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc
@@ -14,37 +14,80 @@
#include <cstdint>
#include <memory>
#include <optional>
+#include <utility>
#include <vector>
#include "api/array_view.h"
+#include "api/transport/ecn_marking.h"
#include "api/transport/network_types.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/ntp_time_util.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "rtc_base/buffer.h"
-#include "rtc_base/checks.h"
#include "rtc_base/network/sent_packet.h"
+#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
-
namespace {
+using ::testing::Bool;
using ::testing::NotNull;
using ::testing::SizeIs;
+using ::testing::TestParamInfo;
-constexpr uint32_t kSsrc = 8492;
const PacedPacketInfo kPacingInfo0(0, 5, 2000);
-const PacedPacketInfo kPacingInfo1(1, 8, 4000);
-const PacedPacketInfo kPacingInfo2(2, 14, 7000);
-const PacedPacketInfo kPacingInfo3(3, 20, 10000);
-const PacedPacketInfo kPacingInfo4(4, 22, 10000);
-void ComparePacketFeedbackVectors(const std::vector<PacketResult>& truth,
+struct PacketTemplate {
+ uint32_t ssrc = 1;
+ int64_t transport_sequence_number = 0;
+ uint16_t rtp_sequence_number = 2;
+ RtpPacketMediaType media_type = RtpPacketMediaType::kVideo;
+ DataSize packet_size = DataSize::Bytes(100);
+
+ EcnMarking ecn = EcnMarking::kNotEct;
+ Timestamp send_timestamp = Timestamp::Millis(0);
+ PacedPacketInfo pacing_info;
+ Timestamp receive_timestamp = Timestamp::MinusInfinity();
+
+ bool is_audio = false;
+};
+
+std::vector<PacketTemplate> CreatePacketTemplates(
+ uint32_t number_of_ssrcs,
+ uint32_t packets_per_ssrc,
+ int64_t first_transport_sequence_number = 99) {
+ int64_t transport_sequence_number = first_transport_sequence_number;
+ Timestamp send_time = Timestamp::Millis(200);
+ Timestamp receive_time = Timestamp::Millis(100);
+ std::vector<PacketTemplate> packets;
+
+ for (uint32_t ssrc = 3; ssrc < 3 + number_of_ssrcs; ++ssrc) {
+ for (int rtp_sequence_number = ssrc * 10;
+ rtp_sequence_number < static_cast<int>(ssrc * 10 + packets_per_ssrc);
+ ++rtp_sequence_number) {
+ packets.push_back({
+ .ssrc = ssrc,
+ .transport_sequence_number = transport_sequence_number++,
+ .rtp_sequence_number = static_cast<uint16_t>(rtp_sequence_number),
+ .send_timestamp = send_time,
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = receive_time,
+ });
+ send_time += TimeDelta::Millis(10);
+ receive_time += TimeDelta::Millis(13);
+ }
+ }
+ return packets;
+}
+
+void ComparePacketFeedbackVectors(const std::vector<PacketTemplate>& truth,
const std::vector<PacketResult>& input) {
ASSERT_EQ(truth.size(), input.size());
size_t len = truth.size();
@@ -55,106 +98,126 @@
// base adjustment performed by the TransportFeedbackAdapter at the first
// packet, the truth[x].arrival_time and input[x].arrival_time may not be
// equal. However, the difference must be the same for all x.
- TimeDelta arrival_time_delta = truth[0].receive_time - input[0].receive_time;
+ TimeDelta arrival_time_delta =
+ truth[0].receive_timestamp - input[0].receive_time;
for (size_t i = 0; i < len; ++i) {
- RTC_CHECK(truth[i].IsReceived());
+ EXPECT_EQ(truth[i].receive_timestamp.IsFinite(), input[i].IsReceived());
if (input[i].IsReceived()) {
- EXPECT_EQ(truth[i].receive_time - input[i].receive_time,
+ EXPECT_EQ(truth[i].receive_timestamp - input[i].receive_time,
arrival_time_delta);
}
- EXPECT_EQ(truth[i].sent_packet.send_time, input[i].sent_packet.send_time);
- EXPECT_EQ(truth[i].sent_packet.sequence_number,
+ EXPECT_EQ(truth[i].send_timestamp, input[i].sent_packet.send_time);
+ EXPECT_EQ(truth[i].transport_sequence_number,
input[i].sent_packet.sequence_number);
- EXPECT_EQ(truth[i].sent_packet.size, input[i].sent_packet.size);
- EXPECT_EQ(truth[i].sent_packet.pacing_info,
- input[i].sent_packet.pacing_info);
- EXPECT_EQ(truth[i].sent_packet.audio, input[i].sent_packet.audio);
+ EXPECT_EQ(truth[i].packet_size, input[i].sent_packet.size);
+ EXPECT_EQ(truth[i].pacing_info, input[i].sent_packet.pacing_info);
+ EXPECT_EQ(truth[i].is_audio, input[i].sent_packet.audio);
}
}
-rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket(
- rtc::ArrayView<const PacketResult> packets) {
- rtcp::TransportFeedback feedback;
- feedback.SetBase(packets[0].sent_packet.sequence_number,
- packets[0].receive_time);
+RtpPacketToSend CreatePacketToSend(PacketTemplate packet) {
+ RtpPacketToSend send_packet(nullptr);
+ send_packet.SetSsrc(packet.ssrc);
+ send_packet.SetPayloadSize(packet.packet_size.bytes() -
+ send_packet.headers_size());
+ send_packet.SetSequenceNumber(packet.rtp_sequence_number);
+ send_packet.set_transport_sequence_number(packet.transport_sequence_number);
+ send_packet.set_packet_type(packet.is_audio ? RtpPacketMediaType::kAudio
+ : RtpPacketMediaType::kVideo);
- for (const PacketResult& packet : packets) {
- if (packet.receive_time.IsFinite()) {
- EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
- packet.receive_time));
+ return send_packet;
+}
+
+rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket(
+ rtc::ArrayView<const PacketTemplate> packets) {
+ rtcp::TransportFeedback feedback;
+ feedback.SetBase(packets[0].transport_sequence_number,
+ packets[0].receive_timestamp);
+
+ for (const PacketTemplate& packet : packets) {
+ if (packet.receive_timestamp.IsFinite()) {
+ EXPECT_TRUE(feedback.AddReceivedPacket(packet.transport_sequence_number,
+ packet.receive_timestamp));
}
}
return feedback;
}
-PacketResult CreatePacket(int64_t receive_time_ms,
- int64_t send_time_ms,
- int64_t sequence_number,
- size_t payload_size,
- const PacedPacketInfo& pacing_info) {
- PacketResult res;
- res.receive_time = Timestamp::Millis(receive_time_ms);
- res.sent_packet.send_time = Timestamp::Millis(send_time_ms);
- res.sent_packet.sequence_number = sequence_number;
- res.sent_packet.size = DataSize::Bytes(payload_size);
- res.sent_packet.pacing_info = pacing_info;
- return res;
+rtcp::CongestionControlFeedback BuildRtcpCongestionControlFeedbackPacket(
+ rtc::ArrayView<const PacketTemplate> packets) {
+ // Assume the feedback was sent when the last packet was received.
+ Timestamp feedback_sent_time = Timestamp::MinusInfinity();
+ for (auto it = packets.crbegin(); it != packets.crend(); ++it) {
+ if (it->receive_timestamp.IsFinite()) {
+ feedback_sent_time = it->receive_timestamp;
+ break;
+ }
+ }
+
+ std::vector<rtcp::CongestionControlFeedback::PacketInfo> packet_infos;
+ for (const PacketTemplate& packet : packets) {
+ rtcp::CongestionControlFeedback::PacketInfo packet_info = {
+ .ssrc = packet.ssrc,
+ .sequence_number = packet.rtp_sequence_number,
+ .ecn = packet.ecn};
+ if (packet.receive_timestamp.IsFinite()) {
+ packet_info.arrival_time_offset =
+ feedback_sent_time - packet.receive_timestamp;
+ }
+ packet_infos.push_back(packet_info);
+ }
+
+ SimulatedClock clock(feedback_sent_time);
+ uint32_t compact_ntp =
+ CompactNtp(clock.ConvertTimestampToNtpTime(feedback_sent_time));
+ return rtcp::CongestionControlFeedback(std::move(packet_infos), compact_ntp);
}
-RtpPacketToSend CreatePacketToSend(const PacketResult& packet,
- uint32_t ssrc = kSsrc,
- uint16_t rtp_sequence_number = 0) {
- RtpPacketToSend send_packet(nullptr);
- send_packet.SetSsrc(ssrc);
- send_packet.SetPayloadSize(packet.sent_packet.size.bytes() -
- send_packet.headers_size());
- send_packet.SetSequenceNumber(rtp_sequence_number);
- send_packet.set_transport_sequence_number(packet.sent_packet.sequence_number);
- send_packet.set_packet_type(packet.sent_packet.audio
- ? RtpPacketMediaType::kAudio
- : RtpPacketMediaType::kVideo);
-
- return send_packet;
+Timestamp TimeNow() {
+ return Timestamp::Millis(1234);
}
-class MockStreamFeedbackObserver : public webrtc::StreamFeedbackObserver {
- public:
- MOCK_METHOD(void,
- OnPacketFeedbackVector,
- (std::vector<StreamPacketInfo> packet_feedback_vector),
- (override));
-};
-
} // namespace
-class TransportFeedbackAdapterTest : public ::testing::Test {
+class TransportFeedbackAdapterTest : public ::testing::TestWithParam<bool> {
public:
- Timestamp TimeNow() const { return Timestamp::Millis(1234); }
+ bool UseRfc8888CongestionControlFeedback() const { return GetParam(); }
std::optional<TransportPacketsFeedback> CreateAndProcessFeedback(
- rtc::ArrayView<const PacketResult> packets,
+ rtc::ArrayView<const PacketTemplate> packets,
TransportFeedbackAdapter& adapter) {
- rtcp::TransportFeedback rtcp_feedback =
- BuildRtcpTransportFeedbackPacket(packets);
- return adapter.ProcessTransportFeedback(rtcp_feedback, TimeNow());
+ if (UseRfc8888CongestionControlFeedback()) {
+ rtcp::CongestionControlFeedback rtcp_feedback =
+ BuildRtcpCongestionControlFeedbackPacket(packets);
+ return adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
+ } else {
+ rtcp::TransportFeedback rtcp_feedback =
+ BuildRtcpTransportFeedbackPacket(packets);
+ return adapter.ProcessTransportFeedback(rtcp_feedback, TimeNow());
+ }
}
};
-TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
- TransportFeedbackAdapter adapter;
- std::vector<PacketResult> packets;
- packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(120, 220, 2, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(130, 230, 3, 1500, kPacingInfo1));
- packets.push_back(CreatePacket(140, 240, 4, 1500, kPacingInfo1));
+INSTANTIATE_TEST_SUITE_P(FeedbackFormats,
+ TransportFeedbackAdapterTest,
+ Bool(),
+ [](TestParamInfo<bool> param) {
+ if (param.param)
+ return "CongestionControlFeedback";
+ else
+ return "TransportFeedback";
+ });
- for (const PacketResult& packet : packets) {
- adapter.AddPacket(CreatePacketToSend(packet),
- packet.sent_packet.pacing_info,
+TEST_P(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
+ TransportFeedbackAdapter adapter;
+ std::vector<PacketTemplate> packets =
+ CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/3);
+
+ for (const PacketTemplate& packet : packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(
- packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
}
std::optional<TransportPacketsFeedback> adapted_feedback =
@@ -162,75 +225,63 @@
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
}
-TEST_F(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
+TEST_P(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
TransportFeedbackAdapter adapter;
- std::vector<PacketResult> sent_packets = {
- CreatePacket(100, 220, 0, 1500, kPacingInfo0),
- CreatePacket(110, 210, 1, 1500, kPacingInfo0),
- CreatePacket(120, 220, 2, 1500, kPacingInfo0),
- CreatePacket(130, 230, 3, 1500, kPacingInfo0),
- CreatePacket(140, 240, 4, 1500, kPacingInfo0),
- CreatePacket(150, 250, 5, 1500, kPacingInfo0),
- CreatePacket(160, 260, 6, 1500, kPacingInfo0)};
- for (const PacketResult& packet : sent_packets) {
- adapter.AddPacket(CreatePacketToSend(packet),
- packet.sent_packet.pacing_info,
+ std::vector<PacketTemplate> sent_packets =
+ CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/3);
+
+ for (const PacketTemplate& packet : sent_packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(
- packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
}
- // Note: Important to include the last packet, as only unreceived packets in
- // between received packets can be inferred.
- std::vector<PacketResult> received_packets = {
- sent_packets[0], sent_packets[2], sent_packets[6]};
-
+ // Note: Important to include the last packet per SSRC, as only unreceived
+ // packets in between received packets can be inferred.
+ sent_packets[1].receive_timestamp = Timestamp::PlusInfinity();
+ sent_packets[4].receive_timestamp = Timestamp::PlusInfinity();
std::optional<TransportPacketsFeedback> adapted_feedback =
- CreateAndProcessFeedback(received_packets, adapter);
+ CreateAndProcessFeedback(sent_packets, adapter);
ComparePacketFeedbackVectors(sent_packets,
adapted_feedback->packet_feedbacks);
}
-TEST_F(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
+TEST_P(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
TransportFeedbackAdapter adapter;
- std::vector<PacketResult> packets;
- packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo1));
- packets.push_back(CreatePacket(120, 220, 2, 1500, kPacingInfo2));
- packets.push_back(CreatePacket(130, 230, 3, 1500, kPacingInfo3));
- packets.push_back(CreatePacket(140, 240, 4, 1500, kPacingInfo4));
+ std::vector<PacketTemplate> packets =
+ CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/2,
+ /*first_transport_sequence_number=*/0);
const uint16_t kSendSideDropBefore = 1;
const uint16_t kReceiveSideDropAfter = 3;
- std::vector<PacketResult> sent_packets;
- for (const PacketResult& packet : packets) {
- if (packet.sent_packet.sequence_number >= kSendSideDropBefore) {
+ std::vector<PacketTemplate> sent_packets;
+ for (const PacketTemplate& packet : packets) {
+ if (packet.transport_sequence_number >= kSendSideDropBefore) {
sent_packets.push_back(packet);
}
}
- for (const PacketResult& packet : sent_packets) {
- adapter.AddPacket(CreatePacketToSend(packet),
- packet.sent_packet.pacing_info,
+ for (const PacketTemplate& packet : sent_packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(
- packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
}
- std::vector<PacketResult> received_packets;
- for (const PacketResult& packet : packets) {
- if (packet.sent_packet.sequence_number <= kReceiveSideDropAfter) {
+ std::vector<PacketTemplate> received_packets;
+ for (const PacketTemplate& packet : packets) {
+ if (packet.transport_sequence_number <= kReceiveSideDropAfter) {
received_packets.push_back(packet);
}
}
-
std::optional<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(received_packets, adapter);
- std::vector<PacketResult> expected_packets(
+ std::vector<PacketTemplate> expected_packets(
packets.begin() + kSendSideDropBefore,
packets.begin() + kReceiveSideDropAfter + 1);
// Packets that have timed out on the send-side have lost the
@@ -240,16 +291,17 @@
adapted_feedback->packet_feedbacks);
}
-TEST_F(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
+TEST_P(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
TransportFeedbackAdapter adapter;
- PacketResult packets[] = {CreatePacket(100, 200, 0, 1500, kPacingInfo0)};
- PacketResult& packet = packets[0];
- packet.sent_packet.audio = true;
- adapter.AddPacket(CreatePacketToSend(packet), packet.sent_packet.pacing_info,
+ PacketTemplate packets[] = {
+ {.receive_timestamp = TimeNow(), .is_audio = true}};
+ PacketTemplate& packet = packets[0];
+
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(packet.sent_packet.sequence_number,
- packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
std::optional<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(packets, adapter);
@@ -257,59 +309,83 @@
EXPECT_TRUE(adapted_feedback->packet_feedbacks[0].sent_packet.audio);
}
-TEST_F(TransportFeedbackAdapterTest, SendTimeWrapsBothWays) {
+TEST_P(TransportFeedbackAdapterTest, ReceiveTimeWrapsBothWays) {
TransportFeedbackAdapter adapter;
TimeDelta kHighArrivalTime =
rtcp::TransportFeedback::kDeltaTick * (1 << 8) * ((1 << 23) - 1);
- std::vector<PacketResult> packets;
- packets.push_back(CreatePacket(kHighArrivalTime.ms() + 64, 210, 0, 1500,
- PacedPacketInfo()));
- packets.push_back(CreatePacket(kHighArrivalTime.ms() - 64, 210, 1, 1500,
- PacedPacketInfo()));
- packets.push_back(
- CreatePacket(kHighArrivalTime.ms(), 220, 2, 1500, PacedPacketInfo()));
- for (const PacketResult& packet : packets) {
- adapter.AddPacket(CreatePacketToSend(packet),
- packet.sent_packet.pacing_info,
+ std::vector<PacketTemplate> packets = {
+ {.transport_sequence_number = 0,
+ .rtp_sequence_number = 102,
+ .receive_timestamp =
+ Timestamp::Zero() + kHighArrivalTime + TimeDelta::Millis(64)},
+ {.transport_sequence_number = 1,
+ .rtp_sequence_number = 103,
+ .receive_timestamp =
+ Timestamp::Zero() + kHighArrivalTime - TimeDelta::Millis(64)},
+ {.transport_sequence_number = 2,
+ .rtp_sequence_number = 104,
+ .receive_timestamp = Timestamp::Zero() + kHighArrivalTime}};
+
+ for (const PacketTemplate& packet : packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(
- packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
}
for (size_t i = 0; i < packets.size(); ++i) {
- std::vector<PacketResult> received_packets = {packets[i]};
+ std::vector<PacketTemplate> received_packets = {packets[i]};
- rtcp::TransportFeedback feedback =
- BuildRtcpTransportFeedbackPacket(received_packets);
- rtc::Buffer raw_packet = feedback.Build();
- std::unique_ptr<rtcp::TransportFeedback> parsed_feedback =
- rtcp::TransportFeedback::ParseFrom(raw_packet.data(),
- raw_packet.size());
- ASSERT_THAT(parsed_feedback, NotNull());
-
- std::optional<TransportPacketsFeedback> res =
- adapter.ProcessTransportFeedback(*parsed_feedback, TimeNow());
- ASSERT_TRUE(res.has_value());
- ComparePacketFeedbackVectors(received_packets, res->packet_feedbacks);
+ std::optional<TransportPacketsFeedback> result;
+ if (UseRfc8888CongestionControlFeedback()) {
+ rtcp::CongestionControlFeedback feedback =
+ BuildRtcpCongestionControlFeedbackPacket(received_packets);
+ rtc::Buffer raw_packet = feedback.Build();
+ rtcp::CommonHeader header;
+ ASSERT_TRUE(header.Parse(raw_packet.data(), raw_packet.size()));
+ rtcp::CongestionControlFeedback parsed_feedback;
+ ASSERT_TRUE(parsed_feedback.Parse(header));
+ result =
+ adapter.ProcessCongestionControlFeedback(parsed_feedback, TimeNow());
+ } else {
+ rtcp::TransportFeedback feedback =
+ BuildRtcpTransportFeedbackPacket(received_packets);
+ rtc::Buffer raw_packet = feedback.Build();
+ std::unique_ptr<rtcp::TransportFeedback> parsed_feedback =
+ rtcp::TransportFeedback::ParseFrom(raw_packet.data(),
+ raw_packet.size());
+ ASSERT_THAT(parsed_feedback, NotNull());
+ result = adapter.ProcessTransportFeedback(*parsed_feedback, TimeNow());
+ }
+ ASSERT_TRUE(result.has_value());
+ ComparePacketFeedbackVectors(received_packets, result->packet_feedbacks);
}
}
-TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
+TEST_P(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
TransportFeedbackAdapter adapter;
- std::vector<PacketResult> packets;
- packets.push_back(CreatePacket(120, 200, 0, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
- packets.push_back(CreatePacket(100, 220, 2, 1500, kPacingInfo0));
+ std::vector<PacketTemplate> packets = {
+ {.transport_sequence_number = 0,
+ .rtp_sequence_number = 101,
+ .send_timestamp = Timestamp::Millis(200),
+ .receive_timestamp = Timestamp::Millis(120)},
+ {.transport_sequence_number = 1,
+ .rtp_sequence_number = 102,
+ .send_timestamp = Timestamp::Millis(210),
+ .receive_timestamp = Timestamp::Millis(110)},
+ {.transport_sequence_number = 2,
+ .rtp_sequence_number = 103,
+ .send_timestamp = Timestamp::Millis(220),
+ .receive_timestamp = Timestamp::Millis(100)}};
- for (const PacketResult& packet : packets) {
- adapter.AddPacket(CreatePacketToSend(packet),
- packet.sent_packet.pacing_info,
+ for (const PacketTemplate& packet : packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
- adapter.ProcessSentPacket(rtc::SentPacket(
- packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
}
// Adapter keeps the packets ordered by sequence number (which is itself
@@ -320,26 +396,163 @@
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
}
-TEST_F(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
+TEST_P(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
TransportFeedbackAdapter adapter;
- PacketResult packet = CreatePacket(100, 200, 0, 1500, kPacingInfo0);
- RtpPacketToSend packet_to_send =
- CreatePacketToSend(packet, kSsrc, /*rtp_sequence_number=*/0);
+ PacketTemplate packet = {};
// Add a packet and then mark it as sent.
- adapter.AddPacket(packet_to_send, packet.sent_packet.pacing_info, 0u,
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, 0u,
TimeNow());
std::optional<SentPacket> sent_packet = adapter.ProcessSentPacket(
- rtc::SentPacket(packet.sent_packet.sequence_number,
- packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
+ rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms(), rtc::PacketInfo()));
EXPECT_TRUE(sent_packet.has_value());
// Call ProcessSentPacket() again with the same sequence number. This packet
// has already been marked as sent and the call should be ignored.
std::optional<SentPacket> duplicate_packet = adapter.ProcessSentPacket(
- rtc::SentPacket(packet.sent_packet.sequence_number,
- packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
+ rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms(), rtc::PacketInfo()));
EXPECT_FALSE(duplicate_packet.has_value());
}
+TEST_P(TransportFeedbackAdapterTest,
+ SendReceiveTimeDiffTimeContinuouseBetweenFeedback) {
+ TransportFeedbackAdapter adapter;
+
+ PacketTemplate packets[] = {{.transport_sequence_number = 1,
+ .rtp_sequence_number = 101,
+ .send_timestamp = Timestamp::Millis(100),
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = Timestamp::Millis(200)},
+ {.transport_sequence_number = 2,
+ .rtp_sequence_number = 102,
+ .send_timestamp = Timestamp::Millis(110),
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = Timestamp::Millis(210)}};
+
+ for (const PacketTemplate& packet : packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
+ /*overhead=*/0u, TimeNow());
+
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
+ }
+
+ std::optional<TransportPacketsFeedback> adapted_feedback_1 =
+ CreateAndProcessFeedback(std::vector<PacketTemplate>({packets[0]}),
+ adapter);
+ std::optional<TransportPacketsFeedback> adapted_feedback_2 =
+ CreateAndProcessFeedback(std::vector<PacketTemplate>({packets[1]}),
+ adapter);
+
+ ASSERT_EQ(adapted_feedback_1->packet_feedbacks.size(),
+ adapted_feedback_2->packet_feedbacks.size());
+ ASSERT_THAT(adapted_feedback_1->packet_feedbacks, testing::SizeIs(1));
+ EXPECT_EQ((adapted_feedback_1->packet_feedbacks[0].receive_time -
+ adapted_feedback_1->packet_feedbacks[0].sent_packet.send_time)
+ .RoundTo(TimeDelta::Millis(1)),
+ (adapted_feedback_2->packet_feedbacks[0].receive_time -
+ adapted_feedback_2->packet_feedbacks[0].sent_packet.send_time)
+ .RoundTo(TimeDelta::Millis(1)));
+}
+
+TEST_P(TransportFeedbackAdapterTest, ProcessSentPacketIncreaseOutstandingData) {
+ TransportFeedbackAdapter adapter;
+
+ PacketTemplate packet_1 = {.transport_sequence_number = 1,
+ .packet_size = DataSize::Bytes(200)};
+ PacketTemplate packet_2 = {.transport_sequence_number = 2,
+ .packet_size = DataSize::Bytes(300)};
+ adapter.AddPacket(CreatePacketToSend(packet_1), packet_1.pacing_info,
+ /*overhead=*/0u, TimeNow());
+ std::optional<SentPacket> sent_packet_1 =
+ adapter.ProcessSentPacket(rtc::SentPacket(
+ packet_1.transport_sequence_number, packet_1.send_timestamp.ms()));
+
+ ASSERT_TRUE(sent_packet_1.has_value());
+ EXPECT_EQ(sent_packet_1->sequence_number, packet_1.transport_sequence_number);
+ // Only one packet in flight.
+ EXPECT_EQ(sent_packet_1->data_in_flight, packet_1.packet_size);
+ EXPECT_EQ(adapter.GetOutstandingData(), packet_1.packet_size);
+
+ adapter.AddPacket(CreatePacketToSend(packet_2), packet_2.pacing_info,
+ /*overhead=*/0u, TimeNow());
+ std::optional<SentPacket> sent_packet_2 =
+ adapter.ProcessSentPacket(rtc::SentPacket(
+ packet_2.transport_sequence_number, packet_2.send_timestamp.ms()));
+
+ ASSERT_TRUE(sent_packet_2.has_value());
+ // Two packets in flight.
+ EXPECT_EQ(sent_packet_2->data_in_flight,
+ packet_1.packet_size + packet_2.packet_size);
+
+ EXPECT_EQ(adapter.GetOutstandingData(),
+ packet_1.packet_size + packet_2.packet_size);
+}
+
+TEST_P(TransportFeedbackAdapterTest, TransportPacketFeedbackHasDataInFlight) {
+ TransportFeedbackAdapter adapter;
+
+ const PacketTemplate packets[] = {
+ {
+ .transport_sequence_number = 1,
+ .rtp_sequence_number = 101,
+ .packet_size = DataSize::Bytes(200),
+ .send_timestamp = Timestamp::Millis(100),
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = Timestamp::Millis(200),
+ },
+ {
+ .transport_sequence_number = 2,
+ .rtp_sequence_number = 102,
+ .packet_size = DataSize::Bytes(300),
+ .send_timestamp = Timestamp::Millis(110),
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = Timestamp::Millis(210),
+ }};
+
+ for (const PacketTemplate& packet : packets) {
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
+ /*overhead=*/0u, TimeNow());
+
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
+ }
+
+ std::optional<TransportPacketsFeedback> adapted_feedback_1 =
+ CreateAndProcessFeedback(rtc::MakeArrayView(&packets[0], 1), adapter);
+ std::optional<TransportPacketsFeedback> adapted_feedback_2 =
+ CreateAndProcessFeedback(rtc::MakeArrayView(&packets[1], 1), adapter);
+ EXPECT_EQ(adapted_feedback_1->data_in_flight, packets[1].packet_size);
+ EXPECT_EQ(adapted_feedback_2->data_in_flight, DataSize::Zero());
+}
+
+TEST(TransportFeedbackAdapterCongestionFeedbackTest,
+ CongestionControlFeedbackResultHasEcn) {
+ TransportFeedbackAdapter adapter;
+
+ PacketTemplate packet = {
+ .transport_sequence_number = 1,
+ .rtp_sequence_number = 101,
+ .packet_size = DataSize::Bytes(200),
+ .send_timestamp = Timestamp::Millis(100),
+ .pacing_info = kPacingInfo0,
+ .receive_timestamp = Timestamp::Millis(200),
+ };
+ adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
+ /*overhead=*/0u, TimeNow());
+ adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
+ packet.send_timestamp.ms()));
+
+ packet.ecn = EcnMarking::kCe;
+ rtcp::CongestionControlFeedback rtcp_feedback =
+ BuildRtcpCongestionControlFeedbackPacket(rtc::MakeArrayView(&packet, 1));
+ std::optional<TransportPacketsFeedback> adapted_feedback =
+ adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
+
+ ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(1));
+ ASSERT_THAT(adapted_feedback->packet_feedbacks[0].ecn, EcnMarking::kCe);
+}
+
} // namespace webrtc
diff --git a/rtc_base/network_route.h b/rtc_base/network_route.h
index 17b43e5..3f678e3 100644
--- a/rtc_base/network_route.h
+++ b/rtc_base/network_route.h
@@ -88,6 +88,7 @@
}
bool operator==(const NetworkRoute& other) const;
+ bool operator!=(const NetworkRoute& other) { return !operator==(other); }
};
} // namespace rtc