Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(515)

Side by Side Diff: third_party/WebKit/Source/core/html/shadow/MediaControlTimelineMetrics.cpp

Issue 2779273003: [Media Controls] Add UMA for timeline scrubber (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/html/shadow/MediaControlTimelineMetrics.h"
6
7 #include <stdint.h>
8 #include <cmath>
9 #include <limits>
10 #include "platform/KeyboardCodes.h"
11 #include "platform/wtf/StdLibExtras.h"
12
13 namespace blink {
14
15 namespace {
16
17 // Correponds to UMA MediaTimelineSeekType enum. Enum values can be added, but
18 // must never be renumbered or deleted and reused.
19 enum class SeekType {
20 kClick = 0,
21 kDragFromCurrentPosition = 1,
22 kDragFromElsewhere = 2,
23 kKeyboardArrowKey = 3,
24 kKeyboardPageUpDownKey = 4,
25 kKeyboardHomeEndKey = 5,
26 // Update kLast when adding new values.
27 kLast = kKeyboardHomeEndKey
28 };
29
30 // Inclusive upper bounds for the positive buckets of UMA MediaTimelinePercent
31 // enum, which are reflected to form the negative buckets. Values must not be
32 // added, modified, or removed.
33 constexpr double kPercentIntervals[] = {
34 0, 0.1, 0.2, 0.3, 0.5, 0.7, 1.0, 1.5, 2.0,
35 3.0, 5.0, 7.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0,
36 40.0, 45.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0};
37 constexpr int32_t kPercentBucketCount = 51;
38 static_assert(arraysize(kPercentIntervals) * 2 == 1 + kPercentBucketCount,
39 "Intervals must match UMA MediaTimelinePercent enum");
40
41 // Corresponds two UMA enums! Values are the inclusive upper bounds for buckets
42 // of UMA MediaTimelineAbsTimeDelta enum with the same index, and also for the
43 // positive buckets of UMA MediaTimelineTimeDelta enum, which are reflected to
44 // form the negative buckets. Values must not be added, modified, or removed.
45 constexpr double kTimeDeltaMSIntervals[] = {
46 0, // 0
47 16, // 16ms
48 32, // 32ms
49 64, // 64ms
50 128, // 128ms
51 256, // 256ms
52 512, // 512ms
53 1000, // 1s
54 2000, // 2s
55 4000, // 4s
56 8000, // 8s
57 15000, // 15s
58 30000, // 30s
59 60000, // 1m
60 120000, // 2m
61 240000, // 4m
62 480000, // 8m
63 900000, // 15m
64 1800000, // 30m
65 3600000, // 1h
66 7200000, // 2h
67 14400000, // 4h
68 28800000, // 8h
69 57600000, // 16h
70 std::numeric_limits<double>::infinity()};
71 constexpr int32_t kAbsTimeDeltaBucketCount = 25;
72 constexpr int32_t kTimeDeltaBucketCount = 49;
mlamouri (slow - plz ping) 2017/03/31 11:39:40 (kAbsTimeDeltaBucketCount * 2) - 1
johnme 2017/03/31 13:47:23 I'd rather not compute this, because it's potentia
73 static_assert(arraysize(kTimeDeltaMSIntervals) == kAbsTimeDeltaBucketCount,
74 "Intervals must match UMA MediaTimelineAbsTimeDelta enum");
75 static_assert(arraysize(kTimeDeltaMSIntervals) * 2 == 1 + kTimeDeltaBucketCount,
76 "Intervals must match UMA MediaTimelineTimeDelta enum");
mlamouri (slow - plz ping) 2017/03/31 11:39:40 If you do the change above, it would make this sta
johnme 2017/03/31 13:47:23 Ack. See above though.
77
78 int32_t toPercentSample(double percent) {
79 constexpr int32_t negativeBucketCount = arraysize(kPercentIntervals) - 1;
80 bool negative = percent < 0;
81 double absPercent = std::abs(percent);
82 for (int32_t i = 0; i < static_cast<int>(arraysize(kPercentIntervals)); i++) {
83 if (absPercent <= kPercentIntervals[i])
84 return negativeBucketCount + (negative ? -i : +i);
85 }
86 // No NOTREACHED since percent may exit -100..100 due to floating point error.
87 return negative ? 0 : kPercentBucketCount - 1;
88 }
89
90 int32_t toAbsTimeDeltaSample(double sumAbsDeltaSeconds) {
91 double sumAbsDeltaMS = 1000 * sumAbsDeltaSeconds;
92 for (int32_t i = 0; i < kAbsTimeDeltaBucketCount; i++) {
93 if (sumAbsDeltaMS <= kTimeDeltaMSIntervals[i])
94 return i;
95 }
96 NOTREACHED();
97 return kAbsTimeDeltaBucketCount - 1;
98 }
99
100 int32_t toTimeDeltaSample(double deltaSeconds) {
101 constexpr int32_t negativeBucketCount = arraysize(kTimeDeltaMSIntervals) - 1;
102 bool negative = deltaSeconds < 0;
103 double absDeltaMS = 1000 * std::abs(deltaSeconds);
104 for (int32_t i = 0; i < static_cast<int>(arraysize(kTimeDeltaMSIntervals));
105 i++) {
106 if (absDeltaMS <= kTimeDeltaMSIntervals[i])
107 return negativeBucketCount + (negative ? -i : +i);
108 }
109 NOTREACHED();
110 return negative ? 0 : kTimeDeltaBucketCount - 1;
111 }
112
113 #define ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, minWidth, maxWidth, metric, \
114 sample, HistogramType, ...) \
115 else if (width >= minWidth) { \
116 DEFINE_STATIC_LOCAL( \
117 HistogramType, metric##minWidth##_##maxWidth##Histogram, \
118 ("Media.Timeline." #metric "." #minWidth "_" #maxWidth, \
119 ##__VA_ARGS__)); \
120 metric##minWidth##_##maxWidth##Histogram.count(sample); \
121 }
122
123 #define RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, metric, sample, \
124 HistogramType, ...) \
125 do { \
126 int width = timelineWidth; /* Avoid multiple evaluation. */ \
127 if (false) { \
128 } \
mlamouri (slow - plz ping) 2017/03/31 11:39:40 Can you add a comment explaining that the if (fals
johnme 2017/03/31 13:47:23 Done.
129 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 512, inf, metric, sample, \
130 HistogramType, ##__VA_ARGS__) \
131 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 256, 511, metric, sample, \
132 HistogramType, ##__VA_ARGS__) \
133 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 128, 255, metric, sample, \
134 HistogramType, ##__VA_ARGS__) \
135 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 80, 127, metric, sample, \
136 HistogramType, ##__VA_ARGS__) \
137 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 48, 79, metric, sample, \
138 HistogramType, ##__VA_ARGS__) \
139 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 32, 47, metric, sample, \
140 HistogramType, ##__VA_ARGS__) \
141 else { \
142 /* Skip logging if timeline is narrower than minimum suffix bucket. */ \
143 /* If this happens a lot, it'll show up in Media.Timeline.Width. */ \
144 } \
145 } while (false)
146
147 void recordDragGestureDurationByWidth(int timelineWidth, TimeDelta duration) {
148 int32_t sample = static_cast<int32_t>(duration.InMilliseconds());
mlamouri (slow - plz ping) 2017/03/31 11:39:40 Here and below, why is it int32_t?
johnme 2017/03/31 13:47:23 For samples, base::HistogramBase::Sample is typede
149 RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, DragGestureDuration, sample,
150 CustomCountHistogram, 1 /* 1 ms */,
151 60000 /* 1 minute */, 50);
152 }
153 void recordDragPercentByWidth(int timelineWidth, double percent) {
154 int32_t sample = toPercentSample(percent);
155 RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, DragPercent, sample,
156 EnumerationHistogram, kPercentBucketCount);
157 }
158 void recordDragSumAbsTimeDeltaByWidth(int timelineWidth,
159 double sumAbsDeltaSeconds) {
160 int32_t sample = toAbsTimeDeltaSample(sumAbsDeltaSeconds);
161 RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, DragSumAbsTimeDelta, sample,
162 EnumerationHistogram, kAbsTimeDeltaBucketCount);
163 }
164 void recordDragTimeDeltaByWidth(int timelineWidth, double deltaSeconds) {
165 int32_t sample = toTimeDeltaSample(deltaSeconds);
166 RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, DragTimeDelta, sample,
167 EnumerationHistogram, kTimeDeltaBucketCount);
168 }
169 void recordSeekTypeByWidth(int timelineWidth, SeekType type) {
170 int32_t sample = static_cast<int>(type);
171 constexpr int bucketCount = static_cast<int>(SeekType::kLast) + 1;
172 RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, SeekType, sample,
173 EnumerationHistogram, bucketCount);
174 }
175
176 #undef RECORD_TIMELINE_UMA_BY_WIDTH
177 #undef ELSEIF_WIDTH_RECORD_TIMELINE_UMA
178
179 } // namespace
180
181 void MediaControlTimelineMetrics::startGesture(bool fromThumb) {
182 // Initialize gesture tracking.
183 m_state = fromThumb ? State::kGestureFromThumb : State::kGestureFromElsewhere;
184 m_dragStartTimeTicks = TimeTicks::Now();
185 m_dragDeltaMediaSeconds = 0;
186 m_dragSumAbsDeltaMediaSeconds = 0;
187 }
188
189 void MediaControlTimelineMetrics::onInput(double fromSeconds,
190 double toSeconds) {
191 switch (m_state) {
192 case State::kInactive:
193 // Unexpected input.
194 m_state = State::kInactive;
195 break;
196 case State::kGestureFromThumb:
197 // Drag confirmed now input has been received.
198 m_state = State::kDragFromThumb;
199 break;
200 case State::kGestureFromElsewhere:
201 // Click/drag confirmed now input has been received. Assume it's a click
202 // until further input is received.
203 m_state = State::kClick;
204 break;
205 case State::kClick:
206 // Drag confirmed now further input has been received.
207 m_state = State::kDragFromElsewhere;
208 break;
209 case State::kDragFromThumb:
210 case State::kDragFromElsewhere:
211 // Continue tracking drag.
212 break;
213 case State::kKeyDown:
214 // Continue tracking key.
215 break;
216 }
217
218 // The following tracking is only for drags. Note that we exclude kClick here,
219 // as even if it progresses to a kDragFromElsewhere, the first input event
220 // corresponds to the position jump from the pointer down on the track.
221 if (m_state != State::kDragFromThumb && m_state != State::kDragFromElsewhere)
222 return;
223
224 float deltaMediaSeconds = static_cast<float>(toSeconds - fromSeconds);
225 m_dragDeltaMediaSeconds += deltaMediaSeconds;
226 m_dragSumAbsDeltaMediaSeconds += std::abs(deltaMediaSeconds);
227 }
228
229 void MediaControlTimelineMetrics::recordEndGesture(
230 int timelineWidth,
231 double mediaDurationSeconds) {
232 State endState = m_state;
233 m_state = State::kInactive; // Reset tracking.
234
235 SeekType seekType;
236 switch (endState) {
237 case State::kInactive:
238 case State::kKeyDown:
239 return; // Pointer and keys were interleaved. Skip UMA in this edge case.
240 case State::kGestureFromThumb:
241 case State::kGestureFromElsewhere:
242 return; // Empty gesture with no calls to gestureInput.
243 case State::kDragFromThumb:
244 seekType = SeekType::kDragFromCurrentPosition;
245 break;
246 case State::kClick:
247 seekType = SeekType::kClick;
248 break;
249 case State::kDragFromElsewhere:
250 seekType = SeekType::kDragFromElsewhere;
251 break;
252 }
253
254 recordSeekTypeByWidth(timelineWidth, seekType);
255
256 if (seekType == SeekType::kClick)
257 return; // Metrics below are only for drags.
258
259 recordDragGestureDurationByWidth(timelineWidth,
260 TimeTicks::Now() - m_dragStartTimeTicks);
261 if (std::isfinite(mediaDurationSeconds)) {
262 recordDragPercentByWidth(
263 timelineWidth, 100.0 * m_dragDeltaMediaSeconds / mediaDurationSeconds);
264 }
265 recordDragSumAbsTimeDeltaByWidth(timelineWidth,
266 m_dragSumAbsDeltaMediaSeconds);
267 recordDragTimeDeltaByWidth(timelineWidth, m_dragDeltaMediaSeconds);
268 }
269
270 void MediaControlTimelineMetrics::startKey() {
271 m_state = State::kKeyDown;
272 }
273
274 void MediaControlTimelineMetrics::recordEndKey(int timelineWidth, int keyCode) {
275 State endState = m_state;
276 m_state = State::kInactive; // Reset tracking.
277 if (endState != State::kKeyDown)
278 return; // Pointer and keys were interleaved. Skip UMA in this edge case.
279
280 SeekType type;
281 switch (keyCode) {
282 case VKEY_UP:
283 case VKEY_DOWN:
284 case VKEY_LEFT:
285 case VKEY_RIGHT:
286 type = SeekType::kKeyboardArrowKey;
287 break;
288 case VKEY_PRIOR: // PageUp
289 case VKEY_NEXT: // PageDown
290 type = SeekType::kKeyboardPageUpDownKey;
291 break;
292 case VKEY_HOME:
293 case VKEY_END:
294 type = SeekType::kKeyboardHomeEndKey;
295 break;
296 default:
297 return; // Other keys don't seek (at time of writing).
298 }
299 recordSeekTypeByWidth(timelineWidth, type);
300 }
301
302 void MediaControlTimelineMetrics::recordWidthOnFirstPlay(bool isFullscreen,
303 bool isPortrait,
304 int timelineWidth) {
305 constexpr int min = 1;
306 constexpr int max = 7680; // Timeline unlikely to be wider than 8K monitors.
mlamouri (slow - plz ping) 2017/03/31 11:39:40 +1 with the comment, especially in CSS pixels.
johnme 2017/03/31 13:47:23 Changed to "Equivalent to an 80inch wide 8K monito
307 constexpr int bucketCount = 50;
308 // Record merged histogram for all configurations.
309 DEFINE_STATIC_LOCAL(CustomCountHistogram, allConfigurationsWidthHistogram,
310 ("Media.Timeline.Width", min, max, bucketCount));
311 allConfigurationsWidthHistogram.count(timelineWidth);
312 // Record configuration-specific histogram.
313 if (!isFullscreen) {
314 if (isPortrait) {
315 DEFINE_STATIC_LOCAL(
316 CustomCountHistogram, widthHistogram,
317 ("Media.Timeline.Width.InlinePortrait", min, max, bucketCount));
318 widthHistogram.count(timelineWidth);
319 } else {
320 DEFINE_STATIC_LOCAL(
321 CustomCountHistogram, widthHistogram,
322 ("Media.Timeline.Width.InlineLandscape", min, max, bucketCount));
323 widthHistogram.count(timelineWidth);
324 }
325 } else {
326 if (isPortrait) {
327 DEFINE_STATIC_LOCAL(
328 CustomCountHistogram, widthHistogram,
329 ("Media.Timeline.Width.FullscreenPortrait", min, max, bucketCount));
330 widthHistogram.count(timelineWidth);
331 } else {
332 DEFINE_STATIC_LOCAL(
333 CustomCountHistogram, widthHistogram,
334 ("Media.Timeline.Width.FullscreenLandscape", min, max, bucketCount));
335 widthHistogram.count(timelineWidth);
336 }
337 }
338 }
339
340 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698