From c3ed630560f43d5af2a2e0b8710625a2874bb919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85sa=20Persson?= Date: Thu, 16 Nov 2017 14:04:52 +0100 Subject: [PATCH] Add stats googHasEnteredLowResolution. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indicates if the forced sw fallback has had an effect (or would have had an effect if it had been enabled). Bug: webrtc:6634 Change-Id: I574b9001a2fae650fb894a1caa0d0f84257658e3 Reviewed-on: https://webrtc-review.googlesource.com/23300 Reviewed-by: Stefan Holmer Reviewed-by: Rasmus Brandt Commit-Queue: Åsa Persson Cr-Commit-Position: refs/heads/master@{#20729} --- api/statstypes.cc | 2 + api/statstypes.h | 1 + call/video_send_stream.h | 1 + media/base/mediachannel.h | 2 + media/engine/webrtcvideoengine.cc | 1 + pc/statscollector.cc | 3 ++ video/send_statistics_proxy.cc | 64 ++++++++++++++++++++----- video/send_statistics_proxy.h | 10 ++++ video/send_statistics_proxy_unittest.cc | 50 ++++++++++++++++++- video/video_stream_encoder.cc | 20 ++++++-- 10 files changed, 135 insertions(+), 19 deletions(-) diff --git a/api/statstypes.cc b/api/statstypes.cc index 940fb73c84..ff70bac442 100644 --- a/api/statstypes.cc +++ b/api/statstypes.cc @@ -547,6 +547,8 @@ const char* StatsReport::Value::display_name() const { return "googFrameWidthReceived"; case kStatsValueNameFrameWidthSent: return "googFrameWidthSent"; + case kStatsValueNameHasEnteredLowResolution: + return "googHasEnteredLowResolution"; case kStatsValueNameInitiator: return "googInitiator"; case kStatsValueNameInterframeDelayMaxMs: diff --git a/api/statstypes.h b/api/statstypes.h index 9e7f08c645..80f0eb5ae6 100644 --- a/api/statstypes.h +++ b/api/statstypes.h @@ -185,6 +185,7 @@ class StatsReport { kStatsValueNameFrameWidthInput, kStatsValueNameFrameWidthReceived, kStatsValueNameFrameWidthSent, + kStatsValueNameHasEnteredLowResolution, kStatsValueNameInitiator, kStatsValueNameInterframeDelayMaxMs, // Max over last 10 seconds. kStatsValueNameIssuerId, diff --git a/call/video_send_stream.h b/call/video_send_stream.h index 3bdb8ce813..e57307d3a9 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -85,6 +85,7 @@ class VideoSendStream { // CPU/quality adaptation. int number_of_cpu_adapt_changes = 0; int number_of_quality_adapt_changes = 0; + bool has_entered_low_resolution = false; std::map substreams; webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED; diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h index b101dc7ebe..0b84ff79cc 100644 --- a/media/base/mediachannel.h +++ b/media/base/mediachannel.h @@ -723,6 +723,7 @@ struct VideoSenderInfo : public MediaSenderInfo { avg_encode_ms(0), encode_usage_percent(0), frames_encoded(0), + has_entered_low_resolution(false), content_type(webrtc::VideoContentType::UNSPECIFIED) {} std::vector ssrc_groups; @@ -743,6 +744,7 @@ struct VideoSenderInfo : public MediaSenderInfo { int avg_encode_ms; int encode_usage_percent; uint32_t frames_encoded; + bool has_entered_low_resolution; rtc::Optional qp_sum; webrtc::VideoContentType content_type; }; diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc index d2457f8734..7d5e2f5159 100644 --- a/media/engine/webrtcvideoengine.cc +++ b/media/engine/webrtcvideoengine.cc @@ -1975,6 +1975,7 @@ VideoSenderInfo WebRtcVideoChannel::WebRtcVideoSendStream::GetVideoSenderInfo( info.adapt_changes = stats.number_of_cpu_adapt_changes; info.adapt_reason = stats.cpu_limited_resolution ? ADAPTREASON_CPU : ADAPTREASON_NONE; + info.has_entered_low_resolution = stats.has_entered_low_resolution; // Get bandwidth limitation info from stream_->GetStats(). // Input resolution (output from video_adapter) can be further scaled down or diff --git a/pc/statscollector.cc b/pc/statscollector.cc index c81b36821f..95201769a6 100644 --- a/pc/statscollector.cc +++ b/pc/statscollector.cc @@ -317,6 +317,9 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { (info.adapt_reason & 0x2) > 0); report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution, (info.adapt_reason & 0x1) > 0); + report->AddBoolean(StatsReport::kStatsValueNameHasEnteredLowResolution, + info.has_entered_low_resolution); + if (info.qp_sum) report->AddInt(StatsReport::kStatsValueNameQpSum, *info.qp_sum); diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc index cc7d034db1..71c9348e3a 100644 --- a/video/send_statistics_proxy.cc +++ b/video/send_statistics_proxy.cc @@ -85,29 +85,37 @@ bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info) { codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx); } -rtc::Optional GetFallbackMaxPixelsFromFieldTrial() { - if (!webrtc::field_trial::IsEnabled(kVp8ForcedFallbackEncoderFieldTrial)) - return rtc::Optional(); - - std::string group = - webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial); +rtc::Optional GetFallbackMaxPixels(const std::string& group) { if (group.empty()) return rtc::Optional(); int min_pixels; int max_pixels; int min_bps; - if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels, - &min_bps) != 3) { + if (sscanf(group.c_str(), "-%d,%d,%d", &min_pixels, &max_pixels, &min_bps) != + 3) { return rtc::Optional(); } - if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels || - min_bps <= 0) { - return rtc::Optional(); // Do not log stats. - } + if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels) + return rtc::Optional(); + return rtc::Optional(max_pixels); } + +rtc::Optional GetFallbackMaxPixelsIfFieldTrialEnabled() { + std::string group = + webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial); + return (group.find("Enabled") == 0) ? GetFallbackMaxPixels(group.substr(7)) + : rtc::Optional(); +} + +rtc::Optional GetFallbackMaxPixelsIfFieldTrialDisabled() { + std::string group = + webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial); + return (group.find("Disabled") == 0) ? GetFallbackMaxPixels(group.substr(8)) + : rtc::Optional(); +} } // namespace @@ -120,7 +128,8 @@ SendStatisticsProxy::SendStatisticsProxy( : clock_(clock), payload_name_(config.encoder_settings.payload_name), rtp_config_(config.rtp), - fallback_max_pixels_(GetFallbackMaxPixelsFromFieldTrial()), + fallback_max_pixels_(GetFallbackMaxPixelsIfFieldTrialEnabled()), + fallback_max_pixels_disabled_(GetFallbackMaxPixelsIfFieldTrialDisabled()), content_type_(content_type), start_ms_(clock->TimeInMilliseconds()), encode_time_(kEncodeTimeWeigthFactor), @@ -729,6 +738,8 @@ void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) { void SendStatisticsProxy::UpdateEncoderFallbackStats( const CodecSpecificInfo* codec_info, int pixels) { + UpdateFallbackDisabledStats(codec_info, pixels); + if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) { return; } @@ -755,6 +766,7 @@ void SendStatisticsProxy::UpdateEncoderFallbackStats( fallback_info->is_possible = false; return; } + stats_.has_entered_low_resolution = true; ++fallback_info->on_off_events; } @@ -772,6 +784,32 @@ void SendStatisticsProxy::UpdateEncoderFallbackStats( fallback_info->last_update_ms.emplace(now_ms); } +void SendStatisticsProxy::UpdateFallbackDisabledStats( + const CodecSpecificInfo* codec_info, + int pixels) { + if (!fallback_max_pixels_disabled_ || + !uma_container_->fallback_info_disabled_.is_possible || + stats_.has_entered_low_resolution) { + return; + } + + if (!IsForcedFallbackPossible(codec_info) || + strcmp(codec_info->codec_name, kVp8SwCodecName) == 0) { + uma_container_->fallback_info_disabled_.is_possible = false; + return; + } + + if (pixels <= *fallback_max_pixels_disabled_ || + uma_container_->fallback_info_disabled_.min_pixel_limit_reached) { + stats_.has_entered_low_resolution = true; + } +} + +void SendStatisticsProxy::OnMinPixelLimitReached() { + rtc::CritScope lock(&crit_); + uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true; +} + void SendStatisticsProxy::OnSendEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_info) { diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h index a543d92643..094e78fa71 100644 --- a/video/send_statistics_proxy.h +++ b/video/send_statistics_proxy.h @@ -73,6 +73,7 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, void OnQualityAdaptationChanged( const VideoStreamEncoder::AdaptCounts& cpu_counts, const VideoStreamEncoder::AdaptCounts& quality_counts); + void OnMinPixelLimitReached(); void OnSuspendChange(bool is_suspended); void OnInactiveSsrc(uint32_t ssrc); @@ -163,6 +164,10 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, rtc::Optional last_update_ms; const int max_frame_diff_ms = 2000; }; + struct FallbackEncoderInfoDisabled { + bool is_possible = true; + bool min_pixel_limit_reached = false; + }; struct StatsTimer { void Start(int64_t now_ms); void Stop(int64_t now_ms); @@ -208,11 +213,15 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, void UpdateEncoderFallbackStats(const CodecSpecificInfo* codec_info, int pixels) RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); + void UpdateFallbackDisabledStats(const CodecSpecificInfo* codec_info, + int pixels) + RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); Clock* const clock_; const std::string payload_name_; const VideoSendStream::Config::Rtp rtp_config_; const rtc::Optional fallback_max_pixels_; + const rtc::Optional fallback_max_pixels_disabled_; rtc::CriticalSection crit_; VideoEncoderConfig::ContentType content_type_ RTC_GUARDED_BY(crit_); const int64_t start_ms_; @@ -273,6 +282,7 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, TargetRateUpdates target_rate_updates_; BoolSampleCounter fallback_active_counter_; FallbackEncoderInfo fallback_info_; + FallbackEncoderInfoDisabled fallback_info_disabled_; ReportBlockStats report_block_stats_; const VideoSendStream::Stats start_stats_; EncodedFrameMap encoded_frames_; diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc index 881aa7c158..a204a469aa 100644 --- a/video/send_statistics_proxy_unittest.cc +++ b/video/send_statistics_proxy_unittest.cc @@ -1867,7 +1867,8 @@ class ForcedFallbackTest : public SendStatisticsProxyTest { class ForcedFallbackDisabled : public ForcedFallbackTest { public: ForcedFallbackDisabled() - : ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Disabled/") {} + : ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Disabled-1," + + std::to_string(kWidth * kHeight) + ",3/") {} }; class ForcedFallbackEnabled : public ForcedFallbackTest { @@ -1886,6 +1887,7 @@ TEST_F(ForcedFallbackEnabled, StatsNotUpdatedIfMinRunTimeHasNotPassed) { TEST_F(ForcedFallbackEnabled, StatsUpdated) { InsertEncodedFrames(kMinFrames, kFrameIntervalMs); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); statistics_proxy_.reset(); EXPECT_EQ(1, metrics::NumSamples(kPrefix + "FallbackTimeInPercent.Vp8")); EXPECT_EQ(1, metrics::NumEvents(kPrefix + "FallbackTimeInPercent.Vp8", 0)); @@ -1924,11 +1926,50 @@ TEST_F(ForcedFallbackDisabled, StatsNotUpdatedIfNoFieldTrial) { EXPECT_EQ(0, metrics::NumSamples(kPrefix + "FallbackChangesPerMinute.Vp8")); } +TEST_F(ForcedFallbackDisabled, EnteredLowResolutionSetIfAtMaxPixels) { + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + +TEST_F(ForcedFallbackEnabled, EnteredLowResolutionNotSetIfNotLibvpx) { + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + +TEST_F(ForcedFallbackEnabled, EnteredLowResolutionSetIfLibvpx) { + codec_info_.codec_name = "libvpx"; + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + +TEST_F(ForcedFallbackDisabled, EnteredLowResolutionNotSetIfAboveMaxPixels) { + encoded_image_._encodedWidth = kWidth + 1; + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + +TEST_F(ForcedFallbackDisabled, EnteredLowResolutionNotSetIfLibvpx) { + codec_info_.codec_name = "libvpx"; + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + +TEST_F(ForcedFallbackDisabled, + EnteredLowResolutionSetIfOnMinPixelLimitReached) { + encoded_image_._encodedWidth = kWidth + 1; + statistics_proxy_->OnMinPixelLimitReached(); + InsertEncodedFrames(1, kFrameIntervalMs); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); +} + TEST_F(ForcedFallbackEnabled, OneFallbackEvent) { // One change. Video: 20000 ms, fallback: 5000 ms (25%). + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); InsertEncodedFrames(15, 1000); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); codec_info_.codec_name = "libvpx"; InsertEncodedFrames(5, 1000); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); statistics_proxy_.reset(); EXPECT_EQ(1, metrics::NumSamples(kPrefix + "FallbackTimeInPercent.Vp8")); @@ -1943,16 +1984,21 @@ TEST_F(ForcedFallbackEnabled, ThreeFallbackEvents) { // Three changes. Video: 60000 ms, fallback: 15000 ms (25%). InsertEncodedFrames(10, 1000); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); codec_info_.codec_name = "libvpx"; InsertEncodedFrames(15, 500); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); codec_info_.codec_name = "notlibvpx"; InsertEncodedFrames(20, 1000); InsertEncodedFrames(3, kMaxFrameDiffMs); // Should not be included. InsertEncodedFrames(10, 1000); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); codec_info_.codec_name = "notlibvpx2"; InsertEncodedFrames(10, 500); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); codec_info_.codec_name = "libvpx"; InsertEncodedFrames(15, 500); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); statistics_proxy_.reset(); EXPECT_EQ(1, metrics::NumSamples(kPrefix + "FallbackTimeInPercent.Vp8")); @@ -1966,6 +2012,7 @@ TEST_F(ForcedFallbackEnabled, NoFallbackIfAboveMaxPixels) { codec_info_.codec_name = "libvpx"; InsertEncodedFrames(kMinFrames, kFrameIntervalMs); + EXPECT_FALSE(statistics_proxy_->GetStats().has_entered_low_resolution); statistics_proxy_.reset(); EXPECT_EQ(0, metrics::NumSamples(kPrefix + "FallbackTimeInPercent.Vp8")); EXPECT_EQ(0, metrics::NumSamples(kPrefix + "FallbackChangesPerMinute.Vp8")); @@ -1976,6 +2023,7 @@ TEST_F(ForcedFallbackEnabled, FallbackIfAtMaxPixels) { codec_info_.codec_name = "libvpx"; InsertEncodedFrames(kMinFrames, kFrameIntervalMs); + EXPECT_TRUE(statistics_proxy_->GetStats().has_entered_low_resolution); statistics_proxy_.reset(); EXPECT_EQ(1, metrics::NumSamples(kPrefix + "FallbackTimeInPercent.Vp8")); EXPECT_EQ(1, metrics::NumSamples(kPrefix + "FallbackChangesPerMinute.Vp8")); diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 6c59670556..77945dc0b4 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -228,7 +228,9 @@ class VideoStreamEncoder::VideoSourceProxy { source_->AddOrUpdateSink(video_stream_encoder_, sink_wants_); } - bool RequestResolutionLowerThan(int pixel_count, int min_pixels_per_frame) { + bool RequestResolutionLowerThan(int pixel_count, + int min_pixels_per_frame, + bool* min_pixels_reached) { // Called on the encoder task queue. rtc::CritScope lock(&crit_); if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) { @@ -239,8 +241,11 @@ class VideoStreamEncoder::VideoSourceProxy { // The input video frame size will have a resolution less than or equal to // |max_pixel_count| depending on how the source can scale the frame size. const int pixels_wanted = (pixel_count * 3) / 5; - if (pixels_wanted < min_pixels_per_frame || - pixels_wanted >= sink_wants_.max_pixel_count) { + if (pixels_wanted >= sink_wants_.max_pixel_count) { + return false; + } + if (pixels_wanted < min_pixels_per_frame) { + *min_pixels_reached = true; return false; } RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: " @@ -977,15 +982,20 @@ void VideoStreamEncoder::AdaptDown(AdaptReason reason) { // Scale down resolution. FALLTHROUGH(); } - case VideoSendStream::DegradationPreference::kMaintainFramerate: + case VideoSendStream::DegradationPreference::kMaintainFramerate: { // Scale down resolution. + bool min_pixels_reached = false; if (!source_proxy_->RequestResolutionLowerThan( adaptation_request.input_pixel_count_, - settings_.encoder->GetScalingSettings().min_pixels_per_frame)) { + settings_.encoder->GetScalingSettings().min_pixels_per_frame, + &min_pixels_reached)) { + if (min_pixels_reached) + stats_proxy_->OnMinPixelLimitReached(); return; } GetAdaptCounter().IncrementResolution(reason); break; + } case VideoSendStream::DegradationPreference::kMaintainResolution: { // Scale down framerate. const int requested_framerate = source_proxy_->RequestFramerateLowerThan(