From 13c0be44b3fe295506bda9d99415a200f6b0de7a Mon Sep 17 00:00:00 2001 From: Evan Shrubsole Date: Mon, 31 Oct 2022 11:03:10 +0000 Subject: [PATCH] Add power efficient stats to RTC stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the exposure of power efficient stats to JavaScript are limited as to reduce the fingerprinting surface to getStats, a new RTCStatsMember derivation, RTCLimitedStatsMember, was added in this change. This sets the exposure criteria of the stat on the type, which keeps the size of the RTCStatsMember class the same and allows for extension in the future for new types of stat restrictions. Bug: webrtc:14483 Change-Id: Ib0303050a112441ba2416fd5f004dd8be26b47ca Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/279021 Commit-Queue: Evan Shrubsole Reviewed-by: Henrik Boström Cr-Commit-Position: refs/heads/main@{#38576} --- api/stats/rtcstats_objects.h | 4 ++++ pc/rtc_stats_collector.cc | 8 ++++++++ pc/rtc_stats_collector_unittest.cc | 8 ++++++++ pc/rtc_stats_integrationtest.cc | 10 ++++++++++ stats/rtcstats_objects.cc | 8 ++++++-- 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index b62b0aa5f7..42553c1b7c 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -496,6 +496,8 @@ class RTC_EXPORT RTCInboundRTPStreamStats final // TimingFrameInfo::ToString(). // TODO(https://crbug.com/webrtc/14586): Unship or standardize this metric. RTCStatsMember goog_timing_frame_info; + RTCRestrictedStatsMember + power_efficient_decoder; // Non-standard audio metrics. RTCNonStandardStatsMember jitter_buffer_flushes; RTCNonStandardStatsMember delayed_packet_outage_samples; @@ -553,6 +555,8 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { RTCStatsMember nack_count; RTCStatsMember qp_sum; RTCStatsMember active; + RTCRestrictedStatsMember + power_efficient_encoder; }; // https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict* diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 07b4f95cd3..e4a541805a 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -654,6 +654,10 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo( inbound_video->decoder_implementation = video_receiver_info.decoder_implementation_name; } + if (video_receiver_info.power_efficient_decoder.has_value()) { + inbound_video->power_efficient_decoder = + video_receiver_info.power_efficient_decoder.value(); + } } // Provides the media independent counters and information (both audio and @@ -784,6 +788,10 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo( if (video_sender_info.rid.has_value()) { outbound_video->rid = *video_sender_info.rid; } + if (video_sender_info.power_efficient_encoder.has_value()) { + outbound_video->power_efficient_encoder = + video_sender_info.power_efficient_encoder.value(); + } } std::unique_ptr diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 742f87bdba..a3a17801c1 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -2597,6 +2597,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { absl::nullopt; video_media_info.receivers[0].decoder_implementation_name = ""; video_media_info.receivers[0].min_playout_delay_ms = 50; + video_media_info.receivers[0].power_efficient_decoder = false; // Note: these two values intentionally differ, // only the decoded one should show up. @@ -2661,6 +2662,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { // `expected_video.decoder_implementation` should be undefined. expected_video.min_playout_delay = 0.05; expected_video.frames_per_second = 5; + expected_video.power_efficient_decoder = false; ASSERT_TRUE(report->Get(expected_video.id())); EXPECT_EQ( @@ -2678,6 +2680,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.estimated_playout_timestamp = 1234; video_media_info.receivers[0].decoder_implementation_name = "libfoodecoder"; expected_video.decoder_implementation = "libfoodecoder"; + video_media_info.receivers[0].power_efficient_decoder = true; + expected_video.power_efficient_decoder = true; video_media_channel->SetStats(video_media_info); report = stats_->GetFreshStatsReport(); @@ -2824,6 +2828,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.senders[0].qp_sum = absl::nullopt; video_media_info.senders[0].content_type = VideoContentType::UNSPECIFIED; video_media_info.senders[0].encoder_implementation_name = ""; + video_media_info.senders[0].power_efficient_encoder = false; video_media_info.senders[0].send_frame_width = 200; video_media_info.senders[0].send_frame_height = 100; video_media_info.senders[0].framerate_sent = 10; @@ -2888,6 +2893,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { expected_video.frames_sent = 5; expected_video.huge_frames_sent = 2; expected_video.active = false; + expected_video.power_efficient_encoder = false; // `expected_video.content_type` should be undefined. // `expected_video.qp_sum` should be undefined. // `expected_video.encoder_implementation` should be undefined. @@ -2905,6 +2911,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.senders[0].encoder_implementation_name = "libfooencoder"; video_media_info.aggregated_senders[0] = video_media_info.senders[0]; expected_video.encoder_implementation = "libfooencoder"; + video_media_info.senders[0].power_efficient_encoder = true; + expected_video.power_efficient_encoder = true; video_media_channel->SetStats(video_media_info); report = stats_->GetFreshStatsReport(); diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index c7d31529f5..c63083dbfc 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -750,9 +750,15 @@ class RTCStatsReportVerifier { if (inbound_stream.kind.is_defined() && *inbound_stream.kind == "video") { verifier.TestMemberIsNonNegative(inbound_stream.qp_sum); verifier.TestMemberIsDefined(inbound_stream.decoder_implementation); + verifier.TestMemberIsDefined(inbound_stream.power_efficient_decoder); + EXPECT_EQ(inbound_stream.power_efficient_decoder.exposure_criteria(), + StatExposureCriteria::kHardwareCapability); } else { verifier.TestMemberIsUndefined(inbound_stream.qp_sum); verifier.TestMemberIsUndefined(inbound_stream.decoder_implementation); + verifier.TestMemberIsUndefined(inbound_stream.power_efficient_decoder); + EXPECT_EQ(inbound_stream.power_efficient_decoder.exposure_criteria(), + StatExposureCriteria::kHardwareCapability); } verifier.TestMemberIsNonNegative(inbound_stream.packets_received); if (inbound_stream.kind.is_defined() && *inbound_stream.kind == "audio") { @@ -958,6 +964,9 @@ class RTCStatsReportVerifier { // this to be present. verifier.MarkMemberTested(outbound_stream.content_type, true); verifier.TestMemberIsDefined(outbound_stream.encoder_implementation); + verifier.TestMemberIsDefined(outbound_stream.power_efficient_encoder); + EXPECT_EQ(outbound_stream.power_efficient_encoder.exposure_criteria(), + StatExposureCriteria::kHardwareCapability); // Unless an implementation-specific amount of time has passed and at // least one frame has been encoded, undefined is reported. Because it // is hard to tell what is the case here, we treat FPS as optional. @@ -989,6 +998,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(outbound_stream.content_type); // TODO(hbos): Implement for audio as well. verifier.TestMemberIsUndefined(outbound_stream.encoder_implementation); + verifier.TestMemberIsUndefined(outbound_stream.power_efficient_encoder); verifier.TestMemberIsUndefined(outbound_stream.rid); verifier.TestMemberIsUndefined(outbound_stream.frames_per_second); verifier.TestMemberIsUndefined(outbound_stream.frame_height); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 8a6b6dfaa0..7c20b87562 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -561,6 +561,7 @@ WEBRTC_RTCSTATS_IMPL( &nack_count, &qp_sum, &goog_timing_frame_info, + &power_efficient_decoder, &jitter_buffer_flushes, &delayed_packet_outage_samples, &relative_packet_arrival_delay, @@ -625,6 +626,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(std::string&& id, nack_count("nackCount"), qp_sum("qpSum"), goog_timing_frame_info("googTimingFrameInfo"), + power_efficient_decoder("powerEfficientDecoder"), jitter_buffer_flushes( "jitterBufferFlushes", {NonStandardGroupId::kRtcAudioJitterBufferMaxPackets}), @@ -675,7 +677,8 @@ WEBRTC_RTCSTATS_IMPL( &pli_count, &nack_count, &qp_sum, - &active) + &active, + &power_efficient_encoder) // clang-format on RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(const std::string& id, @@ -715,7 +718,8 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id, pli_count("pliCount"), nack_count("nackCount"), qp_sum("qpSum"), - active("active") {} + active("active"), + power_efficient_encoder("powerEfficientEncoder") {} RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( const RTCOutboundRTPStreamStats& other) = default;