diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn index 5d340655c2..7769526e07 100644 --- a/api/transport/BUILD.gn +++ b/api/transport/BUILD.gn @@ -47,6 +47,7 @@ rtc_library("network_control") { ] 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 57fa657df9..432b1fc17c 100644 --- a/api/transport/network_types.h +++ b/api/transport/network_types.h @@ -16,6 +16,7 @@ #include #include +#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 @@ struct RTC_EXPORT PacketResult { 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 08b17c2c21..81754f9666 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::OnTransportFeedback( } } +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 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 4725b38f62..51f8faee04 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 @@ class RtpTransportControllerSend final 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 600b156c17..9df7fc9e37 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -43,6 +43,7 @@ rtc_library("transport_feedback") { "../../../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_library("transport_feedback") { "../../../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 @@ if (rtc_include_tests) { ":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 @@ if (rtc_include_tests) { "../../../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 @@ if (rtc_include_tests) { "../../../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 553cf3f9c5..2219645cc0 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc @@ -13,17 +13,25 @@ #include #include +#include #include #include #include +#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 @@ void TransportFeedbackAdapter::AddPacket(const RtpPacketToSend& packet_to_send, // 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 TransportFeedbackAdapter::ProcessSentPacket( @@ -170,12 +190,13 @@ TransportFeedbackAdapter::ProcessTransportFeedback( // 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 @@ TransportFeedbackAdapter::ProcessTransportFeedback( current_offset_ += delta; } } - last_timestamp_ = feedback.BaseTime(); + last_transport_feedback_base_time_ = feedback.BaseTime(); std::vector packet_result_vector; packet_result_vector.reserve(feedback.GetPacketStatusCount()); @@ -229,6 +250,77 @@ TransportFeedbackAdapter::ProcessTransportFeedback( feedback_receive_time); } +std::optional +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 packet_result_vector; + for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info : + feedback.packets()) { + std::optional 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 TransportFeedbackAdapter::ToTransportFeedback( std::vector packet_results, @@ -254,22 +346,33 @@ DataSize TransportFeedbackAdapter::GetOutstandingData() const { } std::optional 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 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 @@ std::optional TransportFeedbackAdapter::RetrievePacketFeedback( 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 4976a53e0d..970c0f1623 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 +#include #include +#include +#include #include -#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 @@ class InFlightBytesTracker { std::map 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 @@ class TransportFeedbackAdapter { const PacedPacketInfo& pacing_info, size_t overhead_bytes, Timestamp creation_time); + std::optional ProcessSentPacket( const rtc::SentPacket& sent_packet); @@ -70,6 +82,10 @@ class TransportFeedbackAdapter { const rtcp::TransportFeedback& feedback, Timestamp feedback_receive_time); + std::optional ProcessCongestionControlFeedback( + const rtcp::CongestionControlFeedback& feedback, + Timestamp feedback_receive_time); + void SetNetworkRoute(const rtc::NetworkRoute& network_route); DataSize GetOutstandingData() const; @@ -77,12 +93,22 @@ class TransportFeedbackAdapter { private: enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate }; - std::vector ProcessTransportFeedbackInner( - const rtcp::TransportFeedback& feedback, - Timestamp feedback_receive_time); + struct SsrcAndRtpSequencenumber { + uint32_t ssrc; + uint16_t rtp_sequence_number; - std::optional 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 RetrievePacketFeedback( + int64_t transport_seq_num, + bool received); + std::optional RetrievePacketFeedback( + const SsrcAndRtpSequencenumber& key, + bool received); std::optional ToTransportFeedback( std::vector packet_results, Timestamp feedback_receive_time); @@ -92,17 +118,24 @@ class TransportFeedbackAdapter { Timestamp last_untracked_send_time_ = Timestamp::MinusInfinity(); RtpSequenceNumberUnwrapper seq_num_unwrapper_; - std::map 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 last_feedback_compact_ntp_time_; + + // Map SSRC and RTP sequence number to transport sequence number. + std::map + rtp_to_transport_sequence_number_; + std::map 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 e003506a56..ca7ebc3e2c 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 #include #include +#include #include #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& 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 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 packets; + + for (uint32_t ssrc = 3; ssrc < 3 + number_of_ssrcs; ++ssrc) { + for (int rtp_sequence_number = ssrc * 10; + rtp_sequence_number < static_cast(ssrc * 10 + packets_per_ssrc); + ++rtp_sequence_number) { + packets.push_back({ + .ssrc = ssrc, + .transport_sequence_number = transport_sequence_number++, + .rtp_sequence_number = static_cast(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& truth, const std::vector& input) { ASSERT_EQ(truth.size(), input.size()); size_t len = truth.size(); @@ -55,106 +98,126 @@ void ComparePacketFeedbackVectors(const std::vector& truth, // 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 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 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 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 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 packet_feedback_vector), - (override)); -}; - } // namespace -class TransportFeedbackAdapterTest : public ::testing::Test { +class TransportFeedbackAdapterTest : public ::testing::TestWithParam { public: - Timestamp TimeNow() const { return Timestamp::Millis(1234); } + bool UseRfc8888CongestionControlFeedback() const { return GetParam(); } std::optional CreateAndProcessFeedback( - rtc::ArrayView packets, + rtc::ArrayView 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 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 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 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 adapted_feedback = @@ -162,75 +225,63 @@ TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) { ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks); } -TEST_F(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) { +TEST_P(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) { TransportFeedbackAdapter adapter; - std::vector 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 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 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 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 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 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 sent_packets; - for (const PacketResult& packet : packets) { - if (packet.sent_packet.sequence_number >= kSendSideDropBefore) { + std::vector 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 received_packets; - for (const PacketResult& packet : packets) { - if (packet.sent_packet.sequence_number <= kReceiveSideDropAfter) { + std::vector received_packets; + for (const PacketTemplate& packet : packets) { + if (packet.transport_sequence_number <= kReceiveSideDropAfter) { received_packets.push_back(packet); } } - std::optional adapted_feedback = CreateAndProcessFeedback(received_packets, adapter); - std::vector expected_packets( + std::vector 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 @@ TEST_F(TransportFeedbackAdapterTest, HandlesDroppedPackets) { 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 adapted_feedback = CreateAndProcessFeedback(packets, adapter); @@ -257,59 +309,83 @@ TEST_F(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) { 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 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 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 received_packets = {packets[i]}; + std::vector received_packets = {packets[i]}; - rtcp::TransportFeedback feedback = - BuildRtcpTransportFeedbackPacket(received_packets); - rtc::Buffer raw_packet = feedback.Build(); - std::unique_ptr parsed_feedback = - rtcp::TransportFeedback::ParseFrom(raw_packet.data(), - raw_packet.size()); - ASSERT_THAT(parsed_feedback, NotNull()); - - std::optional res = - adapter.ProcessTransportFeedback(*parsed_feedback, TimeNow()); - ASSERT_TRUE(res.has_value()); - ComparePacketFeedbackVectors(received_packets, res->packet_feedbacks); + std::optional 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 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 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 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 @@ TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) { 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 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 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 adapted_feedback_1 = + CreateAndProcessFeedback(std::vector({packets[0]}), + adapter); + std::optional adapted_feedback_2 = + CreateAndProcessFeedback(std::vector({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 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 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 adapted_feedback_1 = + CreateAndProcessFeedback(rtc::MakeArrayView(&packets[0], 1), adapter); + std::optional 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 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 17b43e5b69..3f678e3455 100644 --- a/rtc_base/network_route.h +++ b/rtc_base/network_route.h @@ -88,6 +88,7 @@ struct NetworkRoute { } bool operator==(const NetworkRoute& other) const; + bool operator!=(const NetworkRoute& other) { return !operator==(other); } }; } // namespace rtc