Implement L4S send stats
described in
https://github.com/w3c/webrtc-stats/pull/792
with a location change from
https://github.com/w3c/webrtc-stats/pull/808
Bug: webrtc:42225697
Change-Id: I2660a17928ed6ecfb75ca597759c960678f17eaf
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/390866
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Cr-Commit-Position: refs/heads/main@{#45283}
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index 82acb45..46fe7cc 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -369,6 +369,9 @@
// RTX ssrc. Only present if RTX is negotiated.
std::optional<uint32_t> rtx_ssrc;
+
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-packetssentwithect1
+ std::optional<uint64_t> packets_sent_with_ect1;
};
// https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 5c7fa93..5b6c070 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -442,6 +442,7 @@
call_stats.header_and_padding_bytes_sent;
stats.retransmitted_bytes_sent = call_stats.retransmitted_bytes_sent;
stats.packets_sent = call_stats.packetsSent;
+ stats.packets_sent_with_ect1 = call_stats.packets_sent_with_ect1;
stats.total_packet_send_delay = call_stats.total_packet_send_delay;
stats.retransmitted_packets_sent = call_stats.retransmitted_packets_sent;
// RTT isn't known until a RTCP report is received. Until then, VoiceEngine
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 06ebdd2..b075940 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -810,6 +810,8 @@
stats.retransmitted_bytes_sent = rtp_stats.retransmitted.payload_bytes;
stats.packetsSent =
rtp_stats.transmitted.packets + rtx_stats.transmitted.packets;
+ stats.packets_sent_with_ect1 = rtp_stats.transmitted.packets_with_ect1 +
+ rtx_stats.transmitted.packets_with_ect1;
stats.total_packet_send_delay = rtp_stats.transmitted.total_packet_delay;
stats.retransmitted_packets_sent = rtp_stats.retransmitted.packets;
stats.report_block_datas = rtp_rtcp_->GetLatestReportBlockData();
diff --git a/audio/channel_send.h b/audio/channel_send.h
index 0ed544a..1706406 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -46,6 +46,8 @@
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedbytessent
uint64_t retransmitted_bytes_sent;
int packetsSent;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-packetssentwithect1
+ int packets_sent_with_ect1;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
TimeDelta total_packet_send_delay = TimeDelta::Zero();
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedpacketssent
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index 2cc5397..851c7ae 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -48,6 +48,8 @@
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedbytessent
uint64_t retransmitted_bytes_sent = 0;
int32_t packets_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-packetssentwithect1
+ int32_t packets_sent_with_ect1 = 0;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
TimeDelta total_packet_send_delay = TimeDelta::Zero();
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedpacketssent
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index 6a34639..17ad9ad 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -366,6 +366,8 @@
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedbytessent
uint64_t retransmitted_bytes_sent = 0;
int packets_sent = 0;
+ // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-packetssentwithect1
+ int packets_sent_with_ect1 = 0;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedpacketssent
uint64_t retransmitted_packets_sent = 0;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-nackcount
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 553020d..0b62de6 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -2514,6 +2514,8 @@
stream_stats.rtp_stats.transmitted.header_bytes +
stream_stats.rtp_stats.transmitted.padding_bytes;
info.packets_sent = stream_stats.rtp_stats.transmitted.packets;
+ info.packets_sent_with_ect1 =
+ stream_stats.rtp_stats.transmitted.packets_with_ect1;
info.total_packet_send_delay +=
stream_stats.rtp_stats.transmitted.total_packet_delay;
info.send_frame_width = stream_stats.width;
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 3690ffb..d5be912 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -1800,6 +1800,7 @@
sinfo.header_and_padding_bytes_sent = stats.header_and_padding_bytes_sent;
sinfo.retransmitted_bytes_sent = stats.retransmitted_bytes_sent;
sinfo.packets_sent = stats.packets_sent;
+ sinfo.packets_sent_with_ect1 = stats.packets_sent_with_ect1;
sinfo.total_packet_send_delay = stats.total_packet_send_delay;
sinfo.retransmitted_packets_sent = stats.retransmitted_packets_sent;
sinfo.packets_lost = stats.packets_lost;
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.cc b/modules/rtp_rtcp/include/rtp_rtcp_defines.cc
index cc06d1b..1ac853b 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.cc
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.cc
@@ -51,12 +51,16 @@
: header_bytes(packet.headers_size()),
payload_bytes(packet.payload_size()),
padding_bytes(packet.padding_size()),
- packets(1) {}
+ packets(1),
+ packets_with_ect1(0) {}
RtpPacketCounter::RtpPacketCounter(const RtpPacketToSend& packet_to_send)
: RtpPacketCounter(static_cast<const RtpPacket&>(packet_to_send)) {
total_packet_delay =
packet_to_send.time_in_send_queue().value_or(TimeDelta::Zero());
+ if (packet_to_send.send_as_ect1()) {
+ ++packets_with_ect1;
+ }
}
void RtpPacketCounter::AddPacket(const RtpPacket& packet) {
@@ -70,6 +74,9 @@
AddPacket(static_cast<const RtpPacket&>(packet_to_send));
total_packet_delay +=
packet_to_send.time_in_send_queue().value_or(TimeDelta::Zero());
+ if (packet_to_send.send_as_ect1()) {
+ ++packets_with_ect1;
+ }
}
} // namespace webrtc
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index a888e4f..2552792 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -268,7 +268,11 @@
struct RtpPacketCounter {
RtpPacketCounter()
- : header_bytes(0), payload_bytes(0), padding_bytes(0), packets(0) {}
+ : header_bytes(0),
+ payload_bytes(0),
+ padding_bytes(0),
+ packets(0),
+ packets_with_ect1(0) {}
explicit RtpPacketCounter(const RtpPacket& packet);
explicit RtpPacketCounter(const RtpPacketToSend& packet_to_send);
@@ -278,6 +282,7 @@
payload_bytes += other.payload_bytes;
padding_bytes += other.padding_bytes;
packets += other.packets;
+ packets_with_ect1 += other.packets_with_ect1;
total_packet_delay += other.total_packet_delay;
}
@@ -285,6 +290,7 @@
return header_bytes == other.header_bytes &&
payload_bytes == other.payload_bytes &&
padding_bytes == other.padding_bytes && packets == other.packets &&
+ packets_with_ect1 == other.packets_with_ect1 &&
total_packet_delay == other.total_packet_delay;
}
@@ -300,6 +306,7 @@
size_t payload_bytes; // Payload bytes, excluding RTP headers and padding.
size_t padding_bytes; // Number of padding bytes.
size_t packets; // Number of packets.
+ size_t packets_with_ect1; // Number of packets with ECT1 flag set to true.
// The total delay of all `packets`. For RtpPacketToSend packets, this is
// `time_in_send_queue()`. For receive packets, this is zero.
TimeDelta total_packet_delay = TimeDelta::Zero();
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 021baae..38c7ba2 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -730,6 +730,8 @@
if (media_sender_info.active.has_value()) {
outbound_stats->active = *media_sender_info.active;
}
+ outbound_stats->packets_sent_with_ect1 =
+ media_sender_info.packets_sent_with_ect1;
}
std::unique_ptr<RTCOutboundRtpStreamStats>
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 8686354..737b71f 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -2523,6 +2523,7 @@
voice_media_info.senders[0].local_stats.push_back(SsrcSenderInfo());
voice_media_info.senders[0].local_stats[0].ssrc = 1;
voice_media_info.senders[0].packets_sent = 2;
+ voice_media_info.senders[0].packets_sent_with_ect1 = 2;
voice_media_info.senders[0].total_packet_send_delay = TimeDelta::Seconds(1);
voice_media_info.senders[0].retransmitted_packets_sent = 20;
voice_media_info.senders[0].payload_bytes_sent = 3;
@@ -2558,6 +2559,7 @@
expected_audio.transport_id = "TTransportName1";
expected_audio.codec_id = "COTTransportName1_42";
expected_audio.packets_sent = 2;
+ expected_audio.packets_sent_with_ect1 = 2;
expected_audio.total_packet_send_delay = 1;
expected_audio.retransmitted_packets_sent = 20;
expected_audio.bytes_sent = 3;
@@ -2592,6 +2594,7 @@
video_media_info.senders[0].plis_received = 3;
video_media_info.senders[0].nacks_received = 4;
video_media_info.senders[0].packets_sent = 5;
+ video_media_info.senders[0].packets_sent_with_ect1 = 3;
video_media_info.senders[0].retransmitted_packets_sent = 50;
video_media_info.senders[0].payload_bytes_sent = 6;
video_media_info.senders[0].header_and_padding_bytes_sent = 12;
@@ -2669,6 +2672,7 @@
expected_video.pli_count = 3;
expected_video.nack_count = 4;
expected_video.packets_sent = 5;
+ expected_video.packets_sent_with_ect1 = 3;
expected_video.retransmitted_packets_sent = 50;
expected_video.bytes_sent = 6;
expected_video.header_bytes_sent = 12;
@@ -2971,6 +2975,7 @@
voice_media_info.senders[0].local_stats.push_back(SsrcSenderInfo());
voice_media_info.senders[0].local_stats[0].ssrc = 1;
voice_media_info.senders[0].packets_sent = 2;
+ voice_media_info.senders[0].packets_sent_with_ect1 = 0;
voice_media_info.senders[0].total_packet_send_delay = TimeDelta::Seconds(0.5);
voice_media_info.senders[0].retransmitted_packets_sent = 20;
voice_media_info.senders[0].payload_bytes_sent = 3;
@@ -3005,6 +3010,7 @@
expected_audio.transport_id = "TTransportName1";
expected_audio.codec_id = "COTTransportName1_42";
expected_audio.packets_sent = 2;
+ expected_audio.packets_sent_with_ect1 = 0;
expected_audio.total_packet_send_delay = 0.5;
expected_audio.retransmitted_packets_sent = 20;
expected_audio.bytes_sent = 3;
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index f2e92dd..3b7f616 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -799,6 +799,8 @@
verifier.TestAttributeIsNonNegative<uint64_t>(
outbound_stream.retransmitted_bytes_sent);
verifier.TestAttributeIsNonNegative<double>(outbound_stream.target_bitrate);
+ verifier.TestAttributeIsNonNegative<uint64_t>(
+ outbound_stream.packets_sent_with_ect1);
if (outbound_stream.kind.has_value() && *outbound_stream.kind == "video") {
verifier.TestAttributeIsDefined(outbound_stream.frames_encoded);
verifier.TestAttributeIsDefined(outbound_stream.key_frames_encoded);
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 5a801cc..7d2bb64 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -324,7 +324,8 @@
AttributeInit("active", &active),
AttributeInit("powerEfficientEncoder", &power_efficient_encoder),
AttributeInit("scalabilityMode", &scalability_mode),
- AttributeInit("rtxSsrc", &rtx_ssrc))
+ AttributeInit("rtxSsrc", &rtx_ssrc),
+ AttributeInit("packetsSentWithEct1", &packets_sent_with_ect1))
// clang-format on
RTCOutboundRtpStreamStats::RTCOutboundRtpStreamStats(std::string id,
diff --git a/test/peer_scenario/tests/l4s_test.cc b/test/peer_scenario/tests/l4s_test.cc
index 0134d77..a706a78 100644
--- a/test/peer_scenario/tests/l4s_test.cc
+++ b/test/peer_scenario/tests/l4s_test.cc
@@ -9,6 +9,8 @@
*/
#include <atomic>
+#include <cstdint>
+#include <optional>
#include <string>
#include "absl/strings/str_cat.h"
@@ -121,6 +123,15 @@
return DataRate::BitsPerSec(*stats[0]->available_outgoing_bitrate);
}
+std::optional<uint64_t> GetPacketsSentWithEct1(
+ const scoped_refptr<const RTCStatsReport>& report) {
+ auto stats = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
+ if (stats.empty()) {
+ return std::nullopt;
+ }
+ return stats[0]->packets_sent_with_ect1;
+}
+
TEST(L4STest, NegotiateAndUseCcfbIfEnabled) {
PeerScenario s(*test_info_);
@@ -303,11 +314,11 @@
feedback_counter.Count(packet);
if (feedback_counter.ect1() > 0) {
seen_ect1_feedback = true;
- RTC_LOG(LS_INFO) << " ect 1" << feedback_counter.ect1();
+ 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();
+ RTC_LOG(LS_INFO) << " not ect: " << feedback_counter.not_ect();
}
});
@@ -331,6 +342,9 @@
EXPECT_TRUE(s.WaitAndProcess(&seen_ect1_feedback, TimeDelta::Seconds(1)));
EXPECT_FALSE(seen_not_ect_feedback);
EXPECT_TRUE(s.WaitAndProcess(&seen_not_ect_feedback, TimeDelta::Seconds(1)));
+ auto packets_sent_with_ect1_stats =
+ GetPacketsSentWithEct1(GetStatsAndProcess(s, caller));
+ EXPECT_EQ(packets_sent_with_ect1_stats, feedback_counter.ect1());
}
TEST(L4STest, SendsEct1AfterRouteChange) {
@@ -416,6 +430,10 @@
s.net()->DisableEndpoint(callee->endpoint(0));
EXPECT_TRUE(
s.WaitAndProcess(&seen_ect1_on_cellular_feedback, TimeDelta::Seconds(5)));
+ auto packets_sent_with_ect1_stats =
+ GetPacketsSentWithEct1(GetStatsAndProcess(s, caller));
+ EXPECT_EQ(packets_sent_with_ect1_stats,
+ wifi_feedback_counter.ect1() + cellular_feedback_counter.ect1());
}
} // namespace