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 "media/mojo/services/watch_time_recorder.h"
6
7 #include <stddef.h>
8 #include <memory>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/hash/hash.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/test/metrics/histogram_tester.h"
18 #include "base/test/task_environment.h"
19 #include "base/test/test_message_loop.h"
20 #include "base/threading/thread_task_runner_handle.h"
21 #include "base/time/time.h"
22 #include "components/ukm/test_ukm_recorder.h"
23 #include "media/base/video_codecs.h"
24 #include "media/base/watch_time_keys.h"
25 #include "media/mojo/services/media_metrics_provider.h"
26 #include "mojo/public/cpp/bindings/remote.h"
27 #include "services/metrics/public/cpp/ukm_builders.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31
32 using UkmEntry = ukm::builders::Media_BasicPlayback;
33
34 namespace content {
35 class RenderFrameHostDelegate;
36 } // namespace content
37
38 namespace media {
39
40 constexpr char kTestOrigin[] = "https://test.google.com/";
41
42 class WatchTimeRecorderTest : public testing::Test {
43 public:
WatchTimeRecorderTest()44 WatchTimeRecorderTest()
45 : computation_keys_(
46 {WatchTimeKey::kAudioSrc, WatchTimeKey::kAudioMse,
47 WatchTimeKey::kAudioEme, WatchTimeKey::kAudioVideoSrc,
48 WatchTimeKey::kAudioVideoMse, WatchTimeKey::kAudioVideoEme}),
49 mtbr_keys_({kMeanTimeBetweenRebuffersAudioSrc,
50 kMeanTimeBetweenRebuffersAudioMse,
51 kMeanTimeBetweenRebuffersAudioEme,
52 kMeanTimeBetweenRebuffersAudioVideoSrc,
53 kMeanTimeBetweenRebuffersAudioVideoMse,
54 kMeanTimeBetweenRebuffersAudioVideoEme}),
55 smooth_keys_({kRebuffersCountAudioSrc, kRebuffersCountAudioMse,
56 kRebuffersCountAudioEme, kRebuffersCountAudioVideoSrc,
57 kRebuffersCountAudioVideoMse,
58 kRebuffersCountAudioVideoEme}),
59 discard_keys_({kDiscardedWatchTimeAudioSrc, kDiscardedWatchTimeAudioMse,
60 kDiscardedWatchTimeAudioEme,
61 kDiscardedWatchTimeAudioVideoSrc,
62 kDiscardedWatchTimeAudioVideoMse,
63 kDiscardedWatchTimeAudioVideoEme}) {
64 source_id_ = test_recorder_->GetNewSourceID();
65 ResetMetricRecorders();
66 MediaMetricsProvider::Create(
67 MediaMetricsProvider::BrowsingMode::kIncognito,
68 MediaMetricsProvider::FrameStatus::kTopFrame,
69 base::BindRepeating(&WatchTimeRecorderTest::GetSourceId,
70 base::Unretained(this)),
71 base::BindRepeating(
__anond669be580102() 72 []() { return learning::FeatureValue(0); }) /* origin callback */,
73 VideoDecodePerfHistory::SaveCallback(),
74 MediaMetricsProvider::GetLearningSessionCallback(),
75 base::BindRepeating(
76 &WatchTimeRecorderTest::GetRecordAggregateWatchTimeCallback,
77 base::Unretained(this)),
78 provider_.BindNewPipeAndPassReceiver());
79 }
80
~WatchTimeRecorderTest()81 ~WatchTimeRecorderTest() override { base::RunLoop().RunUntilIdle(); }
82
Initialize(mojom::PlaybackPropertiesPtr properties)83 void Initialize(mojom::PlaybackPropertiesPtr properties) {
84 provider_->Initialize(properties->is_mse,
85 properties->is_mse ? mojom::MediaURLScheme::kUnknown
86 : mojom::MediaURLScheme::kHttp);
87 provider_->AcquireWatchTimeRecorder(std::move(properties),
88 wtr_.BindNewPipeAndPassReceiver());
89 }
90
Initialize(bool has_audio,bool has_video,bool is_mse,bool is_encrypted)91 void Initialize(bool has_audio,
92 bool has_video,
93 bool is_mse,
94 bool is_encrypted) {
95 Initialize(mojom::PlaybackProperties::New(
96 has_audio, has_video, false, false, is_mse, is_encrypted, false));
97 }
98
ExpectWatchTime(const std::vector<base::StringPiece> & keys,base::TimeDelta value)99 void ExpectWatchTime(const std::vector<base::StringPiece>& keys,
100 base::TimeDelta value) {
101 for (int i = 0; i <= static_cast<int>(WatchTimeKey::kWatchTimeKeyMax);
102 ++i) {
103 const base::StringPiece test_key =
104 ConvertWatchTimeKeyToStringForUma(static_cast<WatchTimeKey>(i));
105 if (test_key.empty())
106 continue;
107 auto it = std::find(keys.begin(), keys.end(), test_key);
108 if (it == keys.end()) {
109 histogram_tester_->ExpectTotalCount(test_key.as_string(), 0);
110 } else {
111 histogram_tester_->ExpectUniqueSample(test_key.as_string(),
112 value.InMilliseconds(), 1);
113 }
114 }
115 }
116
ExpectHelper(const std::vector<base::StringPiece> & full_key_list,const std::vector<base::StringPiece> & keys,int64_t value)117 void ExpectHelper(const std::vector<base::StringPiece>& full_key_list,
118 const std::vector<base::StringPiece>& keys,
119 int64_t value) {
120 for (auto key : full_key_list) {
121 auto it = std::find(keys.begin(), keys.end(), key);
122 if (it == keys.end())
123 histogram_tester_->ExpectTotalCount(key.as_string(), 0);
124 else
125 histogram_tester_->ExpectUniqueSample(key.as_string(), value, 1);
126 }
127 }
128
ExpectMtbrTime(const std::vector<base::StringPiece> & keys,base::TimeDelta value)129 void ExpectMtbrTime(const std::vector<base::StringPiece>& keys,
130 base::TimeDelta value) {
131 ExpectHelper(mtbr_keys_, keys, value.InMilliseconds());
132 }
133
ExpectZeroRebuffers(const std::vector<base::StringPiece> & keys)134 void ExpectZeroRebuffers(const std::vector<base::StringPiece>& keys) {
135 ExpectHelper(smooth_keys_, keys, 0);
136 }
137
ExpectRebuffers(const std::vector<base::StringPiece> & keys,int count)138 void ExpectRebuffers(const std::vector<base::StringPiece>& keys, int count) {
139 ExpectHelper(smooth_keys_, keys, count);
140 }
141
ExpectAacAudioCodecProfileHistogram(AudioCodecProfile profile)142 void ExpectAacAudioCodecProfileHistogram(AudioCodecProfile profile) {
143 constexpr char kAudioCodecProfileHistogram[] =
144 "Media.AudioCodecProfile.AAC";
145 histogram_tester_->ExpectUniqueSample(kAudioCodecProfileHistogram,
146 static_cast<int64_t>(profile), 1);
147 }
148
ExpectNoUkmWatchTime()149 void ExpectNoUkmWatchTime() {
150 // We always add a source in testing.
151 ASSERT_EQ(1u, test_recorder_->sources_count());
152 ASSERT_EQ(0u, test_recorder_->entries_count());
153 }
154
ExpectUkmWatchTime(const std::vector<base::StringPiece> & keys,base::TimeDelta value)155 void ExpectUkmWatchTime(const std::vector<base::StringPiece>& keys,
156 base::TimeDelta value) {
157 const auto& entries =
158 test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
159 EXPECT_EQ(1u, entries.size());
160 for (const auto* entry : entries) {
161 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
162 for (auto key : keys) {
163 test_recorder_->ExpectEntryMetric(entry, key.data(),
164 value.InMilliseconds());
165 }
166 }
167 }
168
ResetMetricRecorders()169 void ResetMetricRecorders() {
170 histogram_tester_.reset(new base::HistogramTester());
171 // Ensure cleared global before attempting to create a new TestUkmReporter.
172 test_recorder_.reset();
173 test_recorder_.reset(new ukm::TestAutoSetUkmRecorder());
174 test_recorder_->UpdateSourceURL(source_id_, GURL(kTestOrigin));
175 }
176
CreateSecondaryProperties()177 mojom::SecondaryPlaybackPropertiesPtr CreateSecondaryProperties() {
178 return mojom::SecondaryPlaybackProperties::New(
179 kCodecAAC, kCodecH264, AudioCodecProfile::kUnknown, H264PROFILE_MAIN,
180 "", "", EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
181 gfx::Size(800, 600));
182 }
183
GetSourceId()184 ukm::SourceId GetSourceId() { return source_id_; }
185
186 MediaMetricsProvider::RecordAggregateWatchTimeCallback
GetRecordAggregateWatchTimeCallback()187 GetRecordAggregateWatchTimeCallback() {
188 return base::BindRepeating(
189 [](base::WeakPtr<content::RenderFrameHostDelegate> delegate,
190 GURL last_committed_url, base::TimeDelta total_watch_time,
191 base::TimeDelta time_stamp, bool has_video, bool has_audio) {
192 // Do nothing as this mock callback will never be called.
193 },
194 nullptr, GURL());
195 }
196
197 MOCK_METHOD0(GetCurrentMediaTime, base::TimeDelta());
198
199 protected:
200 base::test::SingleThreadTaskEnvironment task_environment_;
201 mojo::Remote<mojom::MediaMetricsProvider> provider_;
202 std::unique_ptr<base::HistogramTester> histogram_tester_;
203 std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_recorder_;
204 ukm::SourceId source_id_;
205 mojo::Remote<mojom::WatchTimeRecorder> wtr_;
206 const std::vector<WatchTimeKey> computation_keys_;
207 const std::vector<base::StringPiece> mtbr_keys_;
208 const std::vector<base::StringPiece> smooth_keys_;
209 const std::vector<base::StringPiece> discard_keys_;
210
211 DISALLOW_COPY_AND_ASSIGN(WatchTimeRecorderTest);
212 };
213
TEST_F(WatchTimeRecorderTest,TestBasicReporting)214 TEST_F(WatchTimeRecorderTest, TestBasicReporting) {
215 constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(25);
216 constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(50);
217
218 for (int i = 0; i <= static_cast<int>(WatchTimeKey::kWatchTimeKeyMax); ++i) {
219 const WatchTimeKey key = static_cast<WatchTimeKey>(i);
220
221 auto key_str = ConvertWatchTimeKeyToStringForUma(key);
222 SCOPED_TRACE(key_str.empty() ? base::NumberToString(i)
223 : key_str.as_string());
224
225 // Values for |is_background| and |is_muted| don't matter in this test since
226 // they don't prevent the muted or background keys from being recorded.
227 Initialize(true, false, true, true);
228 wtr_->UpdateSecondaryProperties(CreateSecondaryProperties());
229
230 wtr_->RecordWatchTime(WatchTimeKey::kWatchTimeKeyMax, kWatchTime1);
231 wtr_->RecordWatchTime(key, kWatchTime1);
232 wtr_->RecordWatchTime(key, kWatchTime2);
233 base::RunLoop().RunUntilIdle();
234
235 // Nothing should be recorded yet since we haven't finalized.
236 ExpectWatchTime({}, base::TimeDelta());
237
238 // Only the requested key should be finalized.
239 wtr_->FinalizeWatchTime({key});
240 base::RunLoop().RunUntilIdle();
241
242 if (!key_str.empty())
243 ExpectWatchTime({key_str}, kWatchTime2);
244
245 // These keys are only reported for a full finalize.
246 ExpectMtbrTime({}, base::TimeDelta());
247 ExpectZeroRebuffers({});
248 ExpectNoUkmWatchTime();
249
250 // Verify nothing else is recorded except for what we finalized above.
251 ResetMetricRecorders();
252 wtr_.reset();
253 base::RunLoop().RunUntilIdle();
254 ExpectWatchTime({}, base::TimeDelta());
255 ExpectMtbrTime({}, base::TimeDelta());
256 ExpectZeroRebuffers({});
257
258 switch (key) {
259 case WatchTimeKey::kAudioAll:
260 case WatchTimeKey::kAudioBackgroundAll:
261 case WatchTimeKey::kAudioVideoAll:
262 case WatchTimeKey::kAudioVideoBackgroundAll:
263 case WatchTimeKey::kAudioVideoMutedAll:
264 case WatchTimeKey::kVideoAll:
265 case WatchTimeKey::kVideoBackgroundAll:
266 ExpectUkmWatchTime({UkmEntry::kWatchTimeName}, kWatchTime2);
267 break;
268
269 // These keys are not reported, instead we boolean flags for each type.
270 case WatchTimeKey::kAudioMse:
271 case WatchTimeKey::kAudioEme:
272 case WatchTimeKey::kAudioSrc:
273 case WatchTimeKey::kAudioEmbeddedExperience:
274 case WatchTimeKey::kAudioBackgroundMse:
275 case WatchTimeKey::kAudioBackgroundEme:
276 case WatchTimeKey::kAudioBackgroundSrc:
277 case WatchTimeKey::kAudioBackgroundEmbeddedExperience:
278 case WatchTimeKey::kAudioVideoMse:
279 case WatchTimeKey::kAudioVideoEme:
280 case WatchTimeKey::kAudioVideoSrc:
281 case WatchTimeKey::kAudioVideoEmbeddedExperience:
282 case WatchTimeKey::kAudioVideoMutedMse:
283 case WatchTimeKey::kAudioVideoMutedEme:
284 case WatchTimeKey::kAudioVideoMutedSrc:
285 case WatchTimeKey::kAudioVideoMutedEmbeddedExperience:
286 case WatchTimeKey::kAudioVideoBackgroundMse:
287 case WatchTimeKey::kAudioVideoBackgroundEme:
288 case WatchTimeKey::kAudioVideoBackgroundSrc:
289 case WatchTimeKey::kAudioVideoBackgroundEmbeddedExperience:
290 case WatchTimeKey::kVideoMse:
291 case WatchTimeKey::kVideoEme:
292 case WatchTimeKey::kVideoSrc:
293 case WatchTimeKey::kVideoEmbeddedExperience:
294 case WatchTimeKey::kVideoBackgroundMse:
295 case WatchTimeKey::kVideoBackgroundEme:
296 case WatchTimeKey::kVideoBackgroundSrc:
297 case WatchTimeKey::kVideoBackgroundEmbeddedExperience:
298 ExpectUkmWatchTime({}, base::TimeDelta());
299 break;
300
301 // These keys roll up into the battery watch time field.
302 case WatchTimeKey::kAudioBattery:
303 case WatchTimeKey::kAudioBackgroundBattery:
304 case WatchTimeKey::kAudioVideoBattery:
305 case WatchTimeKey::kAudioVideoMutedBattery:
306 case WatchTimeKey::kAudioVideoBackgroundBattery:
307 case WatchTimeKey::kVideoBattery:
308 case WatchTimeKey::kVideoBackgroundBattery:
309 ExpectUkmWatchTime({UkmEntry::kWatchTime_BatteryName}, kWatchTime2);
310 break;
311
312 // These keys roll up into the AC watch time field.
313 case WatchTimeKey::kAudioAc:
314 case WatchTimeKey::kAudioBackgroundAc:
315 case WatchTimeKey::kAudioVideoAc:
316 case WatchTimeKey::kAudioVideoBackgroundAc:
317 case WatchTimeKey::kAudioVideoMutedAc:
318 case WatchTimeKey::kVideoAc:
319 case WatchTimeKey::kVideoBackgroundAc:
320 ExpectUkmWatchTime({UkmEntry::kWatchTime_ACName}, kWatchTime2);
321 break;
322
323 case WatchTimeKey::kAudioVideoDisplayFullscreen:
324 case WatchTimeKey::kAudioVideoMutedDisplayFullscreen:
325 case WatchTimeKey::kVideoDisplayFullscreen:
326 ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayFullscreenName},
327 kWatchTime2);
328 break;
329
330 case WatchTimeKey::kAudioVideoDisplayInline:
331 case WatchTimeKey::kAudioVideoMutedDisplayInline:
332 case WatchTimeKey::kVideoDisplayInline:
333 ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayInlineName},
334 kWatchTime2);
335 break;
336
337 case WatchTimeKey::kAudioVideoDisplayPictureInPicture:
338 case WatchTimeKey::kAudioVideoMutedDisplayPictureInPicture:
339 case WatchTimeKey::kVideoDisplayPictureInPicture:
340 ExpectUkmWatchTime({UkmEntry::kWatchTime_DisplayPictureInPictureName},
341 kWatchTime2);
342 break;
343
344 case WatchTimeKey::kAudioNativeControlsOn:
345 case WatchTimeKey::kAudioVideoNativeControlsOn:
346 case WatchTimeKey::kAudioVideoMutedNativeControlsOn:
347 case WatchTimeKey::kVideoNativeControlsOn:
348 ExpectUkmWatchTime({UkmEntry::kWatchTime_NativeControlsOnName},
349 kWatchTime2);
350 break;
351
352 case WatchTimeKey::kAudioNativeControlsOff:
353 case WatchTimeKey::kAudioVideoNativeControlsOff:
354 case WatchTimeKey::kAudioVideoMutedNativeControlsOff:
355 case WatchTimeKey::kVideoNativeControlsOff:
356 ExpectUkmWatchTime({UkmEntry::kWatchTime_NativeControlsOffName},
357 kWatchTime2);
358 break;
359 }
360
361 ResetMetricRecorders();
362 }
363 }
364
TEST_F(WatchTimeRecorderTest,TestRebufferingMetrics)365 TEST_F(WatchTimeRecorderTest, TestRebufferingMetrics) {
366 Initialize(true, false, true, true);
367
368 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(50);
369 for (auto key : computation_keys_)
370 wtr_->RecordWatchTime(key, kWatchTime);
371 wtr_->UpdateUnderflowCount(1);
372 wtr_->UpdateUnderflowCount(2);
373
374 // Trigger finalization of everything.
375 wtr_->FinalizeWatchTime({});
376 base::RunLoop().RunUntilIdle();
377
378 ExpectMtbrTime(mtbr_keys_, kWatchTime / 2);
379 ExpectRebuffers(smooth_keys_, 2);
380
381 // Now rerun the test without any rebuffering.
382 ResetMetricRecorders();
383 for (auto key : computation_keys_)
384 wtr_->RecordWatchTime(key, kWatchTime);
385 wtr_->FinalizeWatchTime({});
386 base::RunLoop().RunUntilIdle();
387
388 ExpectMtbrTime({}, base::TimeDelta());
389 ExpectZeroRebuffers(smooth_keys_);
390
391 // Now rerun the test with a small amount of watch time and ensure rebuffering
392 // isn't recorded because we haven't met the watch time requirements.
393 ResetMetricRecorders();
394 constexpr base::TimeDelta kWatchTimeShort = base::TimeDelta::FromSeconds(5);
395 for (auto key : computation_keys_)
396 wtr_->RecordWatchTime(key, kWatchTimeShort);
397 wtr_->UpdateUnderflowCount(1);
398 wtr_->UpdateUnderflowCount(2);
399 wtr_->FinalizeWatchTime({});
400 base::RunLoop().RunUntilIdle();
401
402 // Nothing should be logged since this doesn't meet requirements.
403 ExpectMtbrTime({}, base::TimeDelta());
404 for (auto key : smooth_keys_)
405 histogram_tester_->ExpectTotalCount(key.as_string(), 0);
406 }
407
TEST_F(WatchTimeRecorderTest,TestDiscardMetrics)408 TEST_F(WatchTimeRecorderTest, TestDiscardMetrics) {
409 Initialize(true, false, true, true);
410 wtr_->UpdateSecondaryProperties(CreateSecondaryProperties());
411
412 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(5);
413 for (auto key : computation_keys_)
414 wtr_->RecordWatchTime(key, kWatchTime);
415
416 // Trigger finalization of everything.
417 wtr_.reset();
418 base::RunLoop().RunUntilIdle();
419
420 // No standard watch time should be recorded because it falls below the
421 // reporting threshold.
422 ExpectWatchTime({}, base::TimeDelta());
423
424 // Verify the time was instead logged to the discard keys.
425 for (auto key : discard_keys_) {
426 histogram_tester_->ExpectUniqueSample(key.as_string(),
427 kWatchTime.InMilliseconds(), 1);
428 }
429
430 // UKM watch time won't be logged because we aren't sending "All" keys.
431 ExpectUkmWatchTime({}, base::TimeDelta());
432 }
433
434 #define EXPECT_UKM(name, value) \
435 test_recorder_->ExpectEntryMetric(entry, name, value)
436 #define EXPECT_NO_UKM(name) \
437 EXPECT_FALSE(test_recorder_->EntryHasMetric(entry, name))
438 #define EXPECT_HAS_UKM(name) \
439 EXPECT_TRUE(test_recorder_->EntryHasMetric(entry, name));
440
TEST_F(WatchTimeRecorderTest,TestFinalizeNoDuplication)441 TEST_F(WatchTimeRecorderTest, TestFinalizeNoDuplication) {
442 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
443 true, true, false, false, false, false, false);
444 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
445 CreateSecondaryProperties();
446 Initialize(properties.Clone());
447 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
448
449 // Verify that UKM is reported along with the watch time.
450 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(4);
451 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime);
452
453 // Finalize everything. UKM is only recorded at destruction, so this should do
454 // nothing.
455 wtr_->FinalizeWatchTime({});
456 base::RunLoop().RunUntilIdle();
457
458 // No watch time should have been recorded since this is below the UMA report
459 // threshold.
460 ExpectWatchTime({}, base::TimeDelta());
461 ExpectMtbrTime({}, base::TimeDelta());
462 ExpectZeroRebuffers({});
463 ExpectNoUkmWatchTime();
464
465 const auto& empty_entries =
466 test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
467 EXPECT_EQ(0u, empty_entries.size());
468
469 // Verify UKM is logged at destruction time.
470 ResetMetricRecorders();
471 wtr_.reset();
472 base::RunLoop().RunUntilIdle();
473 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
474 EXPECT_EQ(1u, entries.size());
475 for (const auto* entry : entries) {
476 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
477
478 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
479 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
480 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
481 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
482 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
483 static_cast<int64_t>(secondary_properties->audio_codec_profile));
484 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
485 secondary_properties->video_codec_profile);
486 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
487 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
488 EXPECT_UKM(
489 UkmEntry::kAudioEncryptionSchemeName,
490 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
491 EXPECT_UKM(
492 UkmEntry::kVideoEncryptionSchemeName,
493 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
494 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
495 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
496 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
497 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
498 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
499 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
500 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
501 secondary_properties->natural_size.width());
502 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
503 secondary_properties->natural_size.height());
504 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds());
505 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
506 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
507 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
508 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
509
510 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
511 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
512 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
513 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
514 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
515 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
516 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
517 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
518 }
519 }
520
TEST_F(WatchTimeRecorderTest,FinalizeWithoutWatchTime)521 TEST_F(WatchTimeRecorderTest, FinalizeWithoutWatchTime) {
522 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
523 true, true, false, false, false, false, false);
524 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
525 CreateSecondaryProperties();
526 Initialize(properties.Clone());
527 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
528
529 // Finalize everything. UKM is only recorded at destruction, so this should do
530 // nothing.
531 wtr_->FinalizeWatchTime({});
532 base::RunLoop().RunUntilIdle();
533
534 // No watch time should have been recorded even though a finalize event will
535 // be sent, however a UKM entry with the playback properties will still be
536 // generated.
537 ExpectWatchTime({}, base::TimeDelta());
538 ExpectMtbrTime({}, base::TimeDelta());
539 ExpectZeroRebuffers({});
540 ExpectNoUkmWatchTime();
541
542 const auto& empty_entries =
543 test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
544 EXPECT_EQ(0u, empty_entries.size());
545
546 // Destructing the recorder should generate a UKM report though.
547 ResetMetricRecorders();
548 wtr_.reset();
549 base::RunLoop().RunUntilIdle();
550 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
551 EXPECT_EQ(1u, entries.size());
552 for (const auto* entry : entries) {
553 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
554
555 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
556 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
557 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
558 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
559 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
560 static_cast<int64_t>(secondary_properties->audio_codec_profile));
561 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
562 secondary_properties->video_codec_profile);
563 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
564 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
565 EXPECT_UKM(
566 UkmEntry::kAudioEncryptionSchemeName,
567 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
568 EXPECT_UKM(
569 UkmEntry::kVideoEncryptionSchemeName,
570 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
571 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
572 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
573 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
574 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
575 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
576 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
577 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
578 secondary_properties->natural_size.width());
579 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
580 secondary_properties->natural_size.height());
581 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
582 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
583 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
584 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
585
586 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
587 EXPECT_NO_UKM(UkmEntry::kWatchTimeName);
588 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
589 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
590 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
591 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
592 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
593 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
594 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
595 }
596 }
597
TEST_F(WatchTimeRecorderTest,BasicUkmAudioVideo)598 TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideo) {
599 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
600 true, true, false, false, false, false, false);
601 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
602 mojom::SecondaryPlaybackProperties::New(
603 kCodecAAC, kCodecH264, AudioCodecProfile::kXHE_AAC, H264PROFILE_MAIN,
604 "", "", EncryptionScheme::kCenc, EncryptionScheme::kCbcs,
605 gfx::Size(800, 600));
606 Initialize(properties.Clone());
607 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
608
609 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(4);
610 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime);
611 wtr_.reset();
612 base::RunLoop().RunUntilIdle();
613
614 ExpectAacAudioCodecProfileHistogram(
615 secondary_properties->audio_codec_profile);
616
617 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
618 EXPECT_EQ(1u, entries.size());
619 for (const auto* entry : entries) {
620 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
621
622 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds());
623 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
624 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
625 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
626 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
627 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
628 static_cast<int64_t>(secondary_properties->audio_codec_profile));
629 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
630 secondary_properties->video_codec_profile);
631 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
632 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
633 EXPECT_UKM(
634 UkmEntry::kAudioEncryptionSchemeName,
635 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
636 EXPECT_UKM(
637 UkmEntry::kVideoEncryptionSchemeName,
638 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
639 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
640 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
641 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
642 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
643 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
644 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
645 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
646 secondary_properties->natural_size.width());
647 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
648 secondary_properties->natural_size.height());
649 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
650 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
651 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
652 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
653
654 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
655 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
656 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
657 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
658 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
659 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
660 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
661 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
662 }
663 }
664
TEST_F(WatchTimeRecorderTest,BasicUkmAudioVideoWithExtras)665 TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoWithExtras) {
666 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
667 true, true, false, false, true, true, false);
668 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
669 mojom::SecondaryPlaybackProperties::New(
670 kCodecOpus, kCodecVP9, AudioCodecProfile::kUnknown,
671 VP9PROFILE_PROFILE0, "", "", EncryptionScheme::kUnencrypted,
672 EncryptionScheme::kUnencrypted, gfx::Size(800, 600));
673 Initialize(properties.Clone());
674 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
675
676 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54);
677 const base::TimeDelta kWatchTime2 = kWatchTime * 2;
678 const base::TimeDelta kWatchTime3 = kWatchTime / 3;
679 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime2);
680 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAc, kWatchTime);
681
682 // Ensure partial finalize does not affect final report.
683 wtr_->FinalizeWatchTime({WatchTimeKey::kAudioVideoAc});
684 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoBattery, kWatchTime);
685 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoNativeControlsOn, kWatchTime);
686 wtr_->FinalizeWatchTime({WatchTimeKey::kAudioVideoNativeControlsOn});
687 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoNativeControlsOff, kWatchTime);
688 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoDisplayFullscreen,
689 kWatchTime3);
690 wtr_->FinalizeWatchTime({WatchTimeKey::kAudioVideoDisplayFullscreen});
691 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoDisplayInline, kWatchTime3);
692 wtr_->FinalizeWatchTime({WatchTimeKey::kAudioVideoDisplayInline});
693 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoDisplayPictureInPicture,
694 kWatchTime3);
695 wtr_->UpdateUnderflowCount(3);
696 constexpr base::TimeDelta kUnderflowDuration =
697 base::TimeDelta::FromMilliseconds(500);
698 wtr_->UpdateUnderflowDuration(2, kUnderflowDuration);
699 wtr_->UpdateVideoDecodeStats(10, 2);
700 wtr_->OnError(PIPELINE_ERROR_DECODE);
701
702 secondary_properties->audio_decoder_name = "MojoAudioDecoder";
703 secondary_properties->video_decoder_name = "MojoVideoDecoder";
704 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
705
706 wtr_->SetAutoplayInitiated(true);
707
708 wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(9500));
709
710 wtr_.reset();
711 base::RunLoop().RunUntilIdle();
712
713 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
714 EXPECT_EQ(1u, entries.size());
715 for (const auto* entry : entries) {
716 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
717 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime2.InMilliseconds());
718 EXPECT_UKM(UkmEntry::kWatchTime_ACName, kWatchTime.InMilliseconds());
719 EXPECT_UKM(UkmEntry::kWatchTime_BatteryName, kWatchTime.InMilliseconds());
720 EXPECT_UKM(UkmEntry::kWatchTime_NativeControlsOnName,
721 kWatchTime.InMilliseconds());
722 EXPECT_UKM(UkmEntry::kWatchTime_NativeControlsOffName,
723 kWatchTime.InMilliseconds());
724 EXPECT_UKM(UkmEntry::kWatchTime_DisplayFullscreenName,
725 kWatchTime3.InMilliseconds());
726 EXPECT_UKM(UkmEntry::kWatchTime_DisplayInlineName,
727 kWatchTime3.InMilliseconds());
728 EXPECT_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName,
729 kWatchTime3.InMilliseconds());
730 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
731 kWatchTime2.InMilliseconds() / 3);
732 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
733
734 // Values taken from .cc private enumeration (and should never change).
735 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
736 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
737
738 // Duration should be rounded up.
739 EXPECT_UKM(UkmEntry::kDurationName, 10000000);
740
741 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
742 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
743 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
744 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
745 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
746 static_cast<int64_t>(secondary_properties->audio_codec_profile));
747 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
748 secondary_properties->video_codec_profile);
749 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
750 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
751 EXPECT_UKM(
752 UkmEntry::kAudioEncryptionSchemeName,
753 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
754 EXPECT_UKM(
755 UkmEntry::kVideoEncryptionSchemeName,
756 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
757 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
758 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
759 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
760 EXPECT_UKM(UkmEntry::kRebuffersCountName, 3);
761 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 2);
762 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
763 kUnderflowDuration.InMilliseconds());
764 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 10);
765 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 2);
766 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
767 secondary_properties->natural_size.width());
768 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
769 secondary_properties->natural_size.height());
770 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, true);
771 }
772 }
773
TEST_F(WatchTimeRecorderTest,BasicUkmAudioVideoBackgroundMuted)774 TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoBackgroundMuted) {
775 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
776 true, true, true, true, false, false, false);
777 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
778 CreateSecondaryProperties();
779 Initialize(properties.Clone());
780 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
781
782 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54);
783 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoBackgroundAll, kWatchTime);
784 wtr_.reset();
785 base::RunLoop().RunUntilIdle();
786
787 if (secondary_properties->audio_codec == kCodecAAC) {
788 ExpectAacAudioCodecProfileHistogram(
789 secondary_properties->audio_codec_profile);
790 }
791
792 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
793 EXPECT_EQ(1u, entries.size());
794 for (const auto* entry : entries) {
795 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
796
797 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds());
798 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
799 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
800 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
801 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
802 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
803 static_cast<int64_t>(secondary_properties->audio_codec_profile));
804 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
805 secondary_properties->video_codec_profile);
806 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
807 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
808 EXPECT_UKM(
809 UkmEntry::kAudioEncryptionSchemeName,
810 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
811 EXPECT_UKM(
812 UkmEntry::kVideoEncryptionSchemeName,
813 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
814 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
815 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
816 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
817 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
818 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
819 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
820 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
821 secondary_properties->natural_size.width());
822 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
823 secondary_properties->natural_size.height());
824 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
825 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
826 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
827 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
828
829 EXPECT_NO_UKM(UkmEntry::kDurationName);
830 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
831 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
832 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
833 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
834 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
835 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
836 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
837 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
838 }
839 }
840
TEST_F(WatchTimeRecorderTest,BasicUkmAudioVideoDuration)841 TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoDuration) {
842 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
843 true, true, false, false, false, false, false);
844 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
845 CreateSecondaryProperties();
846 Initialize(properties.Clone());
847 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
848
849 wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(12345));
850 wtr_.reset();
851 base::RunLoop().RunUntilIdle();
852
853 if (secondary_properties->audio_codec == kCodecAAC) {
854 ExpectAacAudioCodecProfileHistogram(
855 secondary_properties->audio_codec_profile);
856 }
857
858 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
859 EXPECT_EQ(1u, entries.size());
860 for (const auto* entry : entries) {
861 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
862
863 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
864 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
865 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
866 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
867 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
868 static_cast<int64_t>(secondary_properties->audio_codec_profile));
869 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
870 secondary_properties->video_codec_profile);
871 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
872 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
873 EXPECT_UKM(
874 UkmEntry::kAudioEncryptionSchemeName,
875 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
876 EXPECT_UKM(
877 UkmEntry::kVideoEncryptionSchemeName,
878 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
879 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
880 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
881 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
882 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
883 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
884 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
885 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
886 secondary_properties->natural_size.width());
887 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
888 secondary_properties->natural_size.height());
889 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
890 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
891 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
892 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
893
894 // Duration should be rounded to the most significant digit.
895 EXPECT_UKM(UkmEntry::kDurationName, 10000000);
896
897 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
898 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
899 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
900 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
901 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
902 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
903 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
904 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
905 }
906 }
907
TEST_F(WatchTimeRecorderTest,BasicUkmAudioVideoDurationInfinite)908 TEST_F(WatchTimeRecorderTest, BasicUkmAudioVideoDurationInfinite) {
909 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
910 true, true, false, false, false, false, false);
911 mojom::SecondaryPlaybackPropertiesPtr secondary_properties =
912 CreateSecondaryProperties();
913 Initialize(properties.Clone());
914 wtr_->UpdateSecondaryProperties(secondary_properties.Clone());
915
916 wtr_->OnDurationChanged(kInfiniteDuration);
917 wtr_.reset();
918 base::RunLoop().RunUntilIdle();
919
920 if (secondary_properties->audio_codec == kCodecAAC) {
921 ExpectAacAudioCodecProfileHistogram(
922 secondary_properties->audio_codec_profile);
923 }
924
925 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
926 EXPECT_EQ(1u, entries.size());
927 for (const auto* entry : entries) {
928 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
929
930 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
931 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
932 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties->audio_codec);
933 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties->video_codec);
934 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
935 static_cast<int64_t>(secondary_properties->audio_codec_profile));
936 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
937 secondary_properties->video_codec_profile);
938 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
939 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
940 EXPECT_UKM(
941 UkmEntry::kAudioEncryptionSchemeName,
942 static_cast<int64_t>(secondary_properties->audio_encryption_scheme));
943 EXPECT_UKM(
944 UkmEntry::kVideoEncryptionSchemeName,
945 static_cast<int64_t>(secondary_properties->video_encryption_scheme));
946 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
947 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
948 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
949 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
950 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
951 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
952 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
953 secondary_properties->natural_size.width());
954 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
955 secondary_properties->natural_size.height());
956 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
957 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 0);
958 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 0);
959 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
960
961 // Duration should be unrecorded when infinite.
962 EXPECT_NO_UKM(UkmEntry::kDurationName);
963 EXPECT_NO_UKM(UkmEntry::kWatchTimeName);
964 EXPECT_NO_UKM(UkmEntry::kMeanTimeBetweenRebuffersName);
965 EXPECT_NO_UKM(UkmEntry::kWatchTime_ACName);
966 EXPECT_NO_UKM(UkmEntry::kWatchTime_BatteryName);
967 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOnName);
968 EXPECT_NO_UKM(UkmEntry::kWatchTime_NativeControlsOffName);
969 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayFullscreenName);
970 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayInlineName);
971 EXPECT_NO_UKM(UkmEntry::kWatchTime_DisplayPictureInPictureName);
972 }
973 }
974
975 // Might happen due to timing issues, so ensure no crashes.
TEST_F(WatchTimeRecorderTest,NoSecondaryProperties)976 TEST_F(WatchTimeRecorderTest, NoSecondaryProperties) {
977 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
978 true, true, false, false, true, true, false);
979 Initialize(properties.Clone());
980
981 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54);
982 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime);
983 wtr_.reset();
984 base::RunLoop().RunUntilIdle();
985 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
986 EXPECT_EQ(0u, entries.size());
987 }
988
TEST_F(WatchTimeRecorderTest,SingleSecondaryPropertiesUnknownToKnown)989 TEST_F(WatchTimeRecorderTest, SingleSecondaryPropertiesUnknownToKnown) {
990 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
991 true, true, false, false, true, true, false);
992 mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
993 mojom::SecondaryPlaybackProperties::New(
994 kUnknownAudioCodec, kUnknownVideoCodec, AudioCodecProfile::kUnknown,
995 VIDEO_CODEC_PROFILE_UNKNOWN, "", "", EncryptionScheme::kUnencrypted,
996 EncryptionScheme::kUnencrypted, gfx::Size(800, 600));
997 Initialize(properties.Clone());
998 wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
999
1000 constexpr base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(54);
1001 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime);
1002
1003 mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
1004 mojom::SecondaryPlaybackProperties::New(
1005 kCodecAAC, kCodecH264, AudioCodecProfile::kXHE_AAC, H264PROFILE_MAIN,
1006 "FFmpegAudioDecoder", "FFmpegVideoDecoder",
1007 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1008 gfx::Size(800, 600));
1009 wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
1010
1011 wtr_.reset();
1012 base::RunLoop().RunUntilIdle();
1013
1014 // Since we only transitioned unknown values to known values, there should be
1015 // only a single UKM entry.
1016 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
1017 EXPECT_EQ(1u, entries.size());
1018 for (const auto* entry : entries) {
1019 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
1020 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
1021 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
1022 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
1023 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
1024 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
1025 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
1026 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
1027 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
1028 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
1029 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime.InMilliseconds());
1030 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
1031 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
1032 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
1033 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
1034 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
1035 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
1036 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
1037 EXPECT_UKM(
1038 UkmEntry::kAudioCodecProfileName,
1039 static_cast<int64_t>(secondary_properties2->audio_codec_profile));
1040 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1041 secondary_properties2->video_codec_profile);
1042 EXPECT_UKM(
1043 UkmEntry::kAudioEncryptionSchemeName,
1044 static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
1045 EXPECT_UKM(
1046 UkmEntry::kVideoEncryptionSchemeName,
1047 static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
1048 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1049 secondary_properties2->natural_size.width());
1050 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1051 secondary_properties2->natural_size.height());
1052 EXPECT_NO_UKM(UkmEntry::kDurationName);
1053 }
1054 }
1055
TEST_F(WatchTimeRecorderTest,MultipleSecondaryPropertiesNoFinalize)1056 TEST_F(WatchTimeRecorderTest, MultipleSecondaryPropertiesNoFinalize) {
1057 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
1058 true, true, false, false, true, true, false);
1059 mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
1060 mojom::SecondaryPlaybackProperties::New(
1061 kCodecOpus, kCodecVP9, AudioCodecProfile::kUnknown,
1062 VP9PROFILE_PROFILE0, "MojoAudioDecoder", "MojoVideoDecoder",
1063 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1064 gfx::Size(400, 300));
1065 Initialize(properties.Clone());
1066 wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
1067
1068 constexpr base::TimeDelta kUnderflowDuration =
1069 base::TimeDelta::FromMilliseconds(250);
1070 constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
1071 const int kUnderflowCount1 = 2;
1072 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
1073 wtr_->UpdateUnderflowCount(kUnderflowCount1);
1074 wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
1075
1076 constexpr int kDecodedFrameCount1 = 10;
1077 constexpr int kDroppedFrameCount1 = 2;
1078 wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
1079
1080 mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
1081 mojom::SecondaryPlaybackProperties::New(
1082 kCodecAAC, kCodecH264, AudioCodecProfile::kUnknown, H264PROFILE_MAIN,
1083 "FFmpegAudioDecoder", "FFmpegVideoDecoder", EncryptionScheme::kCenc,
1084 EncryptionScheme::kCenc, gfx::Size(800, 600));
1085 wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
1086
1087 constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(25);
1088 const int kUnderflowCount2 = 3;
1089
1090 // Watch time and underflow counts continue to accumulate during property
1091 // changes, so we report the sum here instead of just kWatchTime2.
1092 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll,
1093 kWatchTime1 + kWatchTime2);
1094 wtr_->UpdateUnderflowCount(kUnderflowCount1 + kUnderflowCount2);
1095 wtr_->OnError(PIPELINE_ERROR_DECODE);
1096 wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(5125));
1097
1098 constexpr int kDecodedFrameCount2 = 20;
1099 constexpr int kDroppedFrameCount2 = 10;
1100 wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1 + kDecodedFrameCount2,
1101 kDroppedFrameCount1 + kDroppedFrameCount2);
1102
1103 wtr_.reset();
1104 base::RunLoop().RunUntilIdle();
1105
1106 // All records should have the following:
1107 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
1108 EXPECT_EQ(2u, entries.size());
1109 for (const auto* entry : entries) {
1110 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
1111 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
1112 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
1113 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
1114 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
1115 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
1116 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
1117 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
1118 EXPECT_UKM(UkmEntry::kDurationName, 5000000);
1119 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
1120
1121 // All records inherit the final pipeline status code.
1122 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
1123 }
1124
1125 // The first record should have...
1126 auto* entry = entries[0];
1127 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime1.InMilliseconds());
1128 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1129 kWatchTime1.InMilliseconds() / kUnderflowCount1);
1130 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
1131 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
1132 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
1133 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
1134 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
1135 kUnderflowDuration.InMilliseconds());
1136 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
1137 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
1138 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
1139 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
1140 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1141 static_cast<int64_t>(secondary_properties1->audio_codec_profile));
1142 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1143 secondary_properties1->video_codec_profile);
1144 EXPECT_UKM(
1145 UkmEntry::kAudioEncryptionSchemeName,
1146 static_cast<int64_t>(secondary_properties1->audio_encryption_scheme));
1147 EXPECT_UKM(
1148 UkmEntry::kVideoEncryptionSchemeName,
1149 static_cast<int64_t>(secondary_properties1->video_encryption_scheme));
1150 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1151 secondary_properties1->natural_size.width());
1152 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1153 secondary_properties1->natural_size.height());
1154
1155 // The second record should have...
1156 entry = entries[1];
1157 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime2.InMilliseconds());
1158 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1159 kWatchTime2.InMilliseconds() / kUnderflowCount2);
1160 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
1161 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
1162 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
1163 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
1164 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount2);
1165 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount2);
1166
1167 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
1168 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
1169 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
1170 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1171 static_cast<int64_t>(secondary_properties2->audio_codec_profile));
1172 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1173 secondary_properties2->video_codec_profile);
1174 EXPECT_UKM(
1175 UkmEntry::kAudioEncryptionSchemeName,
1176 static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
1177 EXPECT_UKM(
1178 UkmEntry::kVideoEncryptionSchemeName,
1179 static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
1180 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1181 secondary_properties2->natural_size.width());
1182 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1183 secondary_properties2->natural_size.height());
1184 }
1185
TEST_F(WatchTimeRecorderTest,MultipleSecondaryPropertiesNoFinalizeNo2ndWT)1186 TEST_F(WatchTimeRecorderTest, MultipleSecondaryPropertiesNoFinalizeNo2ndWT) {
1187 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
1188 true, true, false, false, true, true, false);
1189 mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
1190 mojom::SecondaryPlaybackProperties::New(
1191 kCodecOpus, kCodecVP9, AudioCodecProfile::kUnknown,
1192 VP9PROFILE_PROFILE0, "MojoAudioDecoder", "MojoVideoDecoder",
1193 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1194 gfx::Size(400, 300));
1195 Initialize(properties.Clone());
1196 wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
1197
1198 constexpr base::TimeDelta kUnderflowDuration =
1199 base::TimeDelta::FromMilliseconds(250);
1200 constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
1201 const int kUnderflowCount1 = 2;
1202 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
1203 wtr_->UpdateUnderflowCount(kUnderflowCount1);
1204 wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
1205
1206 constexpr int kDecodedFrameCount1 = 10;
1207 constexpr int kDroppedFrameCount1 = 2;
1208 wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
1209
1210 mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
1211 mojom::SecondaryPlaybackProperties::New(
1212 kCodecAAC, kCodecH264, AudioCodecProfile::kXHE_AAC, H264PROFILE_MAIN,
1213 "FFmpegAudioDecoder", "FFmpegVideoDecoder",
1214 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1215 gfx::Size(800, 600));
1216 wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
1217
1218 // Don't record any watch time to the new record, it should report zero watch
1219 // time upon destruction. This ensures there's always a Finalize to prevent
1220 // UKM was receiving negative values from the previous unfinalized record.
1221 wtr_.reset();
1222 base::RunLoop().RunUntilIdle();
1223
1224 // All records should have the following:
1225 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
1226 EXPECT_EQ(2u, entries.size());
1227 for (const auto* entry : entries) {
1228 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
1229 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
1230 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
1231 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
1232 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
1233 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
1234 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
1235 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
1236 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
1237 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
1238 EXPECT_NO_UKM(UkmEntry::kDurationName);
1239 }
1240
1241 // The first record should have...
1242 auto* entry = entries[0];
1243 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime1.InMilliseconds());
1244 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1245 kWatchTime1.InMilliseconds() / kUnderflowCount1);
1246 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
1247 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
1248 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
1249 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
1250 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
1251 kUnderflowDuration.InMilliseconds());
1252 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
1253 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
1254 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
1255 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
1256 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1257 static_cast<int64_t>(secondary_properties1->audio_codec_profile));
1258 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1259 secondary_properties1->video_codec_profile);
1260 EXPECT_UKM(
1261 UkmEntry::kAudioEncryptionSchemeName,
1262 static_cast<int64_t>(secondary_properties1->audio_encryption_scheme));
1263 EXPECT_UKM(
1264 UkmEntry::kVideoEncryptionSchemeName,
1265 static_cast<int64_t>(secondary_properties1->video_encryption_scheme));
1266 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1267 secondary_properties1->natural_size.width());
1268 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1269 secondary_properties1->natural_size.height());
1270
1271 // The second record should have...
1272 entry = entries[1];
1273 EXPECT_UKM(UkmEntry::kWatchTimeName, 0);
1274 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
1275 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
1276 EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
1277 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
1278 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
1279 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 0);
1280 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 0);
1281 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
1282 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
1283 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1284 static_cast<int64_t>(secondary_properties2->audio_codec_profile));
1285 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1286 secondary_properties2->video_codec_profile);
1287 EXPECT_UKM(
1288 UkmEntry::kAudioEncryptionSchemeName,
1289 static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
1290 EXPECT_UKM(
1291 UkmEntry::kVideoEncryptionSchemeName,
1292 static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
1293 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1294 secondary_properties2->natural_size.width());
1295 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1296 secondary_properties2->natural_size.height());
1297 }
1298
TEST_F(WatchTimeRecorderTest,MultipleSecondaryPropertiesWithFinalize)1299 TEST_F(WatchTimeRecorderTest, MultipleSecondaryPropertiesWithFinalize) {
1300 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
1301 true, true, false, false, true, true, false);
1302 mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
1303 mojom::SecondaryPlaybackProperties::New(
1304 kCodecOpus, kCodecVP9, AudioCodecProfile::kUnknown,
1305 VP9PROFILE_PROFILE0, "MojoAudioDecoder", "MojoVideoDecoder",
1306 EncryptionScheme::kCbcs, EncryptionScheme::kCbcs,
1307 gfx::Size(400, 300));
1308 Initialize(properties.Clone());
1309 wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
1310
1311 constexpr base::TimeDelta kUnderflowDuration =
1312 base::TimeDelta::FromMilliseconds(250);
1313 constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
1314 const int kUnderflowCount1 = 2;
1315 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
1316 wtr_->UpdateUnderflowCount(kUnderflowCount1);
1317 wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
1318
1319 constexpr int kDecodedFrameCount1 = 10;
1320 constexpr int kDroppedFrameCount1 = 2;
1321 wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
1322
1323 // Force a finalize here so that the there is no unfinalized watch time at the
1324 // time of the secondary property update.
1325 wtr_->FinalizeWatchTime({});
1326
1327 mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
1328 mojom::SecondaryPlaybackProperties::New(
1329 kCodecAAC, kCodecH264, AudioCodecProfile::kXHE_AAC, H264PROFILE_MAIN,
1330 "FFmpegAudioDecoder", "FFmpegVideoDecoder",
1331 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1332 gfx::Size(800, 600));
1333 wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
1334
1335 constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(25);
1336 const int kUnderflowCount2 = 3;
1337
1338 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime2);
1339 wtr_->UpdateUnderflowCount(kUnderflowCount2);
1340 wtr_->OnError(PIPELINE_ERROR_DECODE);
1341
1342 wtr_.reset();
1343 base::RunLoop().RunUntilIdle();
1344
1345 // All records should have the following:
1346 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
1347 EXPECT_EQ(2u, entries.size());
1348 for (const auto* entry : entries) {
1349 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
1350 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
1351 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
1352 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
1353 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
1354 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
1355 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
1356 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
1357 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
1358 EXPECT_NO_UKM(UkmEntry::kDurationName);
1359
1360 // All records inherit the final pipeline status code.
1361 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
1362 }
1363
1364 // The first record should have...
1365 auto* entry = entries[0];
1366 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime1.InMilliseconds());
1367 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1368 kWatchTime1.InMilliseconds() / kUnderflowCount1);
1369 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
1370 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
1371 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
1372 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
1373 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
1374 kUnderflowDuration.InMilliseconds());
1375 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
1376 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
1377 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
1378 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
1379 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1380 static_cast<int64_t>(secondary_properties1->audio_codec_profile));
1381 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1382 secondary_properties1->video_codec_profile);
1383 EXPECT_UKM(
1384 UkmEntry::kAudioEncryptionSchemeName,
1385 static_cast<int64_t>(secondary_properties1->audio_encryption_scheme));
1386 EXPECT_UKM(
1387 UkmEntry::kVideoEncryptionSchemeName,
1388 static_cast<int64_t>(secondary_properties1->video_encryption_scheme));
1389 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1390 secondary_properties1->natural_size.width());
1391 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1392 secondary_properties1->natural_size.height());
1393
1394 // The second record should have...
1395 entry = entries[1];
1396 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime2.InMilliseconds());
1397 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1398 kWatchTime2.InMilliseconds() / kUnderflowCount2);
1399 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
1400 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
1401 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
1402 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
1403 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
1404 EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 0);
1405 EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 0);
1406 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
1407 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
1408 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1409 static_cast<int64_t>(secondary_properties2->audio_codec_profile));
1410 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1411 secondary_properties2->video_codec_profile);
1412 EXPECT_UKM(
1413 UkmEntry::kAudioEncryptionSchemeName,
1414 static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
1415 EXPECT_UKM(
1416 UkmEntry::kVideoEncryptionSchemeName,
1417 static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
1418 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1419 secondary_properties2->natural_size.width());
1420 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1421 secondary_properties2->natural_size.height());
1422 }
1423
TEST_F(WatchTimeRecorderTest,MultipleSecondaryPropertiesRebufferCarryover)1424 TEST_F(WatchTimeRecorderTest, MultipleSecondaryPropertiesRebufferCarryover) {
1425 mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
1426 true, true, false, false, true, true, false);
1427 mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
1428 mojom::SecondaryPlaybackProperties::New(
1429 kCodecOpus, kCodecVP9, AudioCodecProfile::kUnknown,
1430 VP9PROFILE_PROFILE0, "MojoAudioDecoder", "MojoVideoDecoder",
1431 EncryptionScheme::kCbcs, EncryptionScheme::kCbcs,
1432 gfx::Size(400, 300));
1433 Initialize(properties.Clone());
1434 wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
1435
1436 constexpr base::TimeDelta kUnderflowDuration =
1437 base::TimeDelta::FromMilliseconds(250);
1438 constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
1439 const int kUnderflowCount1 = 2;
1440 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
1441 wtr_->UpdateUnderflowCount(kUnderflowCount1);
1442
1443 // Complete all but one of the rebuffers in this update.
1444 wtr_->UpdateUnderflowDuration(kUnderflowCount1 - 1, kUnderflowDuration);
1445
1446 mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
1447 mojom::SecondaryPlaybackProperties::New(
1448 kCodecAAC, kCodecH264, AudioCodecProfile::kXHE_AAC, H264PROFILE_MAIN,
1449 "FFmpegAudioDecoder", "FFmpegVideoDecoder",
1450 EncryptionScheme::kUnencrypted, EncryptionScheme::kUnencrypted,
1451 gfx::Size(800, 600));
1452 wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
1453
1454 constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(25);
1455 const int kUnderflowCount2 = 3;
1456
1457 // Watch time and underflow counts continue to accumulate during property
1458 // changes, so we report the sum here instead of just kWatchTime2.
1459 wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll,
1460 kWatchTime1 + kWatchTime2);
1461 wtr_->UpdateUnderflowCount(kUnderflowCount1 + kUnderflowCount2);
1462
1463 // Complete the last underflow in the new property set. Unfortunately this
1464 // means it will now be associated with this block of watch time. Use a non
1465 // integer multiplier to avoid incorrect carry over being hidden.
1466 wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration * 1.5);
1467
1468 wtr_->OnError(PIPELINE_ERROR_DECODE);
1469 wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(5125));
1470
1471 wtr_.reset();
1472 base::RunLoop().RunUntilIdle();
1473
1474 // All records should have the following:
1475 const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
1476 EXPECT_EQ(2u, entries.size());
1477 for (const auto* entry : entries) {
1478 test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
1479 EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
1480 EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
1481 EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
1482 EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
1483 EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
1484 EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
1485 EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
1486 EXPECT_UKM(UkmEntry::kDurationName, 5000000);
1487 EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
1488
1489 // All records inherit the final pipeline status code.
1490 EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
1491 }
1492
1493 // The first record should have...
1494 auto* entry = entries[0];
1495 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime1.InMilliseconds());
1496 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1497 kWatchTime1.InMilliseconds() / kUnderflowCount1);
1498 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
1499 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
1500 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
1501 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1 - 1);
1502 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
1503 kUnderflowDuration.InMilliseconds());
1504 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
1505 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
1506 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1507 static_cast<int64_t>(secondary_properties1->audio_codec_profile));
1508 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1509 secondary_properties1->video_codec_profile);
1510 EXPECT_UKM(
1511 UkmEntry::kAudioEncryptionSchemeName,
1512 static_cast<int64_t>(secondary_properties1->audio_encryption_scheme));
1513 EXPECT_UKM(
1514 UkmEntry::kVideoEncryptionSchemeName,
1515 static_cast<int64_t>(secondary_properties1->video_encryption_scheme));
1516 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1517 secondary_properties1->natural_size.width());
1518 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1519 secondary_properties1->natural_size.height());
1520
1521 // The second record should have...
1522 entry = entries[1];
1523 EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime2.InMilliseconds());
1524 EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
1525 kWatchTime2.InMilliseconds() / kUnderflowCount2);
1526 EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
1527 EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
1528 EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
1529 EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 1);
1530 EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
1531 (kUnderflowDuration * 1.5 - kUnderflowDuration).InMilliseconds());
1532 EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
1533 EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
1534 EXPECT_UKM(UkmEntry::kAudioCodecProfileName,
1535 static_cast<int64_t>(secondary_properties2->audio_codec_profile));
1536 EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
1537 secondary_properties2->video_codec_profile);
1538 EXPECT_UKM(
1539 UkmEntry::kAudioEncryptionSchemeName,
1540 static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
1541 EXPECT_UKM(
1542 UkmEntry::kVideoEncryptionSchemeName,
1543 static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
1544 EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
1545 secondary_properties2->natural_size.width());
1546 EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
1547 secondary_properties2->natural_size.height());
1548 }
1549
1550 #undef EXPECT_UKM
1551 #undef EXPECT_NO_UKM
1552 #undef EXPECT_HAS_UKM
1553
TEST_F(WatchTimeRecorderTest,DISABLED_PrintExpectedDecoderNameHashes)1554 TEST_F(WatchTimeRecorderTest, DISABLED_PrintExpectedDecoderNameHashes) {
1555 const std::string kDecoderNames[] = {
1556 "FFmpegAudioDecoder", "FFmpegVideoDecoder", "GpuVideoDecoder",
1557 "MojoVideoDecoder", "MojoAudioDecoder", "VpxVideoDecoder",
1558 "AomVideoDecoder", "DecryptingAudioDecoder", "DecryptingVideoDecoder",
1559 "Dav1dVideoDecoder", "FuchsiaVideoDecoder", "MediaPlayer",
1560 "Gav1VideoDecoder"};
1561 printf("%18s = 0\n", "None");
1562 for (const auto& name : kDecoderNames)
1563 printf("%18s = 0x%08x\n", name.c_str(), base::PersistentHash(name));
1564 }
1565
1566 } // namespace media
1567