1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/remoting/metrics.h"
7 #include "base/metrics/histogram_functions.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "media/base/sample_rates.h"
16 ////////////////////////////////////////////////////////////////////////////////
17 // BEGIN: These were all borrowed from src/media/filters/ffmpeg_demuxer.cc.
18 // Some videos just want to watch the world burn, with a height of 0; cap the
19 // "infinite" aspect ratio resulting.
20 constexpr int kInfiniteRatio = 99999;
22 // Common aspect ratios (multiplied by 100 and truncated) used for histogramming
23 // video sizes. These were taken on 20111103 from
24 // http://wikipedia.org/wiki/Aspect_ratio_(image)#Previous_and_currently_used_aspect_ratios
25 constexpr int kCommonAspectRatios100[] = {
26 100, 115, 133, 137, 143, 150, 155, 160, 166,
27 175, 177, 185, 200, 210, 220, 221, 235, 237,
28 240, 255, 259, 266, 276, 293, 400, 1200, kInfiniteRatio,
31 // END: Code borrowed from src/media/filter/ffmpeg_demuxer.cc.
32 ////////////////////////////////////////////////////////////////////////////////
34 // Buckets for video width histograms.
35 constexpr int kVideoWidthBuckets[] = {
36 180, 240, 320, 480, 640, 720, 872, 940, 1280,
37 1440, 1600, 1760, 1920, 2560, 3840, 7680, 16384,
42 SessionMetricsRecorder::SessionMetricsRecorder()
43 : last_audio_codec_(AudioCodec::kUnknown),
44 last_channel_layout_(CHANNEL_LAYOUT_NONE),
46 last_video_codec_(VideoCodec::kUnknown),
47 last_video_profile_(VIDEO_CODEC_PROFILE_UNKNOWN) {}
49 SessionMetricsRecorder::~SessionMetricsRecorder() = default;
51 void SessionMetricsRecorder::WillStartSession(StartTrigger trigger) {
52 DCHECK(!start_trigger_);
53 start_trigger_ = trigger;
54 start_time_ = base::TimeTicks::Now();
57 void SessionMetricsRecorder::DidStartSession() {
58 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.SessionStartTrigger",
59 *start_trigger_, START_TRIGGER_MAX + 1);
60 if (last_audio_codec_ != AudioCodec::kUnknown)
61 RecordAudioConfiguration();
62 if (last_video_codec_ != VideoCodec::kUnknown)
63 RecordVideoConfiguration();
64 RecordTrackConfiguration();
67 void SessionMetricsRecorder::StartSessionFailed(
68 mojom::RemotingStartFailReason reason) {
69 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.SessionStartFailedReason", reason);
72 void SessionMetricsRecorder::WillStopSession(StopTrigger trigger) {
76 // Record what triggered the end of the session.
77 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.SessionStopTrigger", trigger,
78 STOP_TRIGGER_MAX + 1);
80 // Record the session duration.
81 const base::TimeDelta session_duration = base::TimeTicks::Now() - start_time_;
82 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.SessionDuration", session_duration,
83 base::Seconds(15), base::Hours(12), 50);
85 if (session_duration <= base::Seconds(15)) {
86 // Record the session duration in finer scale for short sessions
87 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.ShortSessionDuration",
88 session_duration, base::Seconds(0.1),
89 base::Seconds(15), 60);
91 if (session_duration <= base::Seconds(0.1)) {
92 UMA_HISTOGRAM_ENUMERATION(
93 "Media.Remoting.SessionStopTrigger.Duration0To100MilliSec", trigger,
94 STOP_TRIGGER_MAX + 1);
95 } else if (session_duration <= base::Seconds(1)) {
96 UMA_HISTOGRAM_ENUMERATION(
97 "Media.Remoting.SessionStopTrigger.Duration100MilliSecTo1Sec",
98 trigger, STOP_TRIGGER_MAX + 1);
99 } else if (session_duration <= base::Seconds(3)) {
100 UMA_HISTOGRAM_ENUMERATION(
101 "Media.Remoting.SessionStopTrigger.Duration1To3Sec", trigger,
102 STOP_TRIGGER_MAX + 1);
103 } else if (session_duration <= base::Seconds(5)) {
104 UMA_HISTOGRAM_ENUMERATION(
105 "Media.Remoting.SessionStopTrigger.Duration3To5Sec", trigger,
106 STOP_TRIGGER_MAX + 1);
107 } else if (session_duration <= base::Seconds(10)) {
108 UMA_HISTOGRAM_ENUMERATION(
109 "Media.Remoting.SessionStopTrigger.Duration5To10Sec", trigger,
110 STOP_TRIGGER_MAX + 1);
112 UMA_HISTOGRAM_ENUMERATION(
113 "Media.Remoting.SessionStopTrigger.Duration10To15Sec", trigger,
114 STOP_TRIGGER_MAX + 1);
118 // Reset |start_trigger_| since metrics recording of the current remoting
119 // session has now completed.
120 start_trigger_ = absl::nullopt;
123 void SessionMetricsRecorder::OnPipelineMetadataChanged(
124 const PipelineMetadata& metadata) {
125 if (metadata.has_audio && metadata.audio_decoder_config.IsValidConfig()) {
126 const auto& config = metadata.audio_decoder_config;
127 // While in a remoting session, record audio configuration changes.
128 const bool need_to_record_audio_configuration =
129 start_trigger_ && (config.codec() != last_audio_codec_ ||
130 config.channel_layout() != last_channel_layout_ ||
131 config.samples_per_second() != last_sample_rate_);
132 last_audio_codec_ = config.codec();
133 last_channel_layout_ = config.channel_layout();
134 last_sample_rate_ = config.samples_per_second();
135 if (need_to_record_audio_configuration)
136 RecordAudioConfiguration();
138 last_audio_codec_ = AudioCodec::kUnknown;
139 last_channel_layout_ = CHANNEL_LAYOUT_NONE;
140 last_sample_rate_ = 0;
143 if (metadata.has_video && metadata.video_decoder_config.IsValidConfig()) {
144 const auto& config = metadata.video_decoder_config;
145 // While in a remoting session, record video configuration changes.
146 const bool need_to_record_video_configuration =
147 start_trigger_ && (config.codec() != last_video_codec_ ||
148 config.profile() != last_video_profile_ ||
149 metadata.natural_size != last_natural_size_);
150 last_video_codec_ = config.codec();
151 last_video_profile_ = config.profile();
152 last_natural_size_ = metadata.natural_size;
153 if (need_to_record_video_configuration)
154 RecordVideoConfiguration();
156 last_video_codec_ = VideoCodec::kUnknown;
157 last_video_profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;
158 last_natural_size_ = gfx::Size();
161 // While in a remoting session, record whether audio or video media streams
164 RecordTrackConfiguration();
167 void SessionMetricsRecorder::OnRemotePlaybackDisabled(bool disabled) {
168 if (remote_playback_is_disabled_ &&
169 disabled == remote_playback_is_disabled_.value()) {
170 return; // De-dupe redundant notifications.
172 UMA_HISTOGRAM_BOOLEAN("Media.Remoting.RemotePlaybackEnabledByPage",
174 remote_playback_is_disabled_ = disabled;
177 void SessionMetricsRecorder::RecordVideoPixelRateSupport(
178 PixelRateSupport support) {
179 if (did_record_pixel_rate_support_) {
182 did_record_pixel_rate_support_ = true;
183 base::UmaHistogramEnumeration("Media.Remoting.VideoPixelRateSupport",
187 void SessionMetricsRecorder::RecordCompatibility(
188 RemotingCompatibility compatibility) {
189 if (did_record_compatibility_) {
192 did_record_compatibility_ = true;
193 base::UmaHistogramEnumeration("Media.Remoting.Compatibility", compatibility);
196 void SessionMetricsRecorder::RecordAudioConfiguration() {
197 base::UmaHistogramEnumeration("Media.Remoting.AudioCodec", last_audio_codec_);
198 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.AudioChannelLayout",
199 last_channel_layout_, CHANNEL_LAYOUT_MAX + 1);
201 if (ToAudioSampleRate(last_sample_rate_, &asr)) {
202 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.AudioSamplesPerSecond", asr,
203 kAudioSampleRateMax + 1);
205 UMA_HISTOGRAM_COUNTS_1M("Media.Remoting.AudioSamplesPerSecondUnexpected",
210 void SessionMetricsRecorder::RecordVideoConfiguration() {
211 base::UmaHistogramEnumeration("Media.Remoting.VideoCodec", last_video_codec_);
212 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.VideoCodecProfile",
213 last_video_profile_, VIDEO_CODEC_PROFILE_MAX + 1);
214 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
215 "Media.Remoting.VideoNaturalWidth", last_natural_size_.width(),
216 base::CustomHistogram::ArrayToCustomEnumRanges(kVideoWidthBuckets));
217 // Intentionally use integer division to truncate the result.
218 const int aspect_ratio_100 =
219 last_natural_size_.height()
220 ? (last_natural_size_.width() * 100) / last_natural_size_.height()
222 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
223 "Media.Remoting.VideoAspectRatio", aspect_ratio_100,
224 base::CustomHistogram::ArrayToCustomEnumRanges(kCommonAspectRatios100));
227 void SessionMetricsRecorder::RecordTrackConfiguration() {
228 TrackConfiguration config = NEITHER_AUDIO_NOR_VIDEO;
229 if (last_audio_codec_ != AudioCodec::kUnknown)
231 if (last_video_codec_ != VideoCodec::kUnknown) {
232 if (config == AUDIO_ONLY)
233 config = AUDIO_AND_VIDEO;
237 UMA_HISTOGRAM_ENUMERATION("Media.Remoting.TrackConfiguration", config,
238 TRACK_CONFIGURATION_MAX + 1);
241 RendererMetricsRecorder::RendererMetricsRecorder()
242 : start_time_(base::TimeTicks::Now()) {}
244 RendererMetricsRecorder::~RendererMetricsRecorder() = default;
246 void RendererMetricsRecorder::OnRendererInitialized() {
247 const base::TimeDelta elapsed_since_start =
248 base::TimeTicks::Now() - start_time_;
249 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.TimeUntilRemoteInitialized",
250 elapsed_since_start, base::Milliseconds(10),
251 base::Seconds(30), 50);
254 void RendererMetricsRecorder::OnEvidenceOfPlayoutAtReceiver() {
255 if (did_record_first_playout_)
257 const base::TimeDelta elapsed_since_start =
258 base::TimeTicks::Now() - start_time_;
259 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.TimeUntilFirstPlayout",
260 elapsed_since_start, base::Milliseconds(10),
261 base::Seconds(30), 50);
262 did_record_first_playout_ = true;
265 void RendererMetricsRecorder::OnAudioRateEstimate(int kilobits_per_second) {
266 UMA_HISTOGRAM_CUSTOM_COUNTS("Media.Remoting.AudioBitrate",
267 kilobits_per_second, 1, 1024, 50);
270 void RendererMetricsRecorder::OnVideoRateEstimate(int kilobits_per_second) {
271 UMA_HISTOGRAM_CUSTOM_COUNTS("Media.Remoting.VideoBitrate",
272 kilobits_per_second, 1, 16 * 1024, 50);
275 } // namespace remoting