Propagate desicion if RTP packet should be ECT(1) marked to socket
With this CL, the decision if an RTP packet should be sent as ect(1) is made in RtpControllerSend depending on if RFC 8888 has been negotiated and if CCFB is received with ECN enabled. Since webrtc does not yet adapt to ECN feedback, packets are sent as ECT(1) until the first feedback is received. Change-Id: Iddf63849328afbe54a7c8f921f2e8db134aeff6a Bug: webrtc:42225697 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/367388 Commit-Queue: Per Kjellander <perkj@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43609}
This commit is contained in:
parent
897906d950
commit
776866774f
@ -31,6 +31,7 @@ struct PacketOptions {
|
|||||||
bool is_media = true;
|
bool is_media = true;
|
||||||
bool included_in_feedback = false;
|
bool included_in_feedback = false;
|
||||||
bool included_in_allocation = false;
|
bool included_in_allocation = false;
|
||||||
|
bool send_as_ect1 = false;
|
||||||
// Whether this packet can be part of a packet batch at lower levels.
|
// Whether this packet can be part of a packet batch at lower levels.
|
||||||
bool batchable = false;
|
bool batchable = false;
|
||||||
// Whether this packet is the last of a batch.
|
// Whether this packet is the last of a batch.
|
||||||
|
|||||||
@ -179,6 +179,7 @@ struct RTC_EXPORT TransportPacketsFeedback {
|
|||||||
|
|
||||||
Timestamp feedback_time = Timestamp::PlusInfinity();
|
Timestamp feedback_time = Timestamp::PlusInfinity();
|
||||||
DataSize data_in_flight = DataSize::Zero();
|
DataSize data_in_flight = DataSize::Zero();
|
||||||
|
bool transport_supports_ecn = false;
|
||||||
std::vector<PacketResult> packet_feedbacks;
|
std::vector<PacketResult> packet_feedbacks;
|
||||||
|
|
||||||
// Arrival times for messages without send time information.
|
// Arrival times for messages without send time information.
|
||||||
|
|||||||
@ -1195,8 +1195,7 @@ Call::Stats Call::GetStats() const {
|
|||||||
|
|
||||||
void Call::EnableSendCongestionControlFeedbackAccordingToRfc8888() {
|
void Call::EnableSendCongestionControlFeedbackAccordingToRfc8888() {
|
||||||
receive_side_cc_.EnableSendCongestionControlFeedbackAccordingToRfc8888();
|
receive_side_cc_.EnableSendCongestionControlFeedbackAccordingToRfc8888();
|
||||||
transport_send_->packet_router()
|
transport_send_->EnableCongestionControlFeedbackAccordingToRfc8888();
|
||||||
->EnableCongestionControlFeedbackAccordingToRfc8888();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Call::FeedbackAccordingToRfc8888Count() {
|
int Call::FeedbackAccordingToRfc8888Count() {
|
||||||
|
|||||||
@ -635,6 +635,13 @@ void RtpTransportControllerSend::NotifyBweOfPacedSentPacket(
|
|||||||
packet, pacing_info, transport_overhead_bytes_per_packet_, creation_time);
|
packet, pacing_info, transport_overhead_bytes_per_packet_, creation_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RtpTransportControllerSend::
|
||||||
|
EnableCongestionControlFeedbackAccordingToRfc8888() {
|
||||||
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
|
transport_is_ecn_capable_ = true;
|
||||||
|
packet_router_.ConfigureForRfc8888Feedback(transport_is_ecn_capable_);
|
||||||
|
}
|
||||||
|
|
||||||
void RtpTransportControllerSend::OnTransportFeedback(
|
void RtpTransportControllerSend::OnTransportFeedback(
|
||||||
Timestamp receive_time,
|
Timestamp receive_time,
|
||||||
const rtcp::TransportFeedback& feedback) {
|
const rtcp::TransportFeedback& feedback) {
|
||||||
@ -645,11 +652,7 @@ void RtpTransportControllerSend::OnTransportFeedback(
|
|||||||
transport_feedback_adapter_.ProcessTransportFeedback(feedback,
|
transport_feedback_adapter_.ProcessTransportFeedback(feedback,
|
||||||
receive_time);
|
receive_time);
|
||||||
if (feedback_msg) {
|
if (feedback_msg) {
|
||||||
if (controller_)
|
HandleTransportPacketsFeedback(*feedback_msg);
|
||||||
PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg));
|
|
||||||
|
|
||||||
// Only update outstanding data if any packet is first time acked.
|
|
||||||
UpdateCongestedState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,14 +668,29 @@ void RtpTransportControllerSend::OnCongestionControlFeedback(
|
|||||||
transport_feedback_adapter_.ProcessCongestionControlFeedback(
|
transport_feedback_adapter_.ProcessCongestionControlFeedback(
|
||||||
feedback, receive_time);
|
feedback, receive_time);
|
||||||
if (feedback_msg) {
|
if (feedback_msg) {
|
||||||
if (controller_)
|
HandleTransportPacketsFeedback(*feedback_msg);
|
||||||
PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg));
|
|
||||||
|
|
||||||
// Only update outstanding data if any packet is first time acked.
|
|
||||||
UpdateCongestedState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RtpTransportControllerSend::HandleTransportPacketsFeedback(
|
||||||
|
const TransportPacketsFeedback& feedback) {
|
||||||
|
if (transport_is_ecn_capable_) {
|
||||||
|
// If transport does not support ECN, packets should not be sent as ECT(1).
|
||||||
|
// TODO: bugs.webrtc.org/42225697 - adapt to ECN feedback and continue to
|
||||||
|
// send packets as ECT(1) if transport is ECN capable.
|
||||||
|
transport_is_ecn_capable_ = false;
|
||||||
|
RTC_LOG(LS_INFO) << " Transport is "
|
||||||
|
<< (feedback.transport_supports_ecn ? "" : " not ")
|
||||||
|
<< " ECN capable. Stop sending ECT(1).";
|
||||||
|
packet_router_.ConfigureForRfc8888Feedback(transport_is_ecn_capable_);
|
||||||
|
}
|
||||||
|
if (controller_)
|
||||||
|
PostUpdates(controller_->OnTransportPacketsFeedback(feedback));
|
||||||
|
|
||||||
|
// Only update outstanding data if any packet is first time acked.
|
||||||
|
UpdateCongestedState();
|
||||||
|
}
|
||||||
|
|
||||||
void RtpTransportControllerSend::OnRemoteNetworkEstimate(
|
void RtpTransportControllerSend::OnRemoteNetworkEstimate(
|
||||||
NetworkStateEstimate estimate) {
|
NetworkStateEstimate estimate) {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
|
|||||||
@ -142,6 +142,9 @@ class RtpTransportControllerSend final
|
|||||||
return controller_.get();
|
return controller_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called once it's known that the remote end supports RFC 8888.
|
||||||
|
void EnableCongestionControlFeedbackAccordingToRfc8888() override;
|
||||||
|
|
||||||
int ReceivedCongestionControlFeedbackCount() const override {
|
int ReceivedCongestionControlFeedbackCount() const override {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
return feedback_count_;
|
return feedback_count_;
|
||||||
@ -153,6 +156,8 @@ class RtpTransportControllerSend final
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void MaybeCreateControllers() RTC_RUN_ON(sequence_checker_);
|
void MaybeCreateControllers() RTC_RUN_ON(sequence_checker_);
|
||||||
|
void HandleTransportPacketsFeedback(const TransportPacketsFeedback& feedback)
|
||||||
|
RTC_RUN_ON(sequence_checker_);
|
||||||
void UpdateNetworkAvailability() RTC_RUN_ON(sequence_checker_);
|
void UpdateNetworkAvailability() RTC_RUN_ON(sequence_checker_);
|
||||||
void UpdateInitialConstraints(TargetRateConstraints new_contraints)
|
void UpdateInitialConstraints(TargetRateConstraints new_contraints)
|
||||||
RTC_RUN_ON(sequence_checker_);
|
RTC_RUN_ON(sequence_checker_);
|
||||||
@ -237,6 +242,7 @@ class RtpTransportControllerSend final
|
|||||||
|
|
||||||
DataSize congestion_window_size_ RTC_GUARDED_BY(sequence_checker_);
|
DataSize congestion_window_size_ RTC_GUARDED_BY(sequence_checker_);
|
||||||
bool is_congested_ RTC_GUARDED_BY(sequence_checker_);
|
bool is_congested_ RTC_GUARDED_BY(sequence_checker_);
|
||||||
|
bool transport_is_ecn_capable_ = false;
|
||||||
// Count of feedback messages received.
|
// Count of feedback messages received.
|
||||||
int feedback_count_ RTC_GUARDED_BY(sequence_checker_) = 0;
|
int feedback_count_ RTC_GUARDED_BY(sequence_checker_) = 0;
|
||||||
int transport_cc_feedback_count_ RTC_GUARDED_BY(sequence_checker_) = 0;
|
int transport_cc_feedback_count_ RTC_GUARDED_BY(sequence_checker_) = 0;
|
||||||
|
|||||||
@ -161,6 +161,9 @@ class RtpTransportControllerSendInterface {
|
|||||||
|
|
||||||
virtual void EnsureStarted() = 0;
|
virtual void EnsureStarted() = 0;
|
||||||
virtual NetworkControllerInterface* GetNetworkController() = 0;
|
virtual NetworkControllerInterface* GetNetworkController() = 0;
|
||||||
|
|
||||||
|
// Called once it's known that the remote end supports RFC 8888.
|
||||||
|
virtual void EnableCongestionControlFeedbackAccordingToRfc8888() = 0;
|
||||||
// Count of RFC8888 feedback reports received
|
// Count of RFC8888 feedback reports received
|
||||||
virtual int ReceivedCongestionControlFeedbackCount() const = 0;
|
virtual int ReceivedCongestionControlFeedbackCount() const = 0;
|
||||||
// Count of transport-cc feedback reports received
|
// Count of transport-cc feedback reports received
|
||||||
|
|||||||
@ -115,6 +115,10 @@ class MockRtpTransportControllerSend
|
|||||||
GetNetworkController,
|
GetNetworkController,
|
||||||
(),
|
(),
|
||||||
(override));
|
(override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
EnableCongestionControlFeedbackAccordingToRfc8888,
|
||||||
|
(),
|
||||||
|
(override));
|
||||||
MOCK_METHOD(int,
|
MOCK_METHOD(int,
|
||||||
ReceivedCongestionControlFeedbackCount,
|
ReceivedCongestionControlFeedbackCount,
|
||||||
(),
|
(),
|
||||||
|
|||||||
@ -207,7 +207,7 @@ bool MediaChannelUtil::TransportForMediaChannels::SendRtp(
|
|||||||
included_in_allocation = options.included_in_allocation,
|
included_in_allocation = options.included_in_allocation,
|
||||||
batchable = options.batchable,
|
batchable = options.batchable,
|
||||||
last_packet_in_batch = options.last_packet_in_batch,
|
last_packet_in_batch = options.last_packet_in_batch,
|
||||||
is_media = options.is_media,
|
is_media = options.is_media, ect_1 = options.send_as_ect1,
|
||||||
packet = rtc::CopyOnWriteBuffer(packet, kMaxRtpPacketLen)]() mutable {
|
packet = rtc::CopyOnWriteBuffer(packet, kMaxRtpPacketLen)]() mutable {
|
||||||
rtc::PacketOptions rtc_options;
|
rtc::PacketOptions rtc_options;
|
||||||
rtc_options.packet_id = packet_id;
|
rtc_options.packet_id = packet_id;
|
||||||
@ -219,6 +219,7 @@ bool MediaChannelUtil::TransportForMediaChannels::SendRtp(
|
|||||||
rtc_options.info_signaled_after_sent.included_in_allocation =
|
rtc_options.info_signaled_after_sent.included_in_allocation =
|
||||||
included_in_allocation;
|
included_in_allocation;
|
||||||
rtc_options.info_signaled_after_sent.is_media = is_media;
|
rtc_options.info_signaled_after_sent.is_media = is_media;
|
||||||
|
rtc_options.ecn_1 = ect_1;
|
||||||
rtc_options.batchable = batchable;
|
rtc_options.batchable = batchable;
|
||||||
rtc_options.last_packet_in_batch = last_packet_in_batch;
|
rtc_options.last_packet_in_batch = last_packet_in_batch;
|
||||||
DoSendPacket(&packet, false, rtc_options);
|
DoSendPacket(&packet, false, rtc_options);
|
||||||
|
|||||||
@ -41,6 +41,7 @@ rtc_library("transport_feedback") {
|
|||||||
deps = [
|
deps = [
|
||||||
"../..:module_api_public",
|
"../..:module_api_public",
|
||||||
"../../../api:sequence_checker",
|
"../../../api:sequence_checker",
|
||||||
|
"../../../api/transport:ecn_marking",
|
||||||
"../../../api/transport:network_control",
|
"../../../api/transport:network_control",
|
||||||
"../../../api/units:data_size",
|
"../../../api/units:data_size",
|
||||||
"../../../api/units:time_delta",
|
"../../../api/units:time_delta",
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
|
#include "api/transport/ecn_marking.h"
|
||||||
#include "api/transport/network_types.h"
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
@ -247,7 +248,7 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
|
|||||||
<< " packets because they were sent on a different route.";
|
<< " packets because they were sent on a different route.";
|
||||||
}
|
}
|
||||||
return ToTransportFeedback(std::move(packet_result_vector),
|
return ToTransportFeedback(std::move(packet_result_vector),
|
||||||
feedback_receive_time);
|
feedback_receive_time, /*suports_ecn=*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback>
|
std::optional<TransportPacketsFeedback>
|
||||||
@ -277,6 +278,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
|||||||
|
|
||||||
int ignored_packets = 0;
|
int ignored_packets = 0;
|
||||||
int failed_lookups = 0;
|
int failed_lookups = 0;
|
||||||
|
bool supports_ecn = true;
|
||||||
std::vector<PacketResult> packet_result_vector;
|
std::vector<PacketResult> packet_result_vector;
|
||||||
for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info :
|
for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info :
|
||||||
feedback.packets()) {
|
feedback.packets()) {
|
||||||
@ -296,6 +298,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
|||||||
result.sent_packet = packet_feedback->sent;
|
result.sent_packet = packet_feedback->sent;
|
||||||
if (packet_info.arrival_time_offset.IsFinite()) {
|
if (packet_info.arrival_time_offset.IsFinite()) {
|
||||||
result.receive_time = current_offset_ - packet_info.arrival_time_offset;
|
result.receive_time = current_offset_ - packet_info.arrival_time_offset;
|
||||||
|
supports_ecn &= packet_info.ecn != EcnMarking::kNotEct;
|
||||||
}
|
}
|
||||||
result.ecn = packet_info.ecn;
|
result.ecn = packet_info.ecn;
|
||||||
packet_result_vector.push_back(result);
|
packet_result_vector.push_back(result);
|
||||||
@ -318,13 +321,14 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
|||||||
return lhs.sent_packet.sequence_number < rhs.sent_packet.sequence_number;
|
return lhs.sent_packet.sequence_number < rhs.sent_packet.sequence_number;
|
||||||
});
|
});
|
||||||
return ToTransportFeedback(std::move(packet_result_vector),
|
return ToTransportFeedback(std::move(packet_result_vector),
|
||||||
feedback_receive_time);
|
feedback_receive_time, supports_ecn);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback>
|
std::optional<TransportPacketsFeedback>
|
||||||
TransportFeedbackAdapter::ToTransportFeedback(
|
TransportFeedbackAdapter::ToTransportFeedback(
|
||||||
std::vector<PacketResult> packet_results,
|
std::vector<PacketResult> packet_results,
|
||||||
Timestamp feedback_receive_time) {
|
Timestamp feedback_receive_time,
|
||||||
|
bool supports_ecn) {
|
||||||
TransportPacketsFeedback msg;
|
TransportPacketsFeedback msg;
|
||||||
msg.feedback_time = feedback_receive_time;
|
msg.feedback_time = feedback_receive_time;
|
||||||
if (packet_results.empty()) {
|
if (packet_results.empty()) {
|
||||||
@ -332,6 +336,7 @@ TransportFeedbackAdapter::ToTransportFeedback(
|
|||||||
}
|
}
|
||||||
msg.packet_feedbacks = std::move(packet_results);
|
msg.packet_feedbacks = std::move(packet_results);
|
||||||
msg.data_in_flight = in_flight_.GetOutstandingData(network_route_);
|
msg.data_in_flight = in_flight_.GetOutstandingData(network_route_);
|
||||||
|
msg.transport_supports_ecn = supports_ecn;
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,7 +111,8 @@ class TransportFeedbackAdapter {
|
|||||||
bool received);
|
bool received);
|
||||||
std::optional<TransportPacketsFeedback> ToTransportFeedback(
|
std::optional<TransportPacketsFeedback> ToTransportFeedback(
|
||||||
std::vector<PacketResult> packet_results,
|
std::vector<PacketResult> packet_results,
|
||||||
Timestamp feedback_receive_time);
|
Timestamp feedback_receive_time,
|
||||||
|
bool supports_ecn);
|
||||||
|
|
||||||
DataSize pending_untracked_size_ = DataSize::Zero();
|
DataSize pending_untracked_size_ = DataSize::Zero();
|
||||||
Timestamp last_send_time_ = Timestamp::MinusInfinity();
|
Timestamp last_send_time_ = Timestamp::MinusInfinity();
|
||||||
|
|||||||
@ -532,27 +532,75 @@ TEST(TransportFeedbackAdapterCongestionFeedbackTest,
|
|||||||
CongestionControlFeedbackResultHasEcn) {
|
CongestionControlFeedbackResultHasEcn) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
PacketTemplate packet = {
|
const PacketTemplate packets[] = {
|
||||||
.transport_sequence_number = 1,
|
{
|
||||||
.rtp_sequence_number = 101,
|
.transport_sequence_number = 1,
|
||||||
.packet_size = DataSize::Bytes(200),
|
.rtp_sequence_number = 101,
|
||||||
.send_timestamp = Timestamp::Millis(100),
|
.ecn = EcnMarking::kCe,
|
||||||
.pacing_info = kPacingInfo0,
|
.send_timestamp = Timestamp::Millis(100),
|
||||||
.receive_timestamp = Timestamp::Millis(200),
|
.receive_timestamp = Timestamp::Millis(200),
|
||||||
};
|
},
|
||||||
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
{
|
||||||
/*overhead=*/0u, TimeNow());
|
.transport_sequence_number = 2,
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
.rtp_sequence_number = 102,
|
||||||
packet.send_timestamp.ms()));
|
.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()));
|
||||||
|
}
|
||||||
|
|
||||||
packet.ecn = EcnMarking::kCe;
|
|
||||||
rtcp::CongestionControlFeedback rtcp_feedback =
|
rtcp::CongestionControlFeedback rtcp_feedback =
|
||||||
BuildRtcpCongestionControlFeedbackPacket(rtc::MakeArrayView(&packet, 1));
|
BuildRtcpCongestionControlFeedbackPacket(packets);
|
||||||
std::optional<TransportPacketsFeedback> adapted_feedback =
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
|
adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
|
||||||
|
|
||||||
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(1));
|
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(2));
|
||||||
ASSERT_THAT(adapted_feedback->packet_feedbacks[0].ecn, EcnMarking::kCe);
|
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
|
} // namespace webrtc
|
||||||
|
|||||||
@ -87,9 +87,10 @@ void PacketRouter::RegisterNotifyBweCallback(
|
|||||||
notify_bwe_callback_ = std::move(callback);
|
notify_bwe_callback_ = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketRouter::EnableCongestionControlFeedbackAccordingToRfc8888() {
|
void PacketRouter::ConfigureForRfc8888Feedback(bool send_rtp_packets_as_ect1) {
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
use_cc_feedback_according_to_rfc8888_ = true;
|
use_cc_feedback_according_to_rfc8888_ = true;
|
||||||
|
send_rtp_packets_as_ect1_ = send_rtp_packets_as_ect1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketRouter::AddSendRtpModuleToMap(RtpRtcpInterface* rtp_module,
|
void PacketRouter::AddSendRtpModuleToMap(RtpRtcpInterface* rtp_module,
|
||||||
@ -203,6 +204,9 @@ void PacketRouter::SendPacket(std::unique_ptr<RtpPacketToSend> packet,
|
|||||||
packet->HasExtension<TransportSequenceNumber>()) {
|
packet->HasExtension<TransportSequenceNumber>()) {
|
||||||
packet->set_transport_sequence_number(transport_seq_++);
|
packet->set_transport_sequence_number(transport_seq_++);
|
||||||
}
|
}
|
||||||
|
if (send_rtp_packets_as_ect1_) {
|
||||||
|
packet->set_send_as_ect1();
|
||||||
|
}
|
||||||
rtp_module->AssignSequenceNumber(*packet);
|
rtp_module->AssignSequenceNumber(*packet);
|
||||||
if (notify_bwe_callback_) {
|
if (notify_bwe_callback_) {
|
||||||
notify_bwe_callback_(*packet, cluster_info);
|
notify_bwe_callback_(*packet, cluster_info);
|
||||||
|
|||||||
@ -55,7 +55,11 @@ class PacketRouter : public PacingController::PacketSender {
|
|||||||
void RegisterNotifyBweCallback(
|
void RegisterNotifyBweCallback(
|
||||||
absl::AnyInvocable<void(const RtpPacketToSend& packet,
|
absl::AnyInvocable<void(const RtpPacketToSend& packet,
|
||||||
const PacedPacketInfo& pacing_info)> callback);
|
const PacedPacketInfo& pacing_info)> callback);
|
||||||
void EnableCongestionControlFeedbackAccordingToRfc8888();
|
|
||||||
|
// Ensures that PacketRouter generates transport sequence numbers for all RTP
|
||||||
|
// packets. If `send_rtp_packets_as_ect1` is true, packets will be requested
|
||||||
|
// to be sent as ect1.
|
||||||
|
void ConfigureForRfc8888Feedback(bool send_rtp_packets_as_ect1);
|
||||||
|
|
||||||
void AddSendRtpModule(RtpRtcpInterface* rtp_module, bool remb_candidate);
|
void AddSendRtpModule(RtpRtcpInterface* rtp_module, bool remb_candidate);
|
||||||
void RemoveSendRtpModule(RtpRtcpInterface* rtp_module);
|
void RemoveSendRtpModule(RtpRtcpInterface* rtp_module);
|
||||||
@ -119,6 +123,7 @@ class PacketRouter : public PacingController::PacketSender {
|
|||||||
uint64_t transport_seq_ RTC_GUARDED_BY(thread_checker_);
|
uint64_t transport_seq_ RTC_GUARDED_BY(thread_checker_);
|
||||||
bool use_cc_feedback_according_to_rfc8888_ RTC_GUARDED_BY(thread_checker_) =
|
bool use_cc_feedback_according_to_rfc8888_ RTC_GUARDED_BY(thread_checker_) =
|
||||||
false;
|
false;
|
||||||
|
bool send_rtp_packets_as_ect1_ RTC_GUARDED_BY(thread_checker_) = false;
|
||||||
absl::AnyInvocable<void(RtpPacketToSend& packet,
|
absl::AnyInvocable<void(RtpPacketToSend& packet,
|
||||||
const PacedPacketInfo& pacing_info)>
|
const PacedPacketInfo& pacing_info)>
|
||||||
notify_bwe_callback_ RTC_GUARDED_BY(thread_checker_) = nullptr;
|
notify_bwe_callback_ RTC_GUARDED_BY(thread_checker_) = nullptr;
|
||||||
|
|||||||
@ -423,7 +423,7 @@ TEST_F(PacketRouterTest,
|
|||||||
EXPECT_CALL(rtp_1, CanSendPacket).WillRepeatedly(Return(true));
|
EXPECT_CALL(rtp_1, CanSendPacket).WillRepeatedly(Return(true));
|
||||||
|
|
||||||
packet_router.AddSendRtpModule(&rtp_1, false);
|
packet_router.AddSendRtpModule(&rtp_1, false);
|
||||||
packet_router.EnableCongestionControlFeedbackAccordingToRfc8888();
|
packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/false);
|
||||||
|
|
||||||
auto packet = BuildRtpPacket(kSsrc1);
|
auto packet = BuildRtpPacket(kSsrc1);
|
||||||
EXPECT_CALL(notify_bwe_callback, Call)
|
EXPECT_CALL(notify_bwe_callback, Call)
|
||||||
@ -437,6 +437,34 @@ TEST_F(PacketRouterTest,
|
|||||||
packet_router.RemoveSendRtpModule(&rtp_1);
|
packet_router.RemoveSendRtpModule(&rtp_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PacketRouterTest, SendPacketsAsEct1IfConfigured) {
|
||||||
|
const uint16_t kSsrc1 = 1234;
|
||||||
|
PacketRouter packet_router;
|
||||||
|
NiceMock<MockRtpRtcpInterface> rtp_1;
|
||||||
|
ON_CALL(rtp_1, SSRC()).WillByDefault(Return(kSsrc1));
|
||||||
|
ON_CALL(rtp_1, CanSendPacket).WillByDefault(Return(kSsrc1));
|
||||||
|
|
||||||
|
packet_router.AddSendRtpModule(&rtp_1, false);
|
||||||
|
packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/true);
|
||||||
|
|
||||||
|
testing::Sequence s;
|
||||||
|
EXPECT_CALL(
|
||||||
|
rtp_1,
|
||||||
|
SendPacket(Pointee(Property(&RtpPacketToSend::send_as_ect1, true)), _))
|
||||||
|
.InSequence(s);
|
||||||
|
EXPECT_CALL(
|
||||||
|
rtp_1,
|
||||||
|
SendPacket(Pointee(Property(&RtpPacketToSend::send_as_ect1, false)), _))
|
||||||
|
.InSequence(s);
|
||||||
|
|
||||||
|
packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo());
|
||||||
|
packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/false);
|
||||||
|
packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo());
|
||||||
|
|
||||||
|
packet_router.OnBatchComplete();
|
||||||
|
packet_router.RemoveSendRtpModule(&rtp_1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(PacketRouterTest, SendTransportFeedback) {
|
TEST_F(PacketRouterTest, SendTransportFeedback) {
|
||||||
NiceMock<MockRtpRtcpInterface> rtp_1;
|
NiceMock<MockRtpRtcpInterface> rtp_1;
|
||||||
NiceMock<MockRtpRtcpInterface> rtp_2;
|
NiceMock<MockRtpRtcpInterface> rtp_2;
|
||||||
|
|||||||
@ -147,6 +147,11 @@ class RtpPacketToSend : public RtpPacket {
|
|||||||
void set_transport_sequence_number(int64_t transport_sequence_number) {
|
void set_transport_sequence_number(int64_t transport_sequence_number) {
|
||||||
transport_sequence_number_ = transport_sequence_number;
|
transport_sequence_number_ = transport_sequence_number;
|
||||||
}
|
}
|
||||||
|
// Transport is capable of handling explicit congestion notification and the
|
||||||
|
// RTP packet should be sent as ect(1)
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9331.html
|
||||||
|
bool send_as_ect1() const { return send_as_ect1_; }
|
||||||
|
void set_send_as_ect1() { send_as_ect1_ = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero();
|
webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero();
|
||||||
@ -161,6 +166,7 @@ class RtpPacketToSend : public RtpPacket {
|
|||||||
bool is_key_frame_ = false;
|
bool is_key_frame_ = false;
|
||||||
bool fec_protect_packet_ = false;
|
bool fec_protect_packet_ = false;
|
||||||
bool is_red_ = false;
|
bool is_red_ = false;
|
||||||
|
bool send_as_ect1_ = false;
|
||||||
std::optional<TimeDelta> time_in_send_queue_;
|
std::optional<TimeDelta> time_in_send_queue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -302,6 +302,7 @@ void RtpSenderEgress::CompleteSendPacket(const Packet& compound_packet,
|
|||||||
send_packet_observer_->OnSendPacket(packet_id, packet->capture_time(),
|
send_packet_observer_->OnSendPacket(packet_id, packet->capture_time(),
|
||||||
packet->Ssrc());
|
packet->Ssrc());
|
||||||
}
|
}
|
||||||
|
options.send_as_ect1 = packet->send_as_ect1();
|
||||||
options.batchable = enable_send_packet_batching_ && !is_audio_;
|
options.batchable = enable_send_packet_batching_ && !is_audio_;
|
||||||
options.last_packet_in_batch = last_in_batch;
|
options.last_packet_in_batch = last_in_batch;
|
||||||
const bool send_success = SendPacketToNetwork(*packet, options, pacing_info);
|
const bool send_success = SendPacketToNetwork(*packet, options, pacing_info);
|
||||||
|
|||||||
@ -24,6 +24,7 @@ if (rtc_include_tests) {
|
|||||||
"../../:field_trial",
|
"../../:field_trial",
|
||||||
"../../:test_support",
|
"../../:test_support",
|
||||||
"../../../api:rtc_stats_api",
|
"../../../api:rtc_stats_api",
|
||||||
|
"../../../api/transport:ecn_marking",
|
||||||
"../../../api/units:data_rate",
|
"../../../api/units:data_rate",
|
||||||
"../../../api/units:time_delta",
|
"../../../api/units:time_delta",
|
||||||
"../../../media:stream_params",
|
"../../../media:stream_params",
|
||||||
|
|||||||
@ -47,6 +47,26 @@ class RtcpFeedbackCounter {
|
|||||||
}
|
}
|
||||||
if (header.fmt() == rtcp::CongestionControlFeedback::kFeedbackMessageType) {
|
if (header.fmt() == rtcp::CongestionControlFeedback::kFeedbackMessageType) {
|
||||||
++congestion_control_feedback_;
|
++congestion_control_feedback_;
|
||||||
|
rtcp::CongestionControlFeedback fb;
|
||||||
|
ASSERT_TRUE(fb.Parse(header));
|
||||||
|
for (const rtcp::CongestionControlFeedback::PacketInfo& info :
|
||||||
|
fb.packets()) {
|
||||||
|
switch (info.ecn) {
|
||||||
|
case EcnMarking::kNotEct:
|
||||||
|
++not_ect_;
|
||||||
|
break;
|
||||||
|
case EcnMarking::kEct0:
|
||||||
|
// Not used.
|
||||||
|
RTC_CHECK_NOTREACHED();
|
||||||
|
break;
|
||||||
|
case EcnMarking::kEct1:
|
||||||
|
// ECN-Capable Transport
|
||||||
|
++ect1_;
|
||||||
|
break;
|
||||||
|
case EcnMarking::kCe:
|
||||||
|
++ce_;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) {
|
if (header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) {
|
||||||
++transport_sequence_number_feedback_;
|
++transport_sequence_number_feedback_;
|
||||||
@ -59,10 +79,16 @@ class RtcpFeedbackCounter {
|
|||||||
int FeedbackAccordingToTransportCc() const {
|
int FeedbackAccordingToTransportCc() const {
|
||||||
return transport_sequence_number_feedback_;
|
return transport_sequence_number_feedback_;
|
||||||
}
|
}
|
||||||
|
int not_ect() const { return not_ect_; }
|
||||||
|
int ect1() const { return ect1_; }
|
||||||
|
int ce() const { return ce_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int congestion_control_feedback_ = 0;
|
int congestion_control_feedback_ = 0;
|
||||||
int transport_sequence_number_feedback_ = 0;
|
int transport_sequence_number_feedback_ = 0;
|
||||||
|
int not_ect_ = 0;
|
||||||
|
int ect1_ = 0;
|
||||||
|
int ce_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
rtc::scoped_refptr<const RTCStatsReport> GetStatsAndProcess(
|
rtc::scoped_refptr<const RTCStatsReport> GetStatsAndProcess(
|
||||||
@ -102,14 +128,12 @@ TEST(L4STest, NegotiateAndUseCcfbIfEnabled) {
|
|||||||
s.net()->CreateRoute(callee->endpoint(), {ret_node}, caller->endpoint());
|
s.net()->CreateRoute(callee->endpoint(), {ret_node}, caller->endpoint());
|
||||||
|
|
||||||
RtcpFeedbackCounter send_node_feedback_counter;
|
RtcpFeedbackCounter send_node_feedback_counter;
|
||||||
send_node->router()->SetFilter([&](const EmulatedIpPacket& packet) {
|
send_node->router()->SetWatcher([&](const EmulatedIpPacket& packet) {
|
||||||
send_node_feedback_counter.Count(packet);
|
send_node_feedback_counter.Count(packet);
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
RtcpFeedbackCounter ret_node_feedback_counter;
|
RtcpFeedbackCounter ret_node_feedback_counter;
|
||||||
ret_node->router()->SetFilter([&](const EmulatedIpPacket& packet) {
|
ret_node->router()->SetWatcher([&](const EmulatedIpPacket& packet) {
|
||||||
ret_node_feedback_counter.Count(packet);
|
ret_node_feedback_counter.Count(packet);
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
auto signaling = s.ConnectSignaling(caller, callee, {send_node}, {ret_node});
|
auto signaling = s.ConnectSignaling(caller, callee, {send_node}, {ret_node});
|
||||||
@ -202,5 +226,60 @@ TEST(L4STest, CallerAdaptToLinkCapacityWithoutEcn) {
|
|||||||
EXPECT_LT(available_bwe.kbps(), 610);
|
EXPECT_LT(available_bwe.kbps(), 610);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(L4STest, SendsEct1UntilFirstFeedback) {
|
||||||
|
test::ScopedFieldTrials trials(
|
||||||
|
"WebRTC-RFC8888CongestionControlFeedback/Enabled/");
|
||||||
|
PeerScenario s(*test_info_);
|
||||||
|
|
||||||
|
PeerScenarioClient::Config config = PeerScenarioClient::Config();
|
||||||
|
config.disable_encryption = true;
|
||||||
|
PeerScenarioClient* caller = s.CreateClient(config);
|
||||||
|
PeerScenarioClient* callee = s.CreateClient(config);
|
||||||
|
|
||||||
|
// Create network path from caller to callee.
|
||||||
|
auto caller_to_callee = s.net()->NodeBuilder().Build().node;
|
||||||
|
auto callee_to_caller = s.net()->NodeBuilder().Build().node;
|
||||||
|
s.net()->CreateRoute(caller->endpoint(), {caller_to_callee},
|
||||||
|
callee->endpoint());
|
||||||
|
s.net()->CreateRoute(callee->endpoint(), {callee_to_caller},
|
||||||
|
caller->endpoint());
|
||||||
|
|
||||||
|
RtcpFeedbackCounter feedback_counter;
|
||||||
|
std::atomic<bool> seen_ect1_feedback = false;
|
||||||
|
std::atomic<bool> seen_not_ect_feedback = false;
|
||||||
|
callee_to_caller->router()->SetWatcher([&](const EmulatedIpPacket& packet) {
|
||||||
|
feedback_counter.Count(packet);
|
||||||
|
if (feedback_counter.ect1() > 0) {
|
||||||
|
seen_ect1_feedback = true;
|
||||||
|
RTC_LOG(LS_INFO) << " ect 1" << feedback_counter.ect1();
|
||||||
|
}
|
||||||
|
if (feedback_counter.not_ect() > 0) {
|
||||||
|
seen_not_ect_feedback = true;
|
||||||
|
RTC_LOG(LS_INFO) << " not ect" << feedback_counter.not_ect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto signaling = s.ConnectSignaling(caller, callee, {caller_to_callee},
|
||||||
|
{callee_to_caller});
|
||||||
|
PeerScenarioClient::VideoSendTrackConfig video_conf;
|
||||||
|
video_conf.generator.squares_video->framerate = 15;
|
||||||
|
|
||||||
|
caller->CreateVideo("VIDEO_1", video_conf);
|
||||||
|
signaling.StartIceSignaling();
|
||||||
|
|
||||||
|
std::atomic<bool> offer_exchange_done(false);
|
||||||
|
signaling.NegotiateSdp([&](const SessionDescriptionInterface& answer) {
|
||||||
|
offer_exchange_done = true;
|
||||||
|
});
|
||||||
|
s.WaitAndProcess(&offer_exchange_done);
|
||||||
|
|
||||||
|
// Wait for first feedback where packets have been sent with ECT(1). Then
|
||||||
|
// feedback for packets sent as not ECT since currently webrtc does not
|
||||||
|
// implement adaptation to ECN.
|
||||||
|
s.WaitAndProcess(&seen_ect1_feedback, TimeDelta::Seconds(1));
|
||||||
|
EXPECT_FALSE(seen_not_ect_feedback);
|
||||||
|
s.WaitAndProcess(&seen_not_ect_feedback, TimeDelta::Seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user