Piggybacking simulcast id and ALR experiment id into video content type extension.
Use it to slice UMA video receive statis.
BUG=8032
Review-Url: https://codereview.webrtc.org/2986893002
Cr-Commit-Position: refs/heads/master@{#19598}
diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn
index 7c43808..3d19f03 100644
--- a/webrtc/api/BUILD.gn
+++ b/webrtc/api/BUILD.gn
@@ -168,6 +168,8 @@
sources = [
"video/i420_buffer.cc",
"video/i420_buffer.h",
+ "video/video_content_type.cc",
+ "video/video_content_type.h",
"video/video_frame.cc",
"video/video_frame.h",
"video/video_frame_buffer.cc",
diff --git a/webrtc/api/video/video_content_type.cc b/webrtc/api/video/video_content_type.cc
new file mode 100644
index 0000000..278b57c
--- /dev/null
+++ b/webrtc/api/video/video_content_type.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/api/video/video_content_type.h"
+
+// VideoContentType stored as a single byte, which is sent over the network.
+// Structure:
+//
+// 0 1 2 3 4 5 6 7
+// +---------------+
+// |r r e e e s s c|
+//
+// where:
+// r - reserved bits.
+// e - 3-bit number of an experiment group counted from 1. 0 means there's no
+// experiment ongoing.
+// s - 2-bit simulcast stream id or spatial layer, counted from 1. 0 means that
+// no simulcast information is set.
+// c - content type. 0 means real-time video, 1 means screenshare.
+//
+
+namespace webrtc {
+namespace videocontenttypehelpers {
+
+namespace {
+static constexpr uint8_t kScreenshareBitsSize = 1;
+static constexpr uint8_t kScreenshareBitsMask =
+ (1u << kScreenshareBitsSize) - 1;
+
+static constexpr uint8_t kSimulcastShift = 1;
+static constexpr uint8_t kSimulcastBitsSize = 2;
+static constexpr uint8_t kSimulcastBitsMask = ((1u << kSimulcastBitsSize) - 1)
+ << kSimulcastShift; // 0b00000110
+
+static constexpr uint8_t kExperimentShift = 3;
+static constexpr uint8_t kExperimentBitsSize = 3;
+static constexpr uint8_t kExperimentBitsMask =
+ ((1u << kExperimentBitsSize) - 1) << kExperimentShift; // 0b00111000
+
+static constexpr uint8_t kTotalBitsSize =
+ kScreenshareBitsSize + kSimulcastBitsSize + kExperimentBitsSize;
+} // namespace
+
+bool SetExperimentId(VideoContentType* content_type,
+ uint8_t experiment_id) {
+ // Store in bits 2-4.
+ if (experiment_id >= (1 << kExperimentBitsSize))
+ return false;
+ *content_type = static_cast<VideoContentType>(
+ (static_cast<uint8_t>(*content_type) & ~kExperimentBitsMask) |
+ ((experiment_id << kExperimentShift) & kExperimentBitsMask));
+ return true;
+}
+
+bool SetSimulcastId(VideoContentType* content_type,
+ uint8_t simulcast_id) {
+ // Store in bits 5-6.
+ if (simulcast_id >= (1 << kSimulcastBitsSize))
+ return false;
+ *content_type = static_cast<VideoContentType>(
+ (static_cast<uint8_t>(*content_type) & ~kSimulcastBitsMask) |
+ ((simulcast_id << kSimulcastShift) & kSimulcastBitsMask));
+ return true;
+}
+
+uint8_t GetExperimentId(
+ const VideoContentType& content_type) {
+ return (static_cast<uint8_t>(content_type) & kExperimentBitsMask) >>
+ kExperimentShift;
+}
+uint8_t GetSimulcastId(
+ const VideoContentType& content_type) {
+ return (static_cast<uint8_t>(content_type) & kSimulcastBitsMask) >>
+ kSimulcastShift;
+}
+
+bool IsScreenshare(
+ const VideoContentType& content_type) {
+ return (static_cast<uint8_t>(content_type) & kScreenshareBitsMask) > 0;
+}
+
+bool IsValidContentType(uint8_t value) {
+ // Any 6-bit value is allowed.
+ return value < (1 << kTotalBitsSize);
+}
+} // namespace videocontenttypehelpers
+} // namespace webrtc
diff --git a/webrtc/api/video/video_content_type.h b/webrtc/api/video/video_content_type.h
index 5c468c0..6f6bbe8 100644
--- a/webrtc/api/video/video_content_type.h
+++ b/webrtc/api/video/video_content_type.h
@@ -18,9 +18,22 @@
enum class VideoContentType : uint8_t {
UNSPECIFIED = 0,
SCREENSHARE = 1,
- TOTAL_CONTENT_TYPES // Must be the last value in the enum.
};
+namespace videocontenttypehelpers {
+ bool SetExperimentId(VideoContentType* content_type,
+ uint8_t experiment_id);
+ bool SetSimulcastId(VideoContentType* content_type,
+ uint8_t simulcast_id);
+
+ uint8_t GetExperimentId(const VideoContentType& content_type);
+ uint8_t GetSimulcastId(const VideoContentType& content_type);
+
+ bool IsScreenshare(const VideoContentType& content_type);
+
+ bool IsValidContentType(uint8_t value);
+} // namespace videocontenttypehelpers
+
} // namespace webrtc
#endif // WEBRTC_API_VIDEO_VIDEO_CONTENT_TYPE_H_
diff --git a/webrtc/common_video/include/video_frame.h b/webrtc/common_video/include/video_frame.h
index 4f8ed08..99e0b8f 100644
--- a/webrtc/common_video/include/video_frame.h
+++ b/webrtc/common_video/include/video_frame.h
@@ -54,7 +54,7 @@
size_t _length;
size_t _size;
VideoRotation rotation_ = kVideoRotation_0;
- VideoContentType content_type_ = VideoContentType::UNSPECIFIED;
+ mutable VideoContentType content_type_ = VideoContentType::UNSPECIFIED;
bool _completeFrame = false;
AdaptReason adapt_reason_;
int qp_ = -1; // Quantizer value.
diff --git a/webrtc/modules/pacing/alr_detector.cc b/webrtc/modules/pacing/alr_detector.cc
index 993c6cd..7d62b21 100644
--- a/webrtc/modules/pacing/alr_detector.cc
+++ b/webrtc/modules/pacing/alr_detector.cc
@@ -91,11 +91,12 @@
return ret;
AlrExperimentSettings settings;
- if (sscanf(group_name.c_str(), "%f,%" PRId64 ",%d,%d,%d",
+ if (sscanf(group_name.c_str(), "%f,%" PRId64 ",%d,%d,%d,%d",
&settings.pacing_factor, &settings.max_paced_queue_time,
&settings.alr_bandwidth_usage_percent,
&settings.alr_start_budget_level_percent,
- &settings.alr_stop_budget_level_percent) == 5) {
+ &settings.alr_stop_budget_level_percent,
+ &settings.group_id) == 6) {
ret.emplace(settings);
LOG(LS_INFO) << "Using ALR experiment settings: "
"pacing factor: "
@@ -106,7 +107,8 @@
<< ", ALR end budget level percent: "
<< settings.alr_start_budget_level_percent
<< ", ALR end budget level percent: "
- << settings.alr_stop_budget_level_percent;
+ << settings.alr_stop_budget_level_percent
+ << ", ALR experiment group ID: " << settings.group_id;
} else {
LOG(LS_INFO) << "Failed to parse ALR experiment: " << experiment_name;
}
diff --git a/webrtc/modules/pacing/alr_detector.h b/webrtc/modules/pacing/alr_detector.h
index 0dcd9ef..1e124d9 100644
--- a/webrtc/modules/pacing/alr_detector.h
+++ b/webrtc/modules/pacing/alr_detector.h
@@ -47,6 +47,10 @@
int alr_bandwidth_usage_percent = kDefaultAlrBandwidthUsagePercent;
int alr_start_budget_level_percent = kDefaultAlrStartBudgetLevelPercent;
int alr_stop_budget_level_percent = kDefaultAlrStopBudgetLevelPercent;
+ // Will be sent to the receive side for stats slicing.
+ // Can be 0..6, because it's sent as a 3 bits value and there's also
+ // reserved value to indicate absence of experiment.
+ int group_id = 0;
};
static rtc::Optional<AlrExperimentSettings> ParseAlrSettingsFromFieldTrial(
const char* experiment_name);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
index 28685b4..d409d5a 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -232,7 +232,7 @@
bool VideoContentTypeExtension::Parse(rtc::ArrayView<const uint8_t> data,
VideoContentType* content_type) {
if (data.size() == 1 &&
- data[0] < static_cast<uint8_t>(VideoContentType::TOTAL_CONTENT_TYPES)) {
+ videocontenttypehelpers::IsValidContentType(data[0])) {
*content_type = static_cast<VideoContentType>(data[0]);
return true;
}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
index 4b5ecfa..fbcf731 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -452,8 +452,7 @@
// | ID | len=0 | Content type |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- if (ptr[0] <
- static_cast<uint8_t>(VideoContentType::TOTAL_CONTENT_TYPES)) {
+ if (videocontenttypehelpers::IsValidContentType(ptr[0])) {
header->extension.hasVideoContentType = true;
header->extension.videoContentType =
static_cast<VideoContentType>(ptr[0]);
diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn
index 9611778..a93d7f7 100644
--- a/webrtc/modules/video_coding/BUILD.gn
+++ b/webrtc/modules/video_coding/BUILD.gn
@@ -104,6 +104,7 @@
"../../rtc_base:rtc_task_queue",
"../../rtc_base:sequenced_task_checker",
"../../system_wrappers",
+ "../pacing",
"../rtp_rtcp:rtp_rtcp",
"../utility:utility",
]
diff --git a/webrtc/modules/video_coding/frame_buffer2.cc b/webrtc/modules/video_coding/frame_buffer2.cc
index a783657..7fcb728 100644
--- a/webrtc/modules/video_coding/frame_buffer2.cc
+++ b/webrtc/modules/video_coding/frame_buffer2.cc
@@ -277,7 +277,8 @@
TRACE_EVENT0("webrtc", "FrameBuffer::InsertFrame");
RTC_DCHECK(frame);
if (stats_callback_)
- stats_callback_->OnCompleteFrame(frame->is_keyframe(), frame->size());
+ stats_callback_->OnCompleteFrame(frame->is_keyframe(), frame->size(),
+ frame->contentType());
FrameKey key(frame->picture_id, frame->spatial_layer);
rtc::CritScope lock(&crit_);
diff --git a/webrtc/modules/video_coding/frame_buffer2_unittest.cc b/webrtc/modules/video_coding/frame_buffer2_unittest.cc
index 88f8410..1ae38c6 100644
--- a/webrtc/modules/video_coding/frame_buffer2_unittest.cc
+++ b/webrtc/modules/video_coding/frame_buffer2_unittest.cc
@@ -105,7 +105,10 @@
public:
MOCK_METHOD2(OnReceiveRatesUpdated,
void(uint32_t bitRate, uint32_t frameRate));
- MOCK_METHOD2(OnCompleteFrame, void(bool is_keyframe, size_t size_bytes));
+ MOCK_METHOD3(OnCompleteFrame,
+ void(bool is_keyframe,
+ size_t size_bytes,
+ VideoContentType content_type));
MOCK_METHOD1(OnDiscardedPacketsUpdated, void(int discarded_packets));
MOCK_METHOD1(OnFrameCountsUpdated, void(const FrameCounts& frame_counts));
MOCK_METHOD7(OnFrameBufferTimingsUpdated,
@@ -489,7 +492,8 @@
uint32_t ts = Rand();
const int kFrameSize = 5000;
- EXPECT_CALL(stats_callback_, OnCompleteFrame(true, kFrameSize));
+ EXPECT_CALL(stats_callback_,
+ OnCompleteFrame(true, kFrameSize, VideoContentType::UNSPECIFIED));
EXPECT_CALL(stats_callback_,
OnFrameBufferTimingsUpdated(_, _, _, _, _, _, _));
diff --git a/webrtc/modules/video_coding/generic_encoder.cc b/webrtc/modules/video_coding/generic_encoder.cc
index cdb244d..adc4365 100644
--- a/webrtc/modules/video_coding/generic_encoder.cc
+++ b/webrtc/modules/video_coding/generic_encoder.cc
@@ -13,6 +13,7 @@
#include <vector>
#include "webrtc/api/video/i420_buffer.h"
+#include "webrtc/modules/pacing/alr_detector.h"
#include "webrtc/modules/video_coding/encoded_frame.h"
#include "webrtc/modules/video_coding/media_optimization.h"
#include "webrtc/rtc_base/checks.h"
@@ -20,6 +21,7 @@
#include "webrtc/rtc_base/optional.h"
#include "webrtc/rtc_base/timeutils.h"
#include "webrtc/rtc_base/trace_event.h"
+#include "webrtc/system_wrappers/include/field_trial.h"
namespace webrtc {
@@ -183,7 +185,23 @@
media_opt_(media_opt),
framerate_(1),
last_timing_frame_time_ms_(-1),
- timing_frames_thresholds_({-1, 0}) {}
+ timing_frames_thresholds_({-1, 0}) {
+ rtc::Optional<AlrDetector::AlrExperimentSettings> experiment_settings =
+ AlrDetector::ParseAlrSettingsFromFieldTrial(
+ AlrDetector::kStrictPacingAndProbingExperimentName);
+ if (experiment_settings) {
+ experiment_groups_[0] = experiment_settings->group_id + 1;
+ } else {
+ experiment_groups_[0] = 0;
+ }
+ experiment_settings = AlrDetector::ParseAlrSettingsFromFieldTrial(
+ AlrDetector::kScreenshareProbingBweExperimentName);
+ if (experiment_settings) {
+ experiment_groups_[1] = experiment_settings->group_id + 1;
+ } else {
+ experiment_groups_[1] = 0;
+ }
+}
VCMEncodedFrameCallback::~VCMEncodedFrameCallback() {}
@@ -231,13 +249,15 @@
rtc::Optional<size_t> outlier_frame_size;
rtc::Optional<int64_t> encode_start_ms;
+ size_t num_simulcast_svc_streams = 1;
uint8_t timing_flags = TimingFrameFlags::kInvalid;
{
rtc::CritScope crit(&timing_params_lock_);
// Encoders with internal sources do not call OnEncodeStarted and
// OnFrameRateChanged. |timing_frames_info_| may be not filled here.
- if (simulcast_svc_idx < timing_frames_info_.size()) {
+ num_simulcast_svc_streams = timing_frames_info_.size();
+ if (simulcast_svc_idx < num_simulcast_svc_streams) {
auto encode_start_map =
&timing_frames_info_[simulcast_svc_idx].encode_start_time_ms;
auto it = encode_start_map->find(encoded_image.capture_time_ms_);
@@ -299,6 +319,23 @@
encoded_image.timing_.flags = TimingFrameFlags::kInvalid;
}
+ // Piggyback ALR experiment group id and simulcast id into the content type.
+ uint8_t experiment_id =
+ experiment_groups_[videocontenttypehelpers::IsScreenshare(
+ encoded_image.content_type_)];
+
+ // TODO(ilnik): This will force content type extension to be present even
+ // for realtime video. At the expense of miniscule overhead we will get
+ // sliced receive statistics.
+ RTC_CHECK(videocontenttypehelpers::SetExperimentId(
+ &encoded_image.content_type_, experiment_id));
+ // We count simulcast streams from 1 on the wire. That's why we set simulcast
+ // id in content type to +1 of that is actual simulcast index. This is because
+ // value 0 on the wire is reserved for 'no simulcast stream specified'.
+ RTC_CHECK(videocontenttypehelpers::SetSimulcastId(
+ &encoded_image.content_type_,
+ static_cast<uint8_t>(simulcast_svc_idx + 1)));
+
Result result = post_encode_callback_->OnEncodedImage(
encoded_image, codec_specific, fragmentation_header);
if (result.error != Result::OK)
diff --git a/webrtc/modules/video_coding/generic_encoder.h b/webrtc/modules/video_coding/generic_encoder.h
index a5c34d7..20e07c7 100644
--- a/webrtc/modules/video_coding/generic_encoder.h
+++ b/webrtc/modules/video_coding/generic_encoder.h
@@ -82,6 +82,11 @@
int64_t last_timing_frame_time_ms_ GUARDED_BY(timing_params_lock_);
VideoCodec::TimingFrameTriggerThresholds timing_frames_thresholds_
GUARDED_BY(timing_params_lock_);
+
+ // Experiment groups parsed from field trials for realtime video ([0]) and
+ // screenshare ([1]). 0 means no group specified. Positive values are
+ // experiment group numbers incremented by 1.
+ uint8_t experiment_groups_[2];
};
class VCMGenericEncoder {
diff --git a/webrtc/modules/video_coding/include/video_coding_defines.h b/webrtc/modules/video_coding/include/video_coding_defines.h
index 66775ac..499aba7 100644
--- a/webrtc/modules/video_coding/include/video_coding_defines.h
+++ b/webrtc/modules/video_coding/include/video_coding_defines.h
@@ -69,7 +69,7 @@
public:
virtual int32_t FrameToRender(VideoFrame& videoFrame, // NOLINT
rtc::Optional<uint8_t> qp,
- VideoContentType /*content_type*/) = 0;
+ VideoContentType content_type) = 0;
virtual int32_t ReceivedDecodedReferenceFrame(const uint64_t pictureId) {
return -1;
@@ -97,7 +97,9 @@
class VCMReceiveStatisticsCallback {
public:
virtual void OnReceiveRatesUpdated(uint32_t bitRate, uint32_t frameRate) = 0;
- virtual void OnCompleteFrame(bool is_keyframe, size_t size_bytes) = 0;
+ virtual void OnCompleteFrame(bool is_keyframe,
+ size_t size_bytes,
+ VideoContentType content_type) = 0;
virtual void OnDiscardedPacketsUpdated(int discarded_packets) = 0;
virtual void OnFrameCountsUpdated(const FrameCounts& frame_counts) = 0;
virtual void OnFrameBufferTimingsUpdated(int decode_ms,
diff --git a/webrtc/system_wrappers/include/metrics.h b/webrtc/system_wrappers/include/metrics.h
index f22cf8f..a10ee33 100644
--- a/webrtc/system_wrappers/include/metrics.h
+++ b/webrtc/system_wrappers/include/metrics.h
@@ -92,16 +92,46 @@
webrtc::metrics::HistogramFactoryGetCountsLinear( \
name, min, max, bucket_count))
-// Deprecated.
-// TODO(asapersson): Remove.
+// Slow metrics: pointer to metric is acquired at each call and is not cached.
+//
#define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) \
RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100, 50)
+#define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) \
+ RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 200, 50)
+
+#define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) \
+ RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 500, 50)
+
+#define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) \
+ RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 1000, 50)
+
+#define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) \
+ RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 10000, 50)
+
+#define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) \
+ RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100000, 50)
+
#define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \
RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, \
webrtc::metrics::HistogramFactoryGetCounts(name, min, max, bucket_count))
// Histogram for percentage (evenly spaced buckets).
+#define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) \
+ RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 101)
+
+// Histogram for booleans.
+#define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) \
+ RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 2)
+
+// Histogram for enumerators (evenly spaced buckets).
+// |boundary| should be above the max enumerator sample.
+#define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \
+ RTC_HISTOGRAM_COMMON_BLOCK_SLOW( \
+ name, sample, \
+ webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary))
+
+// Histogram for percentage (evenly spaced buckets).
#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \
RTC_HISTOGRAM_ENUMERATION(name, sample, 101)
@@ -154,7 +184,9 @@
// Helper macros.
// Macros for calling a histogram with varying name (e.g. when using a metric
-// in different modes such as real-time vs screenshare).
+// in different modes such as real-time vs screenshare). Fast, because pointer
+// is cached. |index| should be different for different names. Allowed |index|
+// values are 0, 1, and 2.
#define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) \
RTC_HISTOGRAMS_COMMON(index, name, sample, \
RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50))
@@ -188,23 +220,22 @@
RTC_HISTOGRAM_PERCENTAGE(name, sample))
#define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \
- do { \
- switch (index) { \
- case 0: \
- macro_invocation; \
- break; \
- case 1: \
- macro_invocation; \
- break; \
- case 2: \
- macro_invocation; \
- break; \
- default: \
- RTC_NOTREACHED(); \
- } \
+ do { \
+ switch (index) { \
+ case 0: \
+ macro_invocation; \
+ break; \
+ case 1: \
+ macro_invocation; \
+ break; \
+ case 2: \
+ macro_invocation; \
+ break; \
+ default: \
+ RTC_NOTREACHED(); \
+ } \
} while (0)
-
namespace webrtc {
namespace metrics {
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index fbc852f..4744049 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -2811,7 +2811,9 @@
std::string video_prefix =
screenshare ? "WebRTC.Video.Screenshare." : "WebRTC.Video.";
-
+ // The content type extension is disabled in non screenshare test,
+ // therefore no slicing on simulcast id should be present.
+ std::string video_suffix = screenshare ? ".S0" : "";
// Verify that stats have been updated once.
EXPECT_EQ(2, metrics::NumSamples("WebRTC.Call.LifetimeInSeconds"));
EXPECT_EQ(1, metrics::NumSamples(
@@ -2847,8 +2849,8 @@
EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InputHeightInPixels"));
EXPECT_EQ(1, metrics::NumSamples(video_prefix + "SentWidthInPixels"));
EXPECT_EQ(1, metrics::NumSamples(video_prefix + "SentHeightInPixels"));
- EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.ReceivedWidthInPixels"));
- EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.ReceivedHeightInPixels"));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "ReceivedWidthInPixels"));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "ReceivedHeightInPixels"));
EXPECT_EQ(1, metrics::NumEvents(video_prefix + "InputWidthInPixels",
kDefaultWidth));
@@ -2858,9 +2860,9 @@
1, metrics::NumEvents(video_prefix + "SentWidthInPixels", kDefaultWidth));
EXPECT_EQ(1, metrics::NumEvents(video_prefix + "SentHeightInPixels",
kDefaultHeight));
- EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.ReceivedWidthInPixels",
+ EXPECT_EQ(1, metrics::NumEvents(video_prefix + "ReceivedWidthInPixels",
kDefaultWidth));
- EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.ReceivedHeightInPixels",
+ EXPECT_EQ(1, metrics::NumEvents(video_prefix + "ReceivedHeightInPixels",
kDefaultHeight));
EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InputFramesPerSecond"));
@@ -2873,10 +2875,14 @@
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.CurrentDelayInMs"));
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.OnewayDelayInMs"));
- EXPECT_EQ(1, metrics::NumSamples(video_prefix + "EndToEndDelayInMs"));
- EXPECT_EQ(1, metrics::NumSamples(video_prefix + "EndToEndDelayMaxInMs"));
- EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InterframeDelayInMs"));
- EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InterframeDelayMaxInMs"));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "EndToEndDelayInMs" +
+ video_suffix));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "EndToEndDelayMaxInMs" +
+ video_suffix));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InterframeDelayInMs" +
+ video_suffix));
+ EXPECT_EQ(1, metrics::NumSamples(video_prefix + "InterframeDelayMaxInMs" +
+ video_suffix));
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.RenderSqrtPixelsPerSecond"));
diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
index 4f23169..6e58e1e 100644
--- a/webrtc/video/receive_statistics_proxy.cc
+++ b/webrtc/video/receive_statistics_proxy.cc
@@ -12,13 +12,14 @@
#include <algorithm>
#include <cmath>
+#include <sstream>
#include <utility>
+#include "webrtc/modules/pacing/alr_detector.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/system_wrappers/include/clock.h"
-#include "webrtc/system_wrappers/include/field_trial.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
@@ -48,6 +49,28 @@
// How large window we use to calculate the framerate/bitrate.
const int kRateStatisticsWindowSizeMs = 1000;
+
+std::string UmaPrefixForContentType(VideoContentType content_type) {
+ std::stringstream ss;
+ ss << "WebRTC.Video";
+ if (videocontenttypehelpers::IsScreenshare(content_type)) {
+ ss << ".Screenshare";
+ }
+ return ss.str();
+}
+
+std::string UmaSuffixForContentType(VideoContentType content_type) {
+ std::stringstream ss;
+ int simulcast_id = videocontenttypehelpers::GetSimulcastId(content_type);
+ if (simulcast_id > 0) {
+ ss << ".S" << simulcast_id - 1;
+ }
+ int experiment_id = videocontenttypehelpers::GetExperimentId(content_type);
+ if (experiment_id > 0) {
+ ss << ".ExperimentGroup" << experiment_id - 1;
+ }
+ return ss.str();
+}
} // namespace
ReceiveStatisticsProxy::ReceiveStatisticsProxy(
@@ -77,10 +100,6 @@
render_fps_tracker_(100, 10u),
render_pixel_tracker_(100, 10u),
total_byte_tracker_(100, 10u), // bucket_interval_ms, bucket_count
- e2e_delay_max_ms_video_(-1),
- e2e_delay_max_ms_screenshare_(-1),
- interframe_delay_max_ms_video_(-1),
- interframe_delay_max_ms_screenshare_(-1),
interframe_delay_max_moving_(kMovingMaxWindowMs),
freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs),
first_report_block_time_ms_(-1),
@@ -99,9 +118,14 @@
}
void ReceiveStatisticsProxy::UpdateHistograms() {
- RTC_HISTOGRAM_COUNTS_100000(
- "WebRTC.Video.ReceiveStreamLifetimeInSeconds",
- (clock_->TimeInMilliseconds() - start_ms_) / 1000);
+ int stream_duration_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
+ if (stats_.frame_counts.key_frames > 0 ||
+ stats_.frame_counts.delta_frames > 0) {
+ RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.ReceiveStreamLifetimeInSeconds",
+ stream_duration_sec);
+ LOG(LS_INFO) << "WebRTC.Video.ReceiveStreamLifetimeInSeconds "
+ << stream_duration_sec;
+ }
if (first_report_block_time_ms_ != -1 &&
((clock_->TimeInMilliseconds() - first_report_block_time_ms_) / 1000) >=
@@ -124,14 +148,7 @@
"WebRTC.Video.RenderSqrtPixelsPerSecond",
round(render_pixel_tracker_.ComputeTotalRate()));
}
- int width = render_width_counter_.Avg(kMinRequiredSamples);
- int height = render_height_counter_.Avg(kMinRequiredSamples);
- if (width != -1) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedWidthInPixels", width);
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedHeightInPixels", height);
- LOG(LS_INFO) << "WebRTC.Video.ReceivedWidthInPixels " << width;
- LOG(LS_INFO) << "WebRTC.Video.ReceivedHeightInPixels " << height;
- }
+
int sync_offset_ms = sync_offset_counter_.Avg(kMinRequiredSamples);
if (sync_offset_ms != -1) {
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.AVSyncOffsetInMs", sync_offset_ms);
@@ -189,52 +206,131 @@
if (delay_ms != -1)
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.OnewayDelayInMs", delay_ms);
- int e2e_delay_ms_video = e2e_delay_counter_video_.Avg(kMinRequiredSamples);
- if (e2e_delay_ms_video != -1) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.EndToEndDelayInMs",
- e2e_delay_ms_video);
- LOG(LS_INFO) << "WebRTC.Video.EndToEndDelayInMs " << e2e_delay_ms_video;
+ // Aggregate content_specific_stats_ by removing experiment or simulcast
+ // information;
+ std::map<VideoContentType, ContentSpecificStats> aggregated_stats;
+ for (auto it : content_specific_stats_) {
+ // Calculate simulcast specific metrics (".S0" ... ".S2" suffixes).
+ VideoContentType content_type = it.first;
+ if (videocontenttypehelpers::GetSimulcastId(content_type) > 0) {
+ // Aggregate on experiment id.
+ videocontenttypehelpers::SetExperimentId(&content_type, 0);
+ aggregated_stats[content_type].Add(it.second);
+ }
+ // Calculate experiment specific metrics (".ExperimentGroup[0-7]" suffixes).
+ content_type = it.first;
+ if (videocontenttypehelpers::GetExperimentId(content_type) > 0) {
+ // Aggregate on simulcast id.
+ videocontenttypehelpers::SetSimulcastId(&content_type, 0);
+ aggregated_stats[content_type].Add(it.second);
+ }
+ // Calculate aggregated metrics (no suffixes. Aggregated on everything).
+ content_type = it.first;
+ videocontenttypehelpers::SetSimulcastId(&content_type, 0);
+ videocontenttypehelpers::SetExperimentId(&content_type, 0);
+ aggregated_stats[content_type].Add(it.second);
}
- int e2e_delay_ms_screenshare =
- e2e_delay_counter_screenshare_.Avg(kMinRequiredSamples);
- if (e2e_delay_ms_screenshare != -1) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.Screenshare.EndToEndDelayInMs",
- e2e_delay_ms_screenshare);
- }
+ for (auto it : aggregated_stats) {
+ // For the metric Foo we report the following slices:
+ // WebRTC.Video.Foo,
+ // WebRTC.Video.Screenshare.Foo,
+ // WebRTC.Video.Foo.S[0-3],
+ // WebRTC.Video.Foo.ExperimentGroup[0-7],
+ // WebRTC.Video.Screenshare.Foo.S[0-3],
+ // WebRTC.Video.Screenshare.Foo.ExperimentGroup[0-7].
+ auto content_type = it.first;
+ auto stats = it.second;
+ std::string uma_prefix = UmaPrefixForContentType(content_type);
+ std::string uma_suffix = UmaSuffixForContentType(content_type);
+ // Metrics can be sliced on either simulcast id or experiment id but not
+ // both.
+ RTC_DCHECK(videocontenttypehelpers::GetExperimentId(content_type) == 0 ||
+ videocontenttypehelpers::GetSimulcastId(content_type) == 0);
- int e2e_delay_max_ms_video = e2e_delay_max_ms_video_;
- if (e2e_delay_max_ms_video != -1) {
- RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.EndToEndDelayMaxInMs",
- e2e_delay_max_ms_video);
- }
+ int e2e_delay_ms = stats.e2e_delay_counter.Avg(kMinRequiredSamples);
+ if (e2e_delay_ms != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".EndToEndDelayInMs" + uma_suffix, e2e_delay_ms);
+ LOG(LS_INFO) << uma_prefix << ".EndToEndDelayInMs" << uma_suffix << " "
+ << e2e_delay_ms;
+ }
+ int e2e_delay_max_ms = stats.e2e_delay_counter.Max();
+ if (e2e_delay_max_ms != -1 && e2e_delay_ms != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_100000(
+ uma_prefix + ".EndToEndDelayMaxInMs" + uma_suffix, e2e_delay_max_ms);
+ LOG(LS_INFO) << uma_prefix << ".EndToEndDelayMaxInMs" << uma_suffix << " "
+ << e2e_delay_max_ms;
+ }
+ int interframe_delay_ms =
+ stats.interframe_delay_counter.Avg(kMinRequiredSamples);
+ if (interframe_delay_ms != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".InterframeDelayInMs" + uma_suffix,
+ interframe_delay_ms);
+ LOG(LS_INFO) << uma_prefix << ".InterframeDelayInMs" << uma_suffix << " "
+ << interframe_delay_ms;
+ }
+ int interframe_delay_max_ms = stats.interframe_delay_counter.Max();
+ if (interframe_delay_max_ms != -1 && interframe_delay_ms != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".InterframeDelayMaxInMs" + uma_suffix,
+ interframe_delay_max_ms);
+ LOG(LS_INFO) << uma_prefix << ".InterframeDelayMaxInMs" << uma_suffix
+ << " " << interframe_delay_max_ms;
+ }
- int e2e_delay_max_ms_screenshare = e2e_delay_max_ms_screenshare_;
- if (e2e_delay_max_ms_screenshare != -1) {
- RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.Screenshare.EndToEndDelayMaxInMs",
- e2e_delay_max_ms_screenshare);
- }
+ int width = stats.received_width.Avg(kMinRequiredSamples);
+ if (width != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".ReceivedWidthInPixels" + uma_suffix, width);
+ LOG(LS_INFO) << uma_prefix << ".ReceivedWidthInPixels" << uma_suffix
+ << " " << width;
+ }
- int interframe_delay_ms_screenshare =
- interframe_delay_counter_screenshare_.Avg(kMinRequiredSamples);
- if (interframe_delay_ms_screenshare != -1) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.Screenshare.InterframeDelayInMs",
- interframe_delay_ms_screenshare);
- RTC_DCHECK_GE(interframe_delay_max_ms_screenshare_,
- interframe_delay_ms_screenshare);
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs",
- interframe_delay_max_ms_screenshare_);
- }
+ int height = stats.received_height.Avg(kMinRequiredSamples);
+ if (height != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".ReceivedHeightInPixels" + uma_suffix, height);
+ LOG(LS_INFO) << uma_prefix << ".ReceivedHeightInPixels" << uma_suffix
+ << " " << height;
+ }
- int interframe_delay_ms_video =
- interframe_delay_counter_video_.Avg(kMinRequiredSamples);
- if (interframe_delay_ms_video != -1) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InterframeDelayInMs",
- interframe_delay_ms_video);
- RTC_DCHECK_GE(interframe_delay_max_ms_video_, interframe_delay_ms_video);
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InterframeDelayMaxInMs",
- interframe_delay_max_ms_video_);
+ if (content_type != VideoContentType::UNSPECIFIED) {
+ // Don't report these 3 metrics unsliced, as more precise variants
+ // are reported separately in this method.
+ float flow_duration_sec = stats.flow_duration_ms / 1000.0;
+ if (flow_duration_sec >= metrics::kMinRunTimeInSeconds) {
+ int media_bitrate_kbps = static_cast<int>(stats.total_media_bytes * 8 /
+ flow_duration_sec / 1000);
+ RTC_HISTOGRAM_COUNTS_SPARSE_10000(
+ uma_prefix + ".MediaBitrateReceivedInKbps" + uma_suffix,
+ media_bitrate_kbps);
+ LOG(LS_INFO) << uma_prefix << ".MediaBitrateReceivedInKbps"
+ << uma_suffix << " " << media_bitrate_kbps;
+ }
+
+ int num_total_frames =
+ stats.frame_counts.key_frames + stats.frame_counts.delta_frames;
+ if (num_total_frames >= kMinRequiredSamples) {
+ int num_key_frames = stats.frame_counts.key_frames;
+ int key_frames_permille =
+ (num_key_frames * 1000 + num_total_frames / 2) / num_total_frames;
+ RTC_HISTOGRAM_COUNTS_SPARSE_1000(
+ uma_prefix + ".KeyFramesReceivedInPermille" + uma_suffix,
+ key_frames_permille);
+ LOG(LS_INFO) << uma_prefix << ".KeyFramesReceivedInPermille"
+ << uma_suffix << " " << key_frames_permille;
+ }
+
+ int qp = stats.qp_counter.Avg(kMinRequiredSamples);
+ if (qp != -1) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_200(
+ uma_prefix + ".Decoded.Vp8.Qp" + uma_suffix, qp);
+ LOG(LS_INFO) << uma_prefix << ".Decoded.Vp8.Qp" << uma_suffix << " "
+ << qp;
+ }
+ }
}
StreamDataCounters rtp = stats_.rtp_stats;
@@ -250,9 +346,12 @@
"WebRTC.Video.BitrateReceivedInKbps",
static_cast<int>(rtp_rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
1000));
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.MediaBitrateReceivedInKbps",
- static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000));
+ int media_bitrate_kbs =
+ static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000);
+ RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.MediaBitrateReceivedInKbps",
+ media_bitrate_kbs);
+ LOG(LS_INFO) << "WebRTC.Video.MediaBitrateReceivedInKbps "
+ << media_bitrate_kbs;
RTC_HISTOGRAM_COUNTS_10000(
"WebRTC.Video.PaddingBitrateReceivedInKbps",
static_cast<int>(rtp_rtx.transmitted.padding_bytes * 8 / elapsed_sec /
@@ -529,6 +628,9 @@
uint64_t now = clock_->TimeInMilliseconds();
rtc::CritScope lock(&crit_);
+
+ ContentSpecificStats* content_specific_stats =
+ &content_specific_stats_[content_type];
++stats_.frames_decoded;
if (qp) {
if (!stats_.qp_sum) {
@@ -540,6 +642,7 @@
stats_.qp_sum = rtc::Optional<uint64_t>(0);
}
*stats_.qp_sum += *qp;
+ content_specific_stats->qp_counter.Add(*qp);
} else if (stats_.qp_sum) {
LOG(LS_WARNING)
<< "QP sum was already set and no QP was given for a frame.";
@@ -551,17 +654,8 @@
int64_t interframe_delay_ms = now - *last_decoded_frame_time_ms_;
RTC_DCHECK_GE(interframe_delay_ms, 0);
interframe_delay_max_moving_.Add(interframe_delay_ms, now);
- if (last_content_type_ == VideoContentType::SCREENSHARE) {
- interframe_delay_counter_screenshare_.Add(interframe_delay_ms);
- if (interframe_delay_max_ms_screenshare_ < interframe_delay_ms) {
- interframe_delay_max_ms_screenshare_ = interframe_delay_ms;
- }
- } else {
- interframe_delay_counter_video_.Add(interframe_delay_ms);
- if (interframe_delay_max_ms_video_ < interframe_delay_ms) {
- interframe_delay_max_ms_video_ = interframe_delay_ms;
- }
- }
+ content_specific_stats->interframe_delay_counter.Add(interframe_delay_ms);
+ content_specific_stats->flow_duration_ms += interframe_delay_ms;
}
last_decoded_frame_time_ms_.emplace(now);
}
@@ -572,28 +666,22 @@
RTC_DCHECK_GT(width, 0);
RTC_DCHECK_GT(height, 0);
uint64_t now = clock_->TimeInMilliseconds();
-
rtc::CritScope lock(&crit_);
+ ContentSpecificStats* content_specific_stats =
+ &content_specific_stats_[last_content_type_];
renders_fps_estimator_.Update(1, now);
++stats_.frames_rendered;
stats_.width = width;
stats_.height = height;
- render_width_counter_.Add(width);
- render_height_counter_.Add(height);
render_fps_tracker_.AddSamples(1);
render_pixel_tracker_.AddSamples(sqrt(width * height));
+ content_specific_stats->received_width.Add(width);
+ content_specific_stats->received_height.Add(height);
if (frame.ntp_time_ms() > 0) {
int64_t delay_ms = clock_->CurrentNtpInMilliseconds() - frame.ntp_time_ms();
if (delay_ms >= 0) {
- if (last_content_type_ == VideoContentType::SCREENSHARE) {
- e2e_delay_max_ms_screenshare_ =
- std::max(delay_ms, e2e_delay_max_ms_screenshare_);
- e2e_delay_counter_screenshare_.Add(delay_ms);
- } else {
- e2e_delay_max_ms_video_ = std::max(delay_ms, e2e_delay_max_ms_video_);
- e2e_delay_counter_video_.Add(delay_ms);
- }
+ content_specific_stats->e2e_delay_counter.Add(delay_ms);
}
}
}
@@ -618,12 +706,24 @@
}
void ReceiveStatisticsProxy::OnCompleteFrame(bool is_keyframe,
- size_t size_bytes) {
+ size_t size_bytes,
+ VideoContentType content_type) {
rtc::CritScope lock(&crit_);
- if (is_keyframe)
+ if (is_keyframe) {
++stats_.frame_counts.key_frames;
- else
+ } else {
++stats_.frame_counts.delta_frames;
+ }
+
+ ContentSpecificStats* content_specific_stats =
+ &content_specific_stats_[content_type];
+
+ content_specific_stats->total_media_bytes += size_bytes;
+ if (is_keyframe) {
+ ++content_specific_stats->frame_counts.key_frames;
+ } else {
+ ++content_specific_stats->frame_counts.delta_frames;
+ }
int64_t now_ms = clock_->TimeInMilliseconds();
frame_window_.insert(std::make_pair(now_ms, size_bytes));
@@ -665,6 +765,16 @@
void ReceiveStatisticsProxy::SampleCounter::Add(int sample) {
sum += sample;
++num_samples;
+ if (!max || sample > *max) {
+ max.emplace(sample);
+ }
+}
+
+void ReceiveStatisticsProxy::SampleCounter::Add(const SampleCounter& other) {
+ sum += other.sum;
+ num_samples += other.num_samples;
+ if (other.max && (!max || *max < *other.max))
+ max = other.max;
}
int ReceiveStatisticsProxy::SampleCounter::Avg(
@@ -674,9 +784,14 @@
return static_cast<int>(sum / num_samples);
}
+int ReceiveStatisticsProxy::SampleCounter::Max() const {
+ return max.value_or(-1);
+}
+
void ReceiveStatisticsProxy::SampleCounter::Reset() {
num_samples = 0;
sum = 0;
+ max.reset();
}
void ReceiveStatisticsProxy::OnRttUpdate(int64_t avg_rtt_ms,
@@ -685,4 +800,17 @@
avg_rtt_ms_ = avg_rtt_ms;
}
+void ReceiveStatisticsProxy::ContentSpecificStats::Add(
+ const ContentSpecificStats& other) {
+ e2e_delay_counter.Add(other.e2e_delay_counter);
+ interframe_delay_counter.Add(other.interframe_delay_counter);
+ flow_duration_ms += other.flow_duration_ms;
+ total_media_bytes += other.total_media_bytes;
+ received_height.Add(other.received_height);
+ received_width.Add(other.received_width);
+ qp_counter.Add(other.qp_counter);
+ frame_counts.key_frames += other.frame_counts.key_frames;
+ frame_counts.delta_frames += other.frame_counts.delta_frames;
+}
+
} // namespace webrtc
diff --git a/webrtc/video/receive_statistics_proxy.h b/webrtc/video/receive_statistics_proxy.h
index 2e93d70..9a6807c 100644
--- a/webrtc/video/receive_statistics_proxy.h
+++ b/webrtc/video/receive_statistics_proxy.h
@@ -20,6 +20,7 @@
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
#include "webrtc/rtc_base/criticalsection.h"
#include "webrtc/rtc_base/moving_max_counter.h"
+#include "webrtc/rtc_base/optional.h"
#include "webrtc/rtc_base/rate_statistics.h"
#include "webrtc/rtc_base/ratetracker.h"
#include "webrtc/rtc_base/thread_annotations.h"
@@ -66,7 +67,9 @@
void OnReceiveRatesUpdated(uint32_t bitRate, uint32_t frameRate) override;
void OnFrameCountsUpdated(const FrameCounts& frame_counts) override;
void OnDiscardedPacketsUpdated(int discarded_packets) override;
- void OnCompleteFrame(bool is_keyframe, size_t size_bytes) override;
+ void OnCompleteFrame(bool is_keyframe,
+ size_t size_bytes,
+ VideoContentType content_type) override;
void OnFrameBufferTimingsUpdated(int decode_ms,
int max_decode_ms,
int current_delay_ms,
@@ -98,16 +101,33 @@
SampleCounter() : sum(0), num_samples(0) {}
void Add(int sample);
int Avg(int64_t min_required_samples) const;
+ int Max() const;
void Reset();
+ void Add(const SampleCounter& other);
private:
int64_t sum;
int64_t num_samples;
+ rtc::Optional<int> max;
};
+
struct QpCounters {
SampleCounter vp8;
};
+ struct ContentSpecificStats {
+ void Add(const ContentSpecificStats& other);
+
+ SampleCounter e2e_delay_counter;
+ SampleCounter interframe_delay_counter;
+ int64_t flow_duration_ms = 0;
+ int64_t total_media_bytes = 0;
+ SampleCounter received_width;
+ SampleCounter received_height;
+ SampleCounter qp_counter;
+ FrameCounts frame_counts;
+ };
+
void UpdateHistograms() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void QualitySample() EXCLUSIVE_LOCKS_REQUIRED(crit_);
@@ -140,24 +160,16 @@
rtc::RateTracker render_fps_tracker_ GUARDED_BY(crit_);
rtc::RateTracker render_pixel_tracker_ GUARDED_BY(crit_);
rtc::RateTracker total_byte_tracker_ GUARDED_BY(crit_);
- SampleCounter render_width_counter_ GUARDED_BY(crit_);
- SampleCounter render_height_counter_ GUARDED_BY(crit_);
SampleCounter sync_offset_counter_ GUARDED_BY(crit_);
SampleCounter decode_time_counter_ GUARDED_BY(crit_);
SampleCounter jitter_buffer_delay_counter_ GUARDED_BY(crit_);
SampleCounter target_delay_counter_ GUARDED_BY(crit_);
SampleCounter current_delay_counter_ GUARDED_BY(crit_);
SampleCounter delay_counter_ GUARDED_BY(crit_);
- SampleCounter e2e_delay_counter_video_ GUARDED_BY(crit_);
- SampleCounter e2e_delay_counter_screenshare_ GUARDED_BY(crit_);
- SampleCounter interframe_delay_counter_video_ GUARDED_BY(crit_);
- SampleCounter interframe_delay_counter_screenshare_ GUARDED_BY(crit_);
- int64_t e2e_delay_max_ms_video_ GUARDED_BY(crit_);
- int64_t e2e_delay_max_ms_screenshare_ GUARDED_BY(crit_);
- int64_t interframe_delay_max_ms_video_ GUARDED_BY(crit_);
- int64_t interframe_delay_max_ms_screenshare_ GUARDED_BY(crit_);
mutable rtc::MovingMaxCounter<int> interframe_delay_max_moving_
GUARDED_BY(crit_);
+ std::map<VideoContentType, ContentSpecificStats> content_specific_stats_
+ GUARDED_BY(crit_);
MaxCounter freq_offset_counter_ GUARDED_BY(crit_);
int64_t first_report_block_time_ms_ GUARDED_BY(crit_);
ReportBlockStats report_block_stats_ GUARDED_BY(crit_);
diff --git a/webrtc/video/receive_statistics_proxy_unittest.cc b/webrtc/video/receive_statistics_proxy_unittest.cc
index 98ec7ed..528ef09 100644
--- a/webrtc/video/receive_statistics_proxy_unittest.cc
+++ b/webrtc/video/receive_statistics_proxy_unittest.cc
@@ -203,7 +203,8 @@
TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsOnCompleteFrame) {
const int kFrameSizeBytes = 1000;
- statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes);
+ statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes,
+ VideoContentType::UNSPECIFIED);
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
EXPECT_EQ(1, stats.network_frame_rate);
EXPECT_EQ(1, stats.frame_counts.key_frames);
@@ -352,6 +353,8 @@
TEST_F(ReceiveStatisticsProxyTest, LifetimeHistogramIsUpdated) {
const int64_t kTimeSec = 3;
fake_clock_.AdvanceTimeMilliseconds(kTimeSec * 1000);
+ // Need at least one frame to report stream lifetime.
+ statistics_proxy_->OnCompleteFrame(true, 1000, VideoContentType::UNSPECIFIED);
// Histograms are updated when the statistics_proxy_ is deleted.
statistics_proxy_.reset();
EXPECT_EQ(1,
@@ -360,6 +363,17 @@
kTimeSec));
}
+TEST_F(ReceiveStatisticsProxyTest,
+ LifetimeHistogramNotReportedForEmptyStreams) {
+ const int64_t kTimeSec = 3;
+ fake_clock_.AdvanceTimeMilliseconds(kTimeSec * 1000);
+ // No frames received.
+ // Histograms are updated when the statistics_proxy_ is deleted.
+ statistics_proxy_.reset();
+ EXPECT_EQ(0,
+ metrics::NumSamples("WebRTC.Video.ReceiveStreamLifetimeInSeconds"));
+}
+
TEST_F(ReceiveStatisticsProxyTest, BadCallHistogramsAreUpdated) {
// Based on the tuning parameters this will produce 7 uncertain states,
// then 10 certainly bad states. There has to be 10 certain states before
@@ -533,7 +547,8 @@
const int kFrameSizeBytes = 1000;
for (int i = 0; i < kMinRequiredSamples - 1; ++i)
- statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes);
+ statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes,
+ VideoContentType::UNSPECIFIED);
EXPECT_EQ(0, statistics_proxy_->GetStats().frame_counts.key_frames);
EXPECT_EQ(kMinRequiredSamples - 1,
@@ -549,7 +564,8 @@
const int kFrameSizeBytes = 1000;
for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes);
+ statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes,
+ VideoContentType::UNSPECIFIED);
EXPECT_EQ(0, statistics_proxy_->GetStats().frame_counts.key_frames);
EXPECT_EQ(kMinRequiredSamples,
@@ -565,10 +581,12 @@
const int kFrameSizeBytes = 1000;
for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes);
+ statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes,
+ VideoContentType::UNSPECIFIED);
for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(false, kFrameSizeBytes);
+ statistics_proxy_->OnCompleteFrame(false, kFrameSizeBytes,
+ VideoContentType::UNSPECIFIED);
EXPECT_EQ(kMinRequiredSamples,
statistics_proxy_->GetStats().frame_counts.key_frames);
@@ -777,23 +795,18 @@
(kInterFrameDelayMs * (kMinRequiredSamples - 1) +
kInterFrameDelayMs * 2) /
kMinRequiredSamples;
- switch (content_type) {
- case VideoContentType::UNSPECIFIED:
- EXPECT_EQ(kExpectedInterFrame,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_EQ(kInterFrameDelayMs * 2,
- metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
- break;
- case VideoContentType::SCREENSHARE:
- EXPECT_EQ(
- kExpectedInterFrame,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_EQ(kInterFrameDelayMs * 2,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- break;
- default:
- RTC_NOTREACHED();
+ if (videocontenttypehelpers::IsScreenshare(content_type)) {
+ EXPECT_EQ(
+ kExpectedInterFrame,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
+ EXPECT_EQ(
+ kInterFrameDelayMs * 2,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
+ } else {
+ EXPECT_EQ(kExpectedInterFrame,
+ metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
+ EXPECT_EQ(kInterFrameDelayMs * 2,
+ metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
}
}
@@ -836,7 +849,7 @@
statistics_proxy_->OnDecodedFrame(rtc::Optional<uint8_t>(), content_type);
statistics_proxy_.reset();
- if (content_type == VideoContentType::SCREENSHARE) {
+ if (videocontenttypehelpers::IsScreenshare(content_type)) {
EXPECT_EQ(
1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
EXPECT_EQ(1, metrics::NumSamples(
@@ -857,4 +870,84 @@
}
}
+TEST_P(ReceiveStatisticsProxyTest, StatsAreSlicedOnSimulcastAndExperiment) {
+ VideoContentType content_type = GetParam();
+ const uint8_t experiment_id = 1;
+ videocontenttypehelpers::SetExperimentId(&content_type, experiment_id);
+ const int kInterFrameDelayMs1 = 30;
+ const int kInterFrameDelayMs2 = 50;
+
+ videocontenttypehelpers::SetSimulcastId(&content_type, 1);
+ for (int i = 0; i <= kMinRequiredSamples; ++i) {
+ fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs1);
+ statistics_proxy_->OnDecodedFrame(rtc::Optional<uint8_t>(), content_type);
+ }
+
+ videocontenttypehelpers::SetSimulcastId(&content_type, 2);
+ for (int i = 0; i <= kMinRequiredSamples; ++i) {
+ fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs2);
+ statistics_proxy_->OnDecodedFrame(rtc::Optional<uint8_t>(), content_type);
+ }
+ statistics_proxy_.reset();
+
+ if (videocontenttypehelpers::IsScreenshare(content_type)) {
+ EXPECT_EQ(
+ 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
+ EXPECT_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
+ EXPECT_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.Screenshare.InterframeDelayInMs.S0"));
+ EXPECT_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.Screenshare.InterframeDelayMaxInMs.S0"));
+ EXPECT_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.Screenshare.InterframeDelayInMs.S1"));
+ EXPECT_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.Screenshare.InterframeDelayMaxInMs.S1"));
+ EXPECT_EQ(1,
+ metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"
+ ".ExperimentGroup0"));
+ EXPECT_EQ(
+ 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"
+ ".ExperimentGroup0"));
+ EXPECT_EQ(
+ kInterFrameDelayMs1,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs.S0"));
+ EXPECT_EQ(
+ kInterFrameDelayMs2,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs.S1"));
+ EXPECT_EQ(
+ (kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
+ EXPECT_EQ(
+ kInterFrameDelayMs2,
+ metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
+ EXPECT_EQ(
+ (kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
+ metrics::MinSample(
+ "WebRTC.Video.Screenshare.InterframeDelayInMs.ExperimentGroup0"));
+ } else {
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs.S0"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs.S0"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs.S1"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs.S1"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"
+ ".ExperimentGroup0"));
+ EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"
+ ".ExperimentGroup0"));
+ EXPECT_EQ(kInterFrameDelayMs1,
+ metrics::MinSample("WebRTC.Video.InterframeDelayInMs.S0"));
+ EXPECT_EQ(kInterFrameDelayMs2,
+ metrics::MinSample("WebRTC.Video.InterframeDelayInMs.S1"));
+ EXPECT_EQ((kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
+ metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
+ EXPECT_EQ(kInterFrameDelayMs2,
+ metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
+ EXPECT_EQ((kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
+ metrics::MinSample(
+ "WebRTC.Video.InterframeDelayInMs.ExperimentGroup0"));
+ }
+}
+
} // namespace webrtc
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index 9a7429f..ce7a0ee 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -318,8 +318,8 @@
if (!header.markerBit)
return SEND_PACKET;
EXPECT_TRUE(header.extension.hasVideoContentType);
- EXPECT_EQ(VideoContentType::SCREENSHARE,
- header.extension.videoContentType);
+ EXPECT_TRUE(videocontenttypehelpers::IsScreenshare(
+ header.extension.videoContentType));
observation_complete_.Set();
return SEND_PACKET;
}
diff --git a/webrtc/video/video_stream_decoder.cc b/webrtc/video/video_stream_decoder.cc
index 915eccf..009a18c 100644
--- a/webrtc/video/video_stream_decoder.cc
+++ b/webrtc/video/video_stream_decoder.cc
@@ -122,7 +122,9 @@
void VideoStreamDecoder::OnTimingFrameInfoUpdated(const TimingFrameInfo& info) {
}
-void VideoStreamDecoder::OnCompleteFrame(bool is_keyframe, size_t size_bytes) {}
+void VideoStreamDecoder::OnCompleteFrame(bool is_keyframe,
+ size_t size_bytes,
+ VideoContentType content_type) {}
void VideoStreamDecoder::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
video_receiver_->SetReceiveChannelParameters(max_rtt_ms);
diff --git a/webrtc/video/video_stream_decoder.h b/webrtc/video/video_stream_decoder.h
index d5103ee..6ea722c 100644
--- a/webrtc/video/video_stream_decoder.h
+++ b/webrtc/video/video_stream_decoder.h
@@ -69,7 +69,9 @@
void OnReceiveRatesUpdated(uint32_t bit_rate, uint32_t frame_rate) override;
void OnDiscardedPacketsUpdated(int discarded_packets) override;
void OnFrameCountsUpdated(const FrameCounts& frame_counts) override;
- void OnCompleteFrame(bool is_keyframe, size_t size_bytes) override;
+ void OnCompleteFrame(bool is_keyframe,
+ size_t size_bytes,
+ VideoContentType content_type) override;
void OnFrameBufferTimingsUpdated(int decode_ms,
int max_decode_ms,
int current_delay_ms,