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}
This commit is contained in:
Per K 2024-10-23 12:11:41 +00:00 committed by WebRTC LUCI CQ
parent e8c97c0d09
commit 1d2f85d1ce
9 changed files with 578 additions and 192 deletions

View File

@ -47,6 +47,7 @@ rtc_library("network_control") {
]
deps = [
":ecn_marking",
"../../api:field_trials_view",
"../../rtc_base/system:rtc_export",
"../environment",

View File

@ -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 @@ struct RTC_EXPORT PacketResult {
SentPacket sent_packet;
Timestamp receive_time = Timestamp::PlusInfinity();
EcnMarking ecn = EcnMarking::kNotEct;
};
struct RTC_EXPORT TransportPacketsFeedback {

View File

@ -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<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_);

View File

@ -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;

View File

@ -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",
]

View File

@ -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 @@ 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<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
@ -170,11 +190,12 @@ 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_)
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_) {
@ -184,7 +205,7 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
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());
@ -229,6 +250,77 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
feedback_receive_time);
}
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,
@ -254,22 +346,33 @@ DataSize TransportFeedbackAdapter::GetOutstandingData() const {
}
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 @@ std::optional<PacketFeedback> 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;

View File

@ -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 @@ class InFlightBytesTracker {
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 @@ class TransportFeedbackAdapter {
const PacedPacketInfo& pacing_info,
size_t overhead_bytes,
Timestamp creation_time);
std::optional<SentPacket> ProcessSentPacket(
const rtc::SentPacket& sent_packet);
@ -70,6 +82,10 @@ class TransportFeedbackAdapter {
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,11 +93,21 @@ class TransportFeedbackAdapter {
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 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,
@ -92,17 +118,24 @@ class TransportFeedbackAdapter {
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

View File

@ -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 @@ void ComparePacketFeedbackVectors(const std::vector<PacketResult>& 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<const PacketResult> packets) {
rtcp::TransportFeedback feedback;
feedback.SetBase(packets[0].sent_packet.sequence_number,
packets[0].receive_time);
for (const PacketResult& packet : packets) {
if (packet.receive_time.IsFinite()) {
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
packet.receive_time));
}
}
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;
}
RtpPacketToSend CreatePacketToSend(const PacketResult& packet,
uint32_t ssrc = kSsrc,
uint16_t rtp_sequence_number = 0) {
RtpPacketToSend CreatePacketToSend(PacketTemplate packet) {
RtpPacketToSend send_packet(nullptr);
send_packet.SetSsrc(ssrc);
send_packet.SetPayloadSize(packet.sent_packet.size.bytes() -
send_packet.SetSsrc(packet.ssrc);
send_packet.SetPayloadSize(packet.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
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;
}
class MockStreamFeedbackObserver : public webrtc::StreamFeedbackObserver {
public:
MOCK_METHOD(void,
OnPacketFeedbackVector,
(std::vector<StreamPacketInfo> packet_feedback_vector),
(override));
};
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::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) {
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 @@ TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
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 @@ 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<TransportPacketsFeedback> adapted_feedback =
CreateAndProcessFeedback(packets, adapter);
@ -257,30 +309,47 @@ 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<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]};
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();
@ -288,28 +357,35 @@ TEST_F(TransportFeedbackAdapterTest, SendTimeWrapsBothWays) {
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);
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 @@ 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<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

View File

@ -88,6 +88,7 @@ struct NetworkRoute {
}
bool operator==(const NetworkRoute& other) const;
bool operator!=(const NetworkRoute& other) { return !operator==(other); }
};
} // namespace rtc