From cc62b16658f247524ed6bebdab45737c0349ac71 Mon Sep 17 00:00:00 2001 From: Evan Shrubsole Date: Mon, 9 Sep 2019 11:26:45 +0200 Subject: [PATCH] Add qualityLimitationResolutionChanges stat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the stat qualityLimitationResolutionChanges [1]. [1] https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges Bug: webrtc:10935 Change-Id: I391f2be5958a96b442e32c40ab7043752f3f71dd Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/150882 Reviewed-by: Erik Språng Reviewed-by: Henrik Boström Reviewed-by: Stefan Holmer Commit-Queue: Evan Shrubsole Cr-Commit-Position: refs/heads/master@{#29113} --- api/stats/rtcstats_objects.h | 2 + api/video/BUILD.gn | 2 + api/video/video_stream_encoder_observer.h | 6 + call/video_send_stream.h | 2 + media/base/media_channel.h | 2 + media/engine/webrtc_video_engine.cc | 2 + pc/rtc_stats_collector.cc | 2 + pc/rtc_stats_collector_unittest.cc | 2 + pc/rtc_stats_integrationtest.cc | 4 + stats/rtcstats_objects.cc | 5 + video/BUILD.gn | 2 + video/quality_limitation_reason_tracker.h | 7 +- video/send_statistics_proxy.cc | 44 ++++++ video/send_statistics_proxy.h | 12 ++ video/send_statistics_proxy_unittest.cc | 165 ++++++++++++++++++++++ video/video_stream_encoder.cc | 5 + 16 files changed, 262 insertions(+), 2 deletions(-) diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 5e8df330cb..5fab85e6e3 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -481,6 +481,8 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { // qualityLimitationDurations. Requires RTCStatsMember support for // "record", see https://crbug.com/webrtc/10685. RTCStatsMember quality_limitation_reason; + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges + RTCStatsMember quality_limitation_resolution_changes; // https://henbos.github.io/webrtc-provisional-stats/#dom-rtcoutboundrtpstreamstats-contenttype RTCStatsMember content_type; // TODO(hbos): This is only implemented for video; implement it for audio as diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn index d5fa370958..d988cb664e 100644 --- a/api/video/BUILD.gn +++ b/api/video/BUILD.gn @@ -223,8 +223,10 @@ rtc_source_set("video_stream_encoder") { ] deps = [ + ":video_bitrate_allocation", ":video_bitrate_allocator", ":video_bitrate_allocator_factory", + ":video_codec_constants", ":video_frame", "..:rtp_parameters", "../:fec_controller_api", diff --git a/api/video/video_stream_encoder_observer.h b/api/video/video_stream_encoder_observer.h index 98b5cfcb2c..49531ae539 100644 --- a/api/video/video_stream_encoder_observer.h +++ b/api/video/video_stream_encoder_observer.h @@ -15,6 +15,8 @@ #include #include "absl/types/optional.h" +#include "api/video/video_bitrate_allocation.h" +#include "api/video/video_codec_constants.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_config.h" @@ -88,6 +90,10 @@ class VideoStreamEncoderObserver : public CpuOveruseMetricsObserver { virtual void OnSuspendChange(bool is_suspended) = 0; + virtual void OnBitrateAllocationUpdated( + const VideoCodec& codec, + const VideoBitrateAllocation& allocation) {} + // TODO(nisse): VideoStreamEncoder wants to query the stats, which makes this // not a pure observer. GetInputFrameRate is needed for the cpu adaptation, so // can be deleted if that responsibility is moved out to a VideoStreamAdaptor diff --git a/call/video_send_stream.h b/call/video_send_stream.h index c715863651..2c31de0f8c 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -99,6 +99,8 @@ class VideoSendStream { QualityLimitationReason::kNone; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations std::map quality_limitation_durations_ms; + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges + uint32_t quality_limitation_resolution_changes = 0; // Total number of times resolution as been requested to be changed due to // CPU/quality adaptation. int number_of_cpu_adapt_changes = 0; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 0f502d3438..9cc787690d 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -562,6 +562,8 @@ struct VideoSenderInfo : public MediaSenderInfo { // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations std::map quality_limitation_durations_ms; + // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges + uint32_t quality_limitation_resolution_changes = 0; int avg_encode_ms = 0; int encode_usage_percent = 0; uint32_t frames_encoded = 0; diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 481567f6b6..4fbb2c8277 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2343,6 +2343,8 @@ VideoSenderInfo WebRtcVideoChannel::WebRtcVideoSendStream::GetVideoSenderInfo( info.quality_limitation_reason = stats.quality_limitation_reason; info.quality_limitation_durations_ms = stats.quality_limitation_durations_ms; + info.quality_limitation_resolution_changes = + stats.quality_limitation_resolution_changes; info.encoder_implementation_name = stats.encoder_implementation_name; info.ssrc_groups = ssrc_groups_; info.framerate_input = stats.input_frame_rate; diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 9fd0df03c0..eba54f6e26 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -398,6 +398,8 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo( outbound_video->quality_limitation_reason = QualityLimitationReasonToRTCQualityLimitationReason( video_sender_info.quality_limitation_reason); + outbound_video->quality_limitation_resolution_changes = + video_sender_info.quality_limitation_resolution_changes; // TODO(https://crbug.com/webrtc/10529): When info's |content_info| is // optional, support the "unspecified" value. if (video_sender_info.content_type == VideoContentType::SCREENSHARE) diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 2efb7e247a..02382cbdce 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1966,6 +1966,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.senders[0].total_packet_send_delay_ms = 10000; video_media_info.senders[0].quality_limitation_reason = QualityLimitationReason::kBandwidth; + video_media_info.senders[0].quality_limitation_resolution_changes = 56u; 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 = ""; @@ -2014,6 +2015,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { expected_video.total_encoded_bytes_target = 1234; expected_video.total_packet_send_delay = 10.0; expected_video.quality_limitation_reason = "bandwidth"; + expected_video.quality_limitation_resolution_changes = 56u; // |expected_video.content_type| should be undefined. // |expected_video.qp_sum| should be undefined. // |expected_video.encoder_implementation| should be undefined. diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index c36566a84c..29f06511c9 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -869,6 +869,8 @@ class RTCStatsReportVerifier { verifier.TestMemberIsNonNegative( outbound_stream.total_packet_send_delay); verifier.TestMemberIsDefined(outbound_stream.quality_limitation_reason); + verifier.TestMemberIsNonNegative( + outbound_stream.quality_limitation_resolution_changes); // The integration test is not set up to test screen share; don't require // this to be present. verifier.MarkMemberTested(outbound_stream.content_type, true); @@ -882,6 +884,8 @@ class RTCStatsReportVerifier { // TODO(https://crbug.com/webrtc/10635): Implement for audio as well. verifier.TestMemberIsUndefined(outbound_stream.total_packet_send_delay); verifier.TestMemberIsUndefined(outbound_stream.quality_limitation_reason); + verifier.TestMemberIsUndefined( + outbound_stream.quality_limitation_resolution_changes); verifier.TestMemberIsUndefined(outbound_stream.content_type); // TODO(hbos): Implement for audio as well. verifier.TestMemberIsUndefined(outbound_stream.encoder_implementation); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 9ecb2a5622..3f8d7521f3 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -694,6 +694,7 @@ WEBRTC_RTCSTATS_IMPL( &total_encoded_bytes_target, &total_packet_send_delay, &quality_limitation_reason, + &quality_limitation_resolution_changes, &content_type, &encoder_implementation) // clang-format on @@ -717,6 +718,8 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id, total_encoded_bytes_target("totalEncodedBytesTarget"), total_packet_send_delay("totalPacketSendDelay"), quality_limitation_reason("qualityLimitationReason"), + quality_limitation_resolution_changes( + "qualityLimitationResolutionChanges"), content_type("contentType"), encoder_implementation("encoderImplementation") {} @@ -735,6 +738,8 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( total_encoded_bytes_target(other.total_encoded_bytes_target), total_packet_send_delay(other.total_packet_send_delay), quality_limitation_reason(other.quality_limitation_reason), + quality_limitation_resolution_changes( + other.quality_limitation_resolution_changes), content_type(other.content_type), encoder_implementation(other.encoder_implementation) {} diff --git a/video/BUILD.gn b/video/BUILD.gn index 4679153100..e4925a2d5e 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -63,6 +63,7 @@ rtc_static_library("video") { "../api/video:encoded_image", "../api/video:video_bitrate_allocation", "../api/video:video_bitrate_allocator", + "../api/video:video_codec_constants", "../api/video:video_frame", "../api/video:video_frame_i420", "../api/video:video_rtp_headers", @@ -192,6 +193,7 @@ rtc_source_set("video_stream_encoder_impl") { "../api/video:video_bitrate_allocation", "../api/video:video_bitrate_allocator", "../api/video:video_bitrate_allocator_factory", + "../api/video:video_codec_constants", "../api/video:video_frame", "../api/video:video_frame_i420", "../api/video:video_rtp_headers", diff --git a/video/quality_limitation_reason_tracker.h b/video/quality_limitation_reason_tracker.h index bd0189981e..1fbd71ffc4 100644 --- a/video/quality_limitation_reason_tracker.h +++ b/video/quality_limitation_reason_tracker.h @@ -21,12 +21,15 @@ namespace webrtc { // A tracker of quality limitation reasons. The quality limitation reason is the // primary reason for limiting resolution and/or framerate (such as CPU or // bandwidth limitations). The tracker keeps track of the current reason and the -// duration of time spent in each reason. See qualityLimitationReason[1] and -// qualityLimitationDurations[2] in the webrtc-stats spec. +// duration of time spent in each reason. See qualityLimitationReason[1], +// qualityLimitationDurations[2], and qualityLimitationResolutionChanges[3] in +// the webrtc-stats spec. // [1] // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationreason // [2] // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations +// [3] +// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges class QualityLimitationReasonTracker { public: // The caller is responsible for making sure |clock| outlives the tracker. diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc index c2aab059c5..5bf3427c25 100644 --- a/video/send_statistics_proxy.cc +++ b/video/send_statistics_proxy.cc @@ -11,11 +11,15 @@ #include "video/send_statistics_proxy.h" #include +#include #include #include #include #include "absl/algorithm/container.h" +#include "api/video/video_codec_constants.h" +#include "api/video/video_codec_type.h" +#include "api/video_codecs/video_codec.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -140,6 +144,9 @@ SendStatisticsProxy::SendStatisticsProxy( quality_limitation_reason_tracker_(clock_), media_byte_rate_tracker_(kBucketSizeMs, kBucketCount), encoded_frame_rate_tracker_(kBucketSizeMs, kBucketCount), + last_num_spatial_layers_(0), + last_num_simulcast_streams_(0), + last_spatial_layer_use_{}, uma_container_( new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) { } @@ -1104,6 +1111,43 @@ void SendStatisticsProxy::UpdateAdaptationStats( // when it is polled; it is updated in SendStatisticsProxy::GetStats(). } +void SendStatisticsProxy::OnBitrateAllocationUpdated( + const VideoCodec& codec, + const VideoBitrateAllocation& allocation) { + int num_spatial_layers = 0; + for (int i = 0; i < kMaxSpatialLayers; i++) { + if (codec.spatialLayers[i].active) { + num_spatial_layers++; + } + } + int num_simulcast_streams = 0; + for (int i = 0; i < kMaxSimulcastStreams; i++) { + if (codec.simulcastStream[i].active) { + num_simulcast_streams++; + } + } + + std::array spatial_layers; + for (int i = 0; i < kMaxSpatialLayers; i++) { + spatial_layers[i] = (allocation.GetSpatialLayerSum(i) > 0); + } + + rtc::CritScope lock(&crit_); + + if (spatial_layers != last_spatial_layer_use_) { + // If the number of spatial layers has changed, the resolution change is + // not due to quality limitations, it is because the configuration + // changed. + if (last_num_spatial_layers_ == num_spatial_layers && + last_num_simulcast_streams_ == num_simulcast_streams) { + ++stats_.quality_limitation_resolution_changes; + } + last_spatial_layer_use_ = spatial_layers; + } + last_num_spatial_layers_ = num_spatial_layers; + last_num_simulcast_streams_ = num_simulcast_streams; +} + // TODO(asapersson): Include fps changes. void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() { rtc::CritScope lock(&crit_); diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h index 5280aad797..bc7655679e 100644 --- a/video/send_statistics_proxy.h +++ b/video/send_statistics_proxy.h @@ -11,12 +11,15 @@ #ifndef VIDEO_SEND_STATISTICS_PROXY_H_ #define VIDEO_SEND_STATISTICS_PROXY_H_ +#include #include #include #include #include +#include "api/video/video_codec_constants.h" #include "api/video/video_stream_encoder_observer.h" +#include "api/video_codecs/video_encoder_config.h" #include "call/video_send_stream.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/video_coding/include/video_codec_interface.h" @@ -70,6 +73,10 @@ class SendStatisticsProxy : public VideoStreamEncoderObserver, const AdaptationSteps& cpu_counts, const AdaptationSteps& quality_counts) override; + void OnBitrateAllocationUpdated( + const VideoCodec& codec, + const VideoBitrateAllocation& allocation) override; + void OnMinPixelLimitReached() override; void OnInitialQualityResolutionAdaptDown() override; @@ -251,6 +258,11 @@ class SendStatisticsProxy : public VideoStreamEncoderObserver, absl::optional last_outlier_timestamp_ RTC_GUARDED_BY(crit_); + int last_num_spatial_layers_ RTC_GUARDED_BY(crit_); + int last_num_simulcast_streams_ RTC_GUARDED_BY(crit_); + std::array last_spatial_layer_use_ + RTC_GUARDED_BY(crit_); + struct EncoderChangeEvent { std::string previous_encoder_implementation; std::string new_encoder_implementation; diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc index 928bc8b701..47ce644ddf 100644 --- a/video/send_statistics_proxy_unittest.cc +++ b/video/send_statistics_proxy_unittest.cc @@ -18,6 +18,10 @@ #include "absl/algorithm/container.h" #include "api/units/timestamp.h" +#include "api/video/video_bitrate_allocation.h" +#include "api/video/video_codec_type.h" +#include "api/video_codecs/video_codec.h" +#include "api/video_codecs/video_encoder_config.h" #include "rtc_base/fake_clock.h" #include "system_wrappers/include/metrics.h" #include "test/field_trial.h" @@ -1206,6 +1210,167 @@ TEST_F(SendStatisticsProxyTest, QualityLimitationDurationIncreasesWithTime) { quality_limitation_durations_ms[QualityLimitationReason::kOther]); } +TEST_F(SendStatisticsProxyTest, QualityLimitationResolutionChangesDefaultZero) { + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionChangesNotChangesWithOnlyDefaultAllocation) { + VideoCodec codec; + VideoBitrateAllocation allocation; + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionChangesDoesNotIncreaseOnFirstAllocation) { + VideoCodec codec; + codec.simulcastStream[0].active = true; + codec.simulcastStream[1].active = true; + codec.simulcastStream[2].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionChangesWhenNewLayerGetsBandwidth) { + VideoCodec codec; + codec.simulcastStream[0].active = true; + codec.simulcastStream[1].active = true; + codec.simulcastStream[2].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + allocation.SetBitrate(1, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 1u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionDoesNotChangeWhenLayerSame) { + VideoCodec codec; + codec.simulcastStream[0].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + // Layer 0 got more bandwidth, but still only one layer on + allocation.SetBitrate(0, 0, 200); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionChangesWithTogglingLayers) { + VideoCodec codec; + codec.simulcastStream[0].active = true; + codec.simulcastStream[1].active = true; + codec.simulcastStream[2].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + allocation.SetBitrate(1, 0, 300); + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 1u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + // Layer 2 off + allocation.SetBitrate(2, 0, 0); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 2u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + // Layer 2 back on + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 3u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + allocation.SetBitrate(0, 0, 0); + allocation.SetBitrate(1, 0, 0); + allocation.SetBitrate(2, 0, 0); + // All layers off + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 4u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionDoesNotUpdateOnCodecSimulcastStreamChanges) { + VideoCodec codec; + // 3 layers + codec.simulcastStream[0].active = true; + codec.simulcastStream[1].active = true; + codec.simulcastStream[2].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 500); + allocation.SetBitrate(1, 0, 500); + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + + // Down to one layer now, triggered by a config change + codec.numberOfSimulcastStreams = 1; + codec.simulcastStream[1].active = false; + codec.simulcastStream[2].active = false; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + + // Up to 3 layers again. + codec.numberOfSimulcastStreams = 3; + codec.simulcastStream[1].active = true; + codec.simulcastStream[2].active = true; + allocation.SetBitrate(0, 0, 500); + allocation.SetBitrate(1, 0, 500); + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + +TEST_F(SendStatisticsProxyTest, + QualityLimitationResolutionDoesNotUpdateForSpatialLayerChanges) { + VideoCodec codec; + codec.simulcastStream[0].active = true; + codec.spatialLayers[0].active = true; + codec.spatialLayers[1].active = true; + codec.spatialLayers[2].active = true; + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 500); + allocation.SetBitrate(1, 0, 500); + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + + // Down to one layer now, triggered by a config change + codec.spatialLayers[1].active = false; + codec.spatialLayers[2].active = false; + allocation.SetBitrate(0, 0, 100); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); + + // Up to 3 layers again. + codec.spatialLayers[1].active = true; + codec.spatialLayers[2].active = true; + allocation.SetBitrate(0, 0, 500); + allocation.SetBitrate(1, 0, 500); + allocation.SetBitrate(2, 0, 500); + statistics_proxy_->OnBitrateAllocationUpdated(codec, allocation); + EXPECT_EQ( + 0u, statistics_proxy_->GetStats().quality_limitation_resolution_changes); +} + TEST_F(SendStatisticsProxyTest, SwitchContentTypeUpdatesHistograms) { for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i) statistics_proxy_->OnIncomingFrame(kWidth, kHeight); diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 61a6f2fc5e..2f289a794b 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -11,6 +11,7 @@ #include "video/video_stream_encoder.h" #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "api/video/encoded_image.h" #include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocator_factory.h" +#include "api/video/video_codec_constants.h" #include "api/video_codecs/video_encoder.h" #include "modules/video_coding/codecs/vp9/svc_rate_allocator.h" #include "modules/video_coding/include/video_codec_initializer.h" @@ -1169,6 +1171,9 @@ VideoStreamEncoder::UpdateBitrateAllocationAndNotifyObserver( new_rate_settings.bitrate = adjusted_allocation; } + encoder_stats_observer_->OnBitrateAllocationUpdated( + send_codec_, new_rate_settings.bitrate); + return new_rate_settings; }