diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc index d6f36f2d27..6ac840eb6c 100644 --- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc +++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc @@ -131,6 +131,15 @@ bool ValidSimulcastResolutions(const VideoCodec& codec, int num_streams) { } return true; } + +int NumStreamsDisabled(std::vector& streams) { + int num_disabled = 0; + for (bool stream : streams) { + if (!stream) + ++num_disabled; + } + return num_disabled; +} } // namespace const float kTl1MaxTimeToDropFrames = 20.0f; @@ -951,6 +960,9 @@ void VP8EncoderImpl::PopulateCodecSpecific( int VP8EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image, bool only_predicting_from_key_frame) { + int bw_resolutions_disabled = + (encoders_.size() > 1) ? NumStreamsDisabled(send_stream_) : -1; + int stream_idx = static_cast(encoders_.size()) - 1; int result = WEBRTC_VIDEO_CODEC_OK; for (size_t encoder_idx = 0; encoder_idx < encoders_.size(); @@ -1018,6 +1030,9 @@ int VP8EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image, encoded_images_[encoder_idx] .adapt_reason_.quality_resolution_downscales = quality_scaler_enabled_ ? quality_scaler_.downscale_shift() : -1; + // Report once per frame (lowest stream always sent). + encoded_images_[encoder_idx].adapt_reason_.bw_resolutions_disabled = + (stream_idx == 0) ? bw_resolutions_disabled : -1; encoded_complete_callback_->Encoded(encoded_images_[encoder_idx], &codec_specific, &frag_info); } else if (codec_.mode == kScreensharing) { diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc index 00edd065ce..32b79023c8 100644 --- a/webrtc/video/send_statistics_proxy.cc +++ b/webrtc/video/send_statistics_proxy.cc @@ -81,6 +81,16 @@ void SendStatisticsProxy::UpdateHistograms() { RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.QualityLimitedResolutionDownscales", downscales, 20); } + int bw_limited = bw_limited_frame_counter_.Percent(kMinRequiredSamples); + if (bw_limited != -1) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BandwidthLimitedResolutionInPercent", + bw_limited); + } + int num_disabled = bw_resolutions_disabled_counter_.Avg(kMinRequiredSamples); + if (num_disabled != -1) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Video.BandwidthLimitedResolutionsDisabled", num_disabled, 10); + } } void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) { @@ -190,6 +200,14 @@ void SendStatisticsProxy::OnSendEncodedImage( encoded_image.adapt_reason_.quality_resolution_downscales); } } + if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) { + bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0; + bw_limited_frame_counter_.Add(bw_limited); + if (bw_limited) { + bw_resolutions_disabled_counter_.Add( + encoded_image.adapt_reason_.bw_resolutions_disabled); + } + } // TODO(asapersson): This is incorrect if simulcast layers are encoded on // different threads and there is no guarantee that one frame of all layers @@ -299,7 +317,7 @@ void SendStatisticsProxy::SampleCounter::Add(int sample) { int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { if (num_samples < min_required_samples || num_samples == 0) return -1; - return sum / num_samples; + return (sum + (num_samples / 2)) / num_samples; } void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) { diff --git a/webrtc/video/send_statistics_proxy.h b/webrtc/video/send_statistics_proxy.h index cd2327e2aa..7451bb57e8 100644 --- a/webrtc/video/send_statistics_proxy.h +++ b/webrtc/video/send_statistics_proxy.h @@ -87,8 +87,10 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, uint32_t ssrc) override; private: - struct SampleCounter { + class SampleCounter { + public: SampleCounter() : sum(0), num_samples(0) {} + ~SampleCounter() {} void Add(int sample); int Avg(int min_required_samples) const; @@ -96,8 +98,10 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, int sum; int num_samples; }; - struct BoolSampleCounter { + class BoolSampleCounter { + public: BoolSampleCounter() : sum(0), num_samples(0) {} + ~BoolSampleCounter() {} void Add(bool sample); int Percent(int min_required_samples) const; int Permille(int min_required_samples) const; @@ -136,6 +140,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, BoolSampleCounter key_frame_counter_ GUARDED_BY(crit_); BoolSampleCounter quality_limited_frame_counter_ GUARDED_BY(crit_); SampleCounter quality_downscales_counter_ GUARDED_BY(crit_); + BoolSampleCounter bw_limited_frame_counter_ GUARDED_BY(crit_); + SampleCounter bw_resolutions_disabled_counter_ GUARDED_BY(crit_); }; } // namespace webrtc diff --git a/webrtc/video_frame.h b/webrtc/video_frame.h index 1968a69b0b..821bfc2551 100644 --- a/webrtc/video_frame.h +++ b/webrtc/video_frame.h @@ -167,6 +167,7 @@ class VideoFrame { VideoRotation rotation_; }; + // TODO(pbos): Rename EncodedFrame and reformat this class' members. class EncodedImage { public: @@ -176,11 +177,15 @@ class EncodedImage { struct AdaptReason { AdaptReason() - : quality_resolution_downscales(-1) {} + : quality_resolution_downscales(-1), + bw_resolutions_disabled(-1) {} int quality_resolution_downscales; // Number of times this frame is down // scaled in resolution due to quality. // Or -1 if information is not provided. + int bw_resolutions_disabled; // Number of resolutions that are not sent + // due to bandwidth for this frame. + // Or -1 if information is not provided. }; uint32_t _encodedWidth = 0; uint32_t _encodedHeight = 0;