Add per frame decode time histograms for 4k/HD and VP9/H264

Add new histograms
WebRTC.Video.DecodeTimePerFrameInMs.[codec].[resolution].[decoder]
These histograms are more explicit than the existing histogram
WebRTC.VideoDecodTimeMs, since they allow to see performance per
codec/resolution/decoder and also contain per frame statistics instead
of an average decode time.

There's a killswitch, WebRTC-DecodeTimeHistogramsKillSwitch, that can be
used to disable the histograms.

Bug: chromium:1007526
Change-Id: I9f75127b4bc5341e9f406c64ed91164564290b26
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157881
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29572}
This commit is contained in:
Johannes Kron
2019-10-22 13:22:26 +02:00
committed by Commit Bot
parent 13a8e16247
commit e76b3abf61
3 changed files with 208 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
@ -85,6 +86,8 @@ ReceiveStatisticsProxy::ReceiveStatisticsProxy(
: clock_(clock),
config_(*config),
start_ms_(clock->TimeInMilliseconds()),
enable_decode_time_histograms_(
!field_trial::IsEnabled("WebRTC-DecodeTimeHistogramsKillSwitch")),
last_sample_time_(clock->TimeInMilliseconds()),
fps_threshold_(kLowFpsThreshold,
kHighFpsThreshold,
@ -553,6 +556,61 @@ void ReceiveStatisticsProxy::UpdateFramerate(int64_t now_ms) const {
stats_.network_frame_rate = static_cast<int>(framerate);
}
void ReceiveStatisticsProxy::UpdateDecodeTimeHistograms(
int width,
int height,
int decode_time_ms) const {
bool is_4k = (width == 3840 || width == 4096) && height == 2160;
bool is_hd = width == 1920 && height == 1080;
// Only update histograms for 4k/HD and VP9/H264.
if ((is_4k || is_hd) && (last_codec_type_ == kVideoCodecVP9 ||
last_codec_type_ == kVideoCodecH264)) {
const std::string kDecodeTimeUmaPrefix =
"WebRTC.Video.DecodeTimePerFrameInMs.";
// Each histogram needs its own line for it to not be reused in the wrong
// way when the format changes.
if (last_codec_type_ == kVideoCodecVP9) {
bool is_sw_decoder =
stats_.decoder_implementation_name.compare(0, 6, "libvpx") == 0;
if (is_4k) {
if (is_sw_decoder)
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.4k.Sw",
decode_time_ms);
else
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.4k.Hw",
decode_time_ms);
} else {
if (is_sw_decoder)
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.Hd.Sw",
decode_time_ms);
else
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.Hd.Hw",
decode_time_ms);
}
} else {
bool is_sw_decoder =
stats_.decoder_implementation_name.compare(0, 6, "FFmpeg") == 0;
if (is_4k) {
if (is_sw_decoder)
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.4k.Sw",
decode_time_ms);
else
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.4k.Hw",
decode_time_ms);
} else {
if (is_sw_decoder)
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.Hd.Sw",
decode_time_ms);
else
RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.Hd.Hw",
decode_time_ms);
}
}
}
}
VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
rtc::CritScope lock(&crit_);
// Get current frame rates here, as only updating them on new frames prevents
@ -697,6 +755,10 @@ void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
decode_time_counter_.Add(decode_time_ms);
stats_.decode_ms = decode_time_ms;
stats_.total_decode_time_ms += decode_time_ms;
if (enable_decode_time_histograms_) {
UpdateDecodeTimeHistograms(frame.width(), frame.height(), decode_time_ms);
}
last_content_type_ = content_type;
decode_fps_estimator_.Update(1, now_ms);
if (last_decoded_frame_time_ms_) {

View File

@ -128,6 +128,11 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
void UpdateFramerate(int64_t now_ms) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
void UpdateDecodeTimeHistograms(int width,
int height,
int decode_time_ms) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
Clock* const clock_;
// Ownership of this object lies with the owner of the ReceiveStatisticsProxy
// instance. Lifetime is guaranteed to outlive |this|.
@ -138,6 +143,7 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
// then no longer store a pointer to the object).
const VideoReceiveStream::Config& config_;
const int64_t start_ms_;
const bool enable_decode_time_histograms_;
rtc::CriticalSection crit_;
int64_t last_sample_time_ RTC_GUARDED_BY(crit_);

View File

@ -16,12 +16,14 @@
#include <tuple>
#include <utility>
#include "absl/types/optional.h"
#include "api/scoped_refptr.h"
#include "api/video/i420_buffer.h"
#include "api/video/video_frame.h"
#include "api/video/video_frame_buffer.h"
#include "api/video/video_rotation.h"
#include "system_wrappers/include/metrics.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
@ -1548,4 +1550,142 @@ TEST_P(ReceiveStatisticsProxyTestWithContent,
"WebRTC.Video.InterframeDelayInMs.ExperimentGroup0"));
}
}
class DecodeTimeHistogramsKillswitch {
public:
explicit DecodeTimeHistogramsKillswitch(bool disable_histograms)
: field_trial_(disable_histograms
? "WebRTC-DecodeTimeHistogramsKillSwitch/Enabled/"
: "") {}
private:
webrtc::test::ScopedFieldTrials field_trial_;
};
class ReceiveStatisticsProxyTestWithDecodeTimeHistograms
: public DecodeTimeHistogramsKillswitch,
public ::testing::WithParamInterface<
std::tuple<bool, int, int, int, VideoCodecType, std::string>>,
public ReceiveStatisticsProxyTest {
public:
ReceiveStatisticsProxyTestWithDecodeTimeHistograms()
: DecodeTimeHistogramsKillswitch(std::get<0>(GetParam())) {}
protected:
const std::string kUmaPrefix = "WebRTC.Video.DecodeTimePerFrameInMs.";
const int expected_number_of_samples_ = {std::get<1>(GetParam())};
const int width_ = {std::get<2>(GetParam())};
const int height_ = {std::get<3>(GetParam())};
const VideoCodecType codec_type_ = {std::get<4>(GetParam())};
const std::string implementation_name_ = {std::get<5>(GetParam())};
const std::string uma_histogram_name_ =
kUmaPrefix + (codec_type_ == kVideoCodecVP9 ? "Vp9." : "H264.") +
(height_ == 2160 ? "4k." : "Hd.") +
(implementation_name_.compare("ExternalDecoder") == 0 ? "Hw" : "Sw");
};
TEST_P(ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
DecodeTimeHistogramsUpdated) {
constexpr int kNumberOfFrames = 10;
constexpr int kDecodeTimeMs = 7;
constexpr int kFrameDurationMs = 1000 / 60;
webrtc::VideoFrame frame = CreateFrame(width_, height_);
statistics_proxy_->OnDecoderImplementationName(implementation_name_.c_str());
statistics_proxy_->OnPreDecode(codec_type_, /*qp=*/0);
for (int i = 0; i < kNumberOfFrames; ++i) {
statistics_proxy_->OnDecodedFrame(frame, /*qp=*/absl::nullopt,
kDecodeTimeMs,
VideoContentType::UNSPECIFIED);
fake_clock_.AdvanceTimeMilliseconds(kFrameDurationMs);
}
EXPECT_EQ(expected_number_of_samples_,
metrics::NumSamples(uma_histogram_name_));
EXPECT_EQ(expected_number_of_samples_,
metrics::NumEvents(uma_histogram_name_, kDecodeTimeMs));
}
const auto kVp94kHw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/3840,
/*height=*/2160,
kVideoCodecVP9,
/*implementation=*/"ExternalDecoder");
const auto kVp94kSw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/3840,
/*height=*/2160,
kVideoCodecVP9,
/*implementation=*/"libvpx");
const auto kVp9HdHw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/1920,
/*height=*/1080,
kVideoCodecVP9,
/*implementation=*/"ExternalDecoder");
const auto kVp9HdSw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/1920,
/*height=*/1080,
kVideoCodecVP9,
/*implementation=*/"libvpx");
const auto kH2644kHw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/3840,
/*height=*/2160,
kVideoCodecH264,
/*implementation=*/"ExternalDecoder");
const auto kH2644kSw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/3840,
/*height=*/2160,
kVideoCodecH264,
/*implementation=*/"FFmpeg");
const auto kH264HdHw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/1920,
/*height=*/1080,
kVideoCodecH264,
/*implementation=*/"ExternalDecoder");
const auto kH264HdSw = std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/1920,
/*height=*/1080,
kVideoCodecH264,
/*implementation=*/"FFmpeg");
INSTANTIATE_TEST_SUITE_P(AllHistogramsPopulated,
ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
::testing::Values(kVp94kHw,
kVp94kSw,
kVp9HdHw,
kVp9HdSw,
kH2644kHw,
kH2644kSw,
kH264HdHw,
kH264HdSw));
const auto kKillswitchDisabled =
std::make_tuple(/*killswitch=*/false,
/*expected_number_of_samples=*/10,
/*width=*/1920,
/*height=*/1080,
kVideoCodecVP9,
/*implementation=*/"libvpx");
const auto kKillswitchEnabled =
std::make_tuple(/*killswitch=*/true,
/*expected_number_of_samples=*/0,
/*width=*/1920,
/*height=*/1080,
kVideoCodecVP9,
/*implementation=*/"libvpx");
INSTANTIATE_TEST_SUITE_P(KillswitchEffective,
ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
::testing::Values(kKillswitchDisabled,
kKillswitchEnabled));
} // namespace webrtc