blob: 0b191f68321283706a3388456e8412a1cd9ccc7d [file] [log] [blame]
/*
* 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 "modules/congestion_controller/rtp/transport_feedback_adapter.h"
#include <cstddef>
#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/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;
const PacedPacketInfo kPacingInfo0(0, 5, 2000);
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();
// truth contains the input data for the test, and input is what will be
// sent to the bandwidth estimator. truth.arrival_tims_ms is used to
// populate the transport feedback messages. As these times may be changed
// (because of resolution limits in the packets, and because of the time
// 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_timestamp - input[0].receive_time;
for (size_t i = 0; i < len; ++i) {
EXPECT_EQ(truth[i].receive_timestamp.IsFinite(), input[i].IsReceived());
if (input[i].IsReceived()) {
EXPECT_EQ(truth[i].receive_timestamp - input[i].receive_time,
arrival_time_delta);
}
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].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);
}
}
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);
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;
}
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);
}
Timestamp TimeNow() {
return Timestamp::Millis(1234);
}
} // namespace
class TransportFeedbackAdapterTest : public ::testing::TestWithParam<bool> {
public:
bool UseRfc8888CongestionControlFeedback() const { return GetParam(); }
std::optional<TransportPacketsFeedback> CreateAndProcessFeedback(
rtc::ArrayView<const PacketTemplate> packets,
TransportFeedbackAdapter& adapter) {
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());
}
}
};
INSTANTIATE_TEST_SUITE_P(FeedbackFormats,
TransportFeedbackAdapterTest,
Bool(),
[](TestParamInfo<bool> param) {
if (param.param)
return "CongestionControlFeedback";
else
return "TransportFeedback";
});
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.transport_sequence_number,
packet.send_timestamp.ms()));
}
std::optional<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(packets, adapter);
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
}
TEST_P(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
TransportFeedbackAdapter adapter;
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.transport_sequence_number,
packet.send_timestamp.ms()));
}
// 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(sent_packets, adapter);
ComparePacketFeedbackVectors(sent_packets,
adapted_feedback->packet_feedbacks);
}
TEST_P(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
TransportFeedbackAdapter adapter;
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<PacketTemplate> sent_packets;
for (const PacketTemplate& packet : packets) {
if (packet.transport_sequence_number >= kSendSideDropBefore) {
sent_packets.push_back(packet);
}
}
for (const PacketTemplate& packet : sent_packets) {
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
/*overhead=*/0u, TimeNow());
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
packet.send_timestamp.ms()));
}
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<PacketTemplate> expected_packets(
packets.begin() + kSendSideDropBefore,
packets.begin() + kReceiveSideDropAfter + 1);
// Packets that have timed out on the send-side have lost the
// information stored on the send-side. And they will not be reported to
// observers since we won't know that they come from the same networks.
ComparePacketFeedbackVectors(expected_packets,
adapted_feedback->packet_feedbacks);
}
TEST_P(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
TransportFeedbackAdapter adapter;
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.transport_sequence_number,
packet.send_timestamp.ms()));
std::optional<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(packets, adapter);
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(1));
EXPECT_TRUE(adapted_feedback->packet_feedbacks[0].sent_packet.audio);
}
TEST_P(TransportFeedbackAdapterTest, ReceiveTimeWrapsBothWays) {
TransportFeedbackAdapter adapter;
TimeDelta kHighArrivalTime =
rtcp::TransportFeedback::kDeltaTick * (1 << 8) * ((1 << 23) - 1);
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.transport_sequence_number,
packet.send_timestamp.ms()));
}
for (size_t i = 0; i < packets.size(); ++i) {
std::vector<PacketTemplate> received_packets = {packets[i]};
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_P(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
TransportFeedbackAdapter adapter;
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 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()));
}
// Adapter keeps the packets ordered by sequence number (which is itself
// assigned by the order of transmission). Reordering by some other criteria,
// eg. arrival time, is up to the observers.
std::optional<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(packets, adapter);
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
}
TEST_P(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
TransportFeedbackAdapter adapter;
PacketTemplate packet = {};
// Add a packet and then mark it as sent.
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, 0u,
TimeNow());
std::optional<SentPacket> sent_packet = adapter.ProcessSentPacket(
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.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;
const PacketTemplate packets[] = {
{
.transport_sequence_number = 1,
.rtp_sequence_number = 101,
.ecn = EcnMarking::kCe,
.send_timestamp = Timestamp::Millis(100),
.receive_timestamp = Timestamp::Millis(200),
},
{
.transport_sequence_number = 2,
.rtp_sequence_number = 102,
.ecn = EcnMarking::kEct1,
.send_timestamp = Timestamp::Millis(110),
.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()));
}
rtcp::CongestionControlFeedback rtcp_feedback =
BuildRtcpCongestionControlFeedbackPacket(packets);
std::optional<TransportPacketsFeedback> adapted_feedback =
adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(2));
EXPECT_THAT(adapted_feedback->packet_feedbacks[0].ecn, EcnMarking::kCe);
EXPECT_THAT(adapted_feedback->packet_feedbacks[1].ecn, EcnMarking::kEct1);
EXPECT_TRUE(adapted_feedback->transport_supports_ecn);
}
TEST(TransportFeedbackAdapterCongestionFeedbackTest,
ReportTransportDoesNotSupportEcnIfFeedbackContainNotEctPacket) {
TransportFeedbackAdapter adapter;
const PacketTemplate packets[] = {
{
.transport_sequence_number = 1,
.rtp_sequence_number = 101,
.ecn = EcnMarking::kCe,
.send_timestamp = Timestamp::Millis(100),
.receive_timestamp = Timestamp::Millis(200),
},
{
.transport_sequence_number = 2,
.rtp_sequence_number = 102,
.ecn = EcnMarking::kNotEct,
.send_timestamp = Timestamp::Millis(110),
.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()));
}
rtcp::CongestionControlFeedback rtcp_feedback =
BuildRtcpCongestionControlFeedbackPacket(packets);
std::optional<TransportPacketsFeedback> adapted_feedback =
adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
EXPECT_FALSE(adapted_feedback->transport_supports_ecn);
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(2));
}
} // namespace webrtc