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

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

Issue 2820343002: Media Controls: move timeline related code to modules. (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 // Exclusive upper bounds for the positive buckets of UMA MediaTimelinePercent
31 // enum, which are reflected to form the negative buckets. The custom enum is
32 // because UMA count histograms don't support negative values. Values must not
33 // be added/modified/removed due to the way the negative buckets are formed.
34 constexpr double kPercentIntervals[] = {
35 0, // Dedicated zero bucket so upper bound is inclusive, unlike the others.
36 0.1, 0.2, 0.3, 0.5, 0.7, 1.0, 1.5, 2.0, 3.0, 5.0, 7.0, 10.0,
37 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 60.0, 70.0, 80.0, 90.0,
38 100.0 // 100% upper bound is inclusive, unlike the others.
39 };
40 // Must match length of UMA MediaTimelinePercent enum.
41 constexpr int32_t kPercentBucketCount = 51;
42 static_assert(arraysize(kPercentIntervals) * 2 - 1 == kPercentBucketCount,
43 "Intervals must match UMA MediaTimelinePercent enum");
44
45 // Corresponds to two UMA enums of different sizes! Values are the exclusive
46 // upper bounds for buckets of UMA MediaTimelineAbsTimeDelta enum with the same
47 // index, and also for the positive buckets of UMA MediaTimelineTimeDelta enum,
48 // which are reflected to form the negative buckets. MediaTimelineTimeDelta
49 // needed a custom enum because UMA count histograms don't support negative
50 // values, and MediaTimelineAbsTimeDelta uses the same mechanism so the values
51 // can be compared easily. Values must not be added/modified/removed due to the
52 // way the negative buckets are formed.
53 constexpr double kTimeDeltaMSIntervals[] = {
54 1, // 1ms
55 16, // 16ms
56 32, // 32ms
57 64, // 64ms
58 128, // 128ms
59 256, // 256ms
60 512, // 512ms
61 1000, // 1s
62 2000, // 2s
63 4000, // 4s
64 8000, // 8s
65 15000, // 15s
66 30000, // 30s
67 60000, // 1m
68 120000, // 2m
69 240000, // 4m
70 480000, // 8m
71 900000, // 15m
72 1800000, // 30m
73 3600000, // 1h
74 7200000, // 2h
75 14400000, // 4h
76 28800000, // 8h
77 57600000, // 16h
78 std::numeric_limits<double>::infinity()};
79 // Must match length of UMA MediaTimelineAbsTimeDelta enum.
80 constexpr int32_t kAbsTimeDeltaBucketCount = 25;
81 // Must match length of UMA MediaTimelineTimeDelta enum.
82 constexpr int32_t kTimeDeltaBucketCount = 49;
83 static_assert(arraysize(kTimeDeltaMSIntervals) == kAbsTimeDeltaBucketCount,
84 "Intervals must match UMA MediaTimelineAbsTimeDelta enum");
85 static_assert(arraysize(kTimeDeltaMSIntervals) * 2 - 1 == kTimeDeltaBucketCount,
86 "Intervals must match UMA MediaTimelineTimeDelta enum");
87
88 // Calculates index of UMA MediaTimelinePercent enum corresponding to |percent|.
89 // Negative values use kPercentIntervals in reverse.
90 int32_t ToPercentSample(double percent) {
91 constexpr int32_t kNonNegativeBucketCount = arraysize(kPercentIntervals);
92 constexpr int32_t kNegativeBucketCount = arraysize(kPercentIntervals) - 1;
93 bool negative = percent < 0;
94 double abs_percent = std::abs(percent);
95 if (abs_percent == 0)
96 return kNegativeBucketCount; // Dedicated zero bucket.
97 for (int32_t i = 0; i < kNonNegativeBucketCount; i++) {
98 if (abs_percent < kPercentIntervals[i])
99 return kNegativeBucketCount + (negative ? -i : +i);
100 }
101 // No NOTREACHED since the +/-100 bounds are inclusive (even if they are
102 // slightly exceeded due to floating point inaccuracies).
103 return negative ? 0 : kPercentBucketCount - 1;
104 }
105
106 // Calculates index of UMA MediaTimelineAbsTimeDelta enum corresponding to
107 // |sumAbsDeltaSeconds|.
108 int32_t ToAbsTimeDeltaSample(double sum_abs_delta_seconds) {
109 double sum_abs_delta_ms = 1000 * sum_abs_delta_seconds;
110 if (sum_abs_delta_ms == 0)
111 return 0; // Dedicated zero bucket.
112 for (int32_t i = 0; i < kAbsTimeDeltaBucketCount; i++) {
113 if (sum_abs_delta_ms < kTimeDeltaMSIntervals[i])
114 return i;
115 }
116 NOTREACHED() << "sumAbsDeltaSeconds shouldn't be infinite";
117 return kAbsTimeDeltaBucketCount - 1;
118 }
119
120 // Calculates index of UMA MediaTimelineTimeDelta enum corresponding to
121 // |deltaSeconds|. Negative values use kTimeDeltaMSIntervals in reverse.
122 int32_t ToTimeDeltaSample(double delta_seconds) {
123 constexpr int32_t kNonNegativeBucketCount = arraysize(kTimeDeltaMSIntervals);
124 constexpr int32_t kNegativeBucketCount = arraysize(kTimeDeltaMSIntervals) - 1;
125 bool negative = delta_seconds < 0;
126 double abs_delta_ms = 1000 * std::abs(delta_seconds);
127 if (abs_delta_ms == 0)
128 return kNegativeBucketCount; // Dedicated zero bucket.
129 for (int32_t i = 0; i < kNonNegativeBucketCount; i++) {
130 if (abs_delta_ms < kTimeDeltaMSIntervals[i])
131 return kNegativeBucketCount + (negative ? -i : +i);
132 }
133 NOTREACHED() << "deltaSeconds shouldn't be infinite";
134 return negative ? 0 : kTimeDeltaBucketCount - 1;
135 }
136
137 // Helper for RECORD_TIMELINE_UMA_BY_WIDTH.
138 #define ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, minWidth, maxWidth, metric, \
139 sample, HistogramType, ...) \
140 else if (width >= minWidth) { \
141 DEFINE_STATIC_LOCAL( \
142 HistogramType, metric##minWidth##_##maxWidth##Histogram, \
143 ("Media.Timeline." #metric "." #minWidth "_" #maxWidth, \
144 ##__VA_ARGS__)); \
145 metric##minWidth##_##maxWidth##Histogram.Count(sample); \
146 }
147
148 // Records UMA with a histogram suffix based on timelineWidth.
149 #define RECORD_TIMELINE_UMA_BY_WIDTH(timelineWidth, metric, sample, \
150 HistogramType, ...) \
151 do { \
152 int width = timelineWidth; /* Avoid multiple evaluation. */ \
153 if (false) { \
154 /* This if(false) allows all the conditions below to start with else. */ \
155 } \
156 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 512, inf, metric, sample, \
157 HistogramType, ##__VA_ARGS__) \
158 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 256, 511, metric, sample, \
159 HistogramType, ##__VA_ARGS__) \
160 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 128, 255, metric, sample, \
161 HistogramType, ##__VA_ARGS__) \
162 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 80, 127, metric, sample, \
163 HistogramType, ##__VA_ARGS__) \
164 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 48, 79, metric, sample, \
165 HistogramType, ##__VA_ARGS__) \
166 ELSEIF_WIDTH_RECORD_TIMELINE_UMA(width, 32, 47, metric, sample, \
167 HistogramType, ##__VA_ARGS__) \
168 else { \
169 /* Skip logging if timeline is narrower than minimum suffix bucket. */ \
170 /* If this happens a lot, it'll show up in Media.Timeline.Width. */ \
171 } \
172 } while (false)
173
174 void RecordDragGestureDurationByWidth(int timeline_width, TimeDelta duration) {
175 int32_t sample = static_cast<int32_t>(duration.InMilliseconds());
176 RECORD_TIMELINE_UMA_BY_WIDTH(timeline_width, DragGestureDuration, sample,
177 CustomCountHistogram, 1 /* 1 ms */,
178 60000 /* 1 minute */, 50);
179 }
180 void RecordDragPercentByWidth(int timeline_width, double percent) {
181 int32_t sample = ToPercentSample(percent);
182 RECORD_TIMELINE_UMA_BY_WIDTH(timeline_width, DragPercent, sample,
183 EnumerationHistogram, kPercentBucketCount);
184 }
185 void RecordDragSumAbsTimeDeltaByWidth(int timeline_width,
186 double sum_abs_delta_seconds) {
187 int32_t sample = ToAbsTimeDeltaSample(sum_abs_delta_seconds);
188 RECORD_TIMELINE_UMA_BY_WIDTH(timeline_width, DragSumAbsTimeDelta, sample,
189 EnumerationHistogram, kAbsTimeDeltaBucketCount);
190 }
191 void RecordDragTimeDeltaByWidth(int timeline_width, double delta_seconds) {
192 int32_t sample = ToTimeDeltaSample(delta_seconds);
193 RECORD_TIMELINE_UMA_BY_WIDTH(timeline_width, DragTimeDelta, sample,
194 EnumerationHistogram, kTimeDeltaBucketCount);
195 }
196 void RecordSeekTypeByWidth(int timeline_width, SeekType type) {
197 int32_t sample = static_cast<int32_t>(type);
198 constexpr int32_t kBucketCount = static_cast<int32_t>(SeekType::kLast) + 1;
199 RECORD_TIMELINE_UMA_BY_WIDTH(timeline_width, SeekType, sample,
200 EnumerationHistogram, kBucketCount);
201 }
202
203 #undef RECORD_TIMELINE_UMA_BY_WIDTH
204 #undef ELSEIF_WIDTH_RECORD_TIMELINE_UMA
205
206 } // namespace
207
208 void MediaControlTimelineMetrics::StartGesture(bool from_thumb) {
209 // Initialize gesture tracking.
210 state_ = from_thumb ? State::kGestureFromThumb : State::kGestureFromElsewhere;
211 drag_start_time_ticks_ = TimeTicks::Now();
212 drag_delta_media_seconds_ = 0;
213 drag_sum_abs_delta_media_seconds_ = 0;
214 }
215
216 void MediaControlTimelineMetrics::RecordEndGesture(
217 int timeline_width,
218 double media_duration_seconds) {
219 State end_state = state_;
220 state_ = State::kInactive; // Reset tracking.
221
222 SeekType seek_type =
223 SeekType::kLast; // Arbitrary inital val to appease MSVC.
224 switch (end_state) {
225 case State::kInactive:
226 case State::kKeyDown:
227 return; // Pointer and keys were interleaved. Skip UMA in this edge case.
228 case State::kGestureFromThumb:
229 case State::kGestureFromElsewhere:
230 return; // Empty gesture with no calls to gestureInput.
231 case State::kDragFromThumb:
232 seek_type = SeekType::kDragFromCurrentPosition;
233 break;
234 case State::kClick:
235 seek_type = SeekType::kClick;
236 break;
237 case State::kDragFromElsewhere:
238 seek_type = SeekType::kDragFromElsewhere;
239 break;
240 }
241
242 RecordSeekTypeByWidth(timeline_width, seek_type);
243
244 if (seek_type == SeekType::kClick)
245 return; // Metrics below are only for drags.
246
247 RecordDragGestureDurationByWidth(timeline_width,
248 TimeTicks::Now() - drag_start_time_ticks_);
249 if (std::isfinite(media_duration_seconds)) {
250 RecordDragPercentByWidth(timeline_width, 100.0 * drag_delta_media_seconds_ /
251 media_duration_seconds);
252 }
253 RecordDragSumAbsTimeDeltaByWidth(timeline_width,
254 drag_sum_abs_delta_media_seconds_);
255 RecordDragTimeDeltaByWidth(timeline_width, drag_delta_media_seconds_);
256 }
257
258 void MediaControlTimelineMetrics::StartKey() {
259 state_ = State::kKeyDown;
260 }
261
262 void MediaControlTimelineMetrics::RecordEndKey(int timeline_width,
263 int key_code) {
264 State end_state = state_;
265 state_ = State::kInactive; // Reset tracking.
266 if (end_state != State::kKeyDown)
267 return; // Pointer and keys were interleaved. Skip UMA in this edge case.
268
269 SeekType type;
270 switch (key_code) {
271 case VKEY_UP:
272 case VKEY_DOWN:
273 case VKEY_LEFT:
274 case VKEY_RIGHT:
275 type = SeekType::kKeyboardArrowKey;
276 break;
277 case VKEY_PRIOR: // PageUp
278 case VKEY_NEXT: // PageDown
279 type = SeekType::kKeyboardPageUpDownKey;
280 break;
281 case VKEY_HOME:
282 case VKEY_END:
283 type = SeekType::kKeyboardHomeEndKey;
284 break;
285 default:
286 return; // Other keys don't seek (at time of writing).
287 }
288 RecordSeekTypeByWidth(timeline_width, type);
289 }
290
291 void MediaControlTimelineMetrics::OnInput(double from_seconds,
292 double to_seconds) {
293 switch (state_) {
294 case State::kInactive:
295 // Unexpected input.
296 state_ = State::kInactive;
297 break;
298 case State::kGestureFromThumb:
299 // Drag confirmed now input has been received.
300 state_ = State::kDragFromThumb;
301 break;
302 case State::kGestureFromElsewhere:
303 // Click/drag confirmed now input has been received. Assume it's a click
304 // until further input is received.
305 state_ = State::kClick;
306 break;
307 case State::kClick:
308 // Drag confirmed now further input has been received.
309 state_ = State::kDragFromElsewhere;
310 break;
311 case State::kDragFromThumb:
312 case State::kDragFromElsewhere:
313 // Continue tracking drag.
314 break;
315 case State::kKeyDown:
316 // Continue tracking key.
317 break;
318 }
319
320 // The following tracking is only for drags. Note that we exclude kClick here,
321 // as even if it progresses to a kDragFromElsewhere, the first input event
322 // corresponds to the position jump from the pointer down on the track.
323 if (state_ != State::kDragFromThumb && state_ != State::kDragFromElsewhere)
324 return;
325
326 float delta_media_seconds = static_cast<float>(to_seconds - from_seconds);
327 drag_delta_media_seconds_ += delta_media_seconds;
328 drag_sum_abs_delta_media_seconds_ += std::abs(delta_media_seconds);
329 }
330
331 void MediaControlTimelineMetrics::RecordPlaying(
332 WebScreenOrientationType orientation,
333 bool is_fullscreen,
334 int timeline_width) {
335 bool is_portrait = false; // Arbitrary initial value to appease MSVC.
336 switch (orientation) {
337 case kWebScreenOrientationPortraitPrimary:
338 case kWebScreenOrientationPortraitSecondary:
339 is_portrait = true;
340 break;
341 case kWebScreenOrientationLandscapePrimary:
342 case kWebScreenOrientationLandscapeSecondary:
343 is_portrait = false;
344 break;
345 case kWebScreenOrientationUndefined:
346 return; // Skip UMA in the unlikely event we fail to detect orientation.
347 }
348
349 // Only record the first time each media element enters the playing state.
350 if (!has_never_been_playing_)
351 return;
352 has_never_been_playing_ = false;
353
354 constexpr int32_t kMin = 1;
355 constexpr int32_t kMax = 7680; // Equivalent to an 80inch wide 8K monitor.
356 constexpr int32_t kBucketCount = 50;
357 // Record merged histogram for all configurations.
358 DEFINE_STATIC_LOCAL(CustomCountHistogram, all_configurations_width_histogram,
359 ("Media.Timeline.Width", kMin, kMax, kBucketCount));
360 all_configurations_width_histogram.Count(timeline_width);
361 // Record configuration-specific histogram.
362 if (!is_fullscreen) {
363 if (is_portrait) {
364 DEFINE_STATIC_LOCAL(
365 CustomCountHistogram, width_histogram,
366 ("Media.Timeline.Width.InlinePortrait", kMin, kMax, kBucketCount));
367 width_histogram.Count(timeline_width);
368 } else {
369 DEFINE_STATIC_LOCAL(
370 CustomCountHistogram, width_histogram,
371 ("Media.Timeline.Width.InlineLandscape", kMin, kMax, kBucketCount));
372 width_histogram.Count(timeline_width);
373 }
374 } else {
375 if (is_portrait) {
376 DEFINE_STATIC_LOCAL(CustomCountHistogram, width_histogram,
377 ("Media.Timeline.Width.FullscreenPortrait", kMin,
378 kMax, kBucketCount));
379 width_histogram.Count(timeline_width);
380 } else {
381 DEFINE_STATIC_LOCAL(CustomCountHistogram, width_histogram,
382 ("Media.Timeline.Width.FullscreenLandscape", kMin,
383 kMax, kBucketCount));
384 width_histogram.Count(timeline_width);
385 }
386 }
387 }
388
389 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698