diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc index 31e1d80636..e4df9290ab 100644 --- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc +++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc @@ -1040,6 +1040,10 @@ int VP8EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image, // Report once per frame (lowest stream always sent). encoded_images_[encoder_idx].adapt_reason_.bw_resolutions_disabled = (stream_idx == 0) ? bw_resolutions_disabled : -1; + int qp_128 = -1; + vpx_codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER, + &qp_128); + encoded_images_[encoder_idx].qp_ = qp_128; 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 6c689bfcdb..3d82a3b046 100644 --- a/webrtc/video/send_statistics_proxy.cc +++ b/webrtc/video/send_statistics_proxy.cc @@ -193,6 +193,29 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms( kIndex, uma_prefix_ + "SendSideDelayMaxInMs", max_delay_ms); } + for (const auto& it : qp_counters_) { + int qp = it.second.vp8.Avg(kMinRequiredSamples); + if (qp != -1) { + int spatial_idx = it.first; + if (spatial_idx == -1) { + RTC_LOGGED_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8", + qp); + } else if (spatial_idx == 0) { + RTC_LOGGED_HISTOGRAMS_COUNTS_200(kIndex, + uma_prefix_ + "Encoded.Qp.Vp8.S0", qp); + } else if (spatial_idx == 1) { + RTC_LOGGED_HISTOGRAMS_COUNTS_200(kIndex, + uma_prefix_ + "Encoded.Qp.Vp8.S1", qp); + } else if (spatial_idx == 2) { + RTC_LOGGED_HISTOGRAMS_COUNTS_200(kIndex, + uma_prefix_ + "Encoded.Qp.Vp8.S2", qp); + } else { + LOG(LS_WARNING) << "QP stats not recorded for VP8 spatial idx " + << spatial_idx; + } + } + } + if (first_rtcp_stats_time_ms_ != -1) { int64_t elapsed_sec = (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000; @@ -427,6 +450,13 @@ void SendStatisticsProxy::OnSendEncodedImage( } } + if (encoded_image.qp_ != -1 && rtp_video_header != nullptr && + rtp_video_header->codec == kRtpVideoVp8) { + int spatial_idx = + (config_.rtp.ssrcs.size() == 1) ? -1 : static_cast(simulcast_idx); + uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_); + } + // TODO(asapersson): This is incorrect if simulcast layers are encoded on // different threads and there is no guarantee that one frame of all layers // are encoded before the next start. diff --git a/webrtc/video/send_statistics_proxy.h b/webrtc/video/send_statistics_proxy.h index 66f03367b2..5a479a1e80 100644 --- a/webrtc/video/send_statistics_proxy.h +++ b/webrtc/video/send_statistics_proxy.h @@ -125,6 +125,9 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, int64_t resolution_update_ms; int64_t bitrate_update_ms; }; + struct QpCounters { + SampleCounter vp8; + }; void PurgeOldStats() EXCLUSIVE_LOCKS_REQUIRED(crit_); VideoSendStream::StreamStats* GetStatsEntry(uint32_t ssrc) EXCLUSIVE_LOCKS_REQUIRED(crit_); @@ -172,6 +175,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, int64_t first_rtp_stats_time_ms_; ReportBlockStats report_block_stats_; const VideoSendStream::Stats start_stats_; + std::map + qp_counters_; // QP counters mapped by spatial idx. }; std::unique_ptr uma_container_ GUARDED_BY(crit_); diff --git a/webrtc/video/send_statistics_proxy_unittest.cc b/webrtc/video/send_statistics_proxy_unittest.cc index a98505f84d..bb404fb8be 100644 --- a/webrtc/video/send_statistics_proxy_unittest.cc +++ b/webrtc/video/send_statistics_proxy_unittest.cc @@ -327,6 +327,57 @@ TEST_F(SendStatisticsProxyTest, SwitchContentTypeUpdatesHistograms) { EXPECT_EQ(1, test::NumHistogramSamples("WebRTC.Video.InputWidthInPixels")); } +TEST_F(SendStatisticsProxyTest, VerifyQpHistogramStats_Vp8) { + test::ClearHistograms(); + const int kMinRequiredSamples = 200; + const int kQpIdx0 = 21; + const int kQpIdx1 = 39; + EncodedImage encoded_image; + + RTPVideoHeader rtp_video_header; + rtp_video_header.codec = kRtpVideoVp8; + + for (int i = 0; i < kMinRequiredSamples; ++i) { + rtp_video_header.simulcastIdx = 0; + encoded_image.qp_ = kQpIdx0; + statistics_proxy_->OnSendEncodedImage(encoded_image, &rtp_video_header); + rtp_video_header.simulcastIdx = 1; + encoded_image.qp_ = kQpIdx1; + statistics_proxy_->OnSendEncodedImage(encoded_image, &rtp_video_header); + } + statistics_proxy_.reset(); + EXPECT_EQ(1, test::NumHistogramSamples("WebRTC.Video.Encoded.Qp.Vp8.S0")); + EXPECT_EQ(kQpIdx0, + test::LastHistogramSample("WebRTC.Video.Encoded.Qp.Vp8.S0")); + EXPECT_EQ(1, test::NumHistogramSamples("WebRTC.Video.Encoded.Qp.Vp8.S1")); + EXPECT_EQ(kQpIdx1, + test::LastHistogramSample("WebRTC.Video.Encoded.Qp.Vp8.S1")); +} + +TEST_F(SendStatisticsProxyTest, VerifyQpHistogramStats_Vp8OneSsrc) { + VideoSendStream::Config config(nullptr); + config.rtp.ssrcs.push_back(kFirstSsrc); + statistics_proxy_.reset(new SendStatisticsProxy( + &fake_clock_, config, VideoEncoderConfig::ContentType::kRealtimeVideo)); + + test::ClearHistograms(); + const int kMinRequiredSamples = 200; + const int kQpIdx0 = 21; + EncodedImage encoded_image; + + RTPVideoHeader rtp_video_header; + rtp_video_header.codec = kRtpVideoVp8; + + for (int i = 0; i < kMinRequiredSamples; ++i) { + rtp_video_header.simulcastIdx = 0; + encoded_image.qp_ = kQpIdx0; + statistics_proxy_->OnSendEncodedImage(encoded_image, &rtp_video_header); + } + statistics_proxy_.reset(); + EXPECT_EQ(1, test::NumHistogramSamples("WebRTC.Video.Encoded.Qp.Vp8")); + EXPECT_EQ(kQpIdx0, test::LastHistogramSample("WebRTC.Video.Encoded.Qp.Vp8")); +} + TEST_F(SendStatisticsProxyTest, NoSubstreams) { uint32_t excluded_ssrc = std::max(