OLD | NEW |
---|---|
(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 | |
OLD | NEW |