diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index 3a74ac3b477f..3dd22c325cc2 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2085,6 +2085,10 @@ double HTMLMediaElement::TotalPlayTime() const {
return mDecoder ? mDecoder->GetTotalPlayTimeInSeconds() : -1.0;
}
+double HTMLMediaElement::VisiblePlayTime() const {
+ return mDecoder ? mDecoder->GetVisibleVideoPlayTimeInSeconds() : -1.0;
+}
+
double HTMLMediaElement::InvisiblePlayTime() const {
return mDecoder ? mDecoder->GetInvisibleVideoPlayTimeInSeconds() : -1.0;
}
diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h
index 159135549381..fd80f4e202d4 100644
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -656,6 +656,7 @@ class HTMLMediaElement : public nsGenericHTMLElement,
// These functions return accumulated time, which are used for the telemetry
// usage. Return -1 for error.
double TotalPlayTime() const;
+ double VisiblePlayTime() const;
double InvisiblePlayTime() const;
double VideoDecodeSuspendedTime() const;
diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp
index 9c93a76be1f7..89aa03a0d803 100644
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1400,6 +1400,10 @@ double MediaDecoder::GetTotalPlayTimeInSeconds() const {
return mTelemetryProbesReporter->GetTotalPlayTimeInSeconds();
}
+double MediaDecoder::GetVisibleVideoPlayTimeInSeconds() const {
+ return mTelemetryProbesReporter->GetVisibleVideoPlayTimeInSeconds();
+}
+
double MediaDecoder::GetInvisibleVideoPlayTimeInSeconds() const {
return mTelemetryProbesReporter->GetInvisibleVideoPlayTimeInSeconds();
}
diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h
index 321e7a9cade9..24b02818ab87 100644
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -720,6 +720,7 @@ class MediaDecoder : public DecoderDoctorLifeLogger {
// They are used for reporting telemetry related results.
double GetTotalPlayTimeInSeconds() const;
+ double GetVisibleVideoPlayTimeInSeconds() const;
double GetInvisibleVideoPlayTimeInSeconds() const;
double GetVideoDecodeSuspendedTimeInSeconds() const;
diff --git a/dom/media/test/chrome/test_accumulated_play_time.html b/dom/media/test/chrome/test_accumulated_play_time.html
index 509ebd5a07dc..70c14e502e23 100644
--- a/dom/media/test/chrome/test_accumulated_play_time.html
+++ b/dom/media/test/chrome/test_accumulated_play_time.html
@@ -14,9 +14,10 @@
* - VIDEO_HIDDEN_PLAY_TIME_MS
* - VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE
* - VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE
+ * - VIDEO_VISIBLE_PLAY_TIME_MS
*/
const histNames = ["VIDEO_PLAY_TIME_MS", "VIDEO_HIDDEN_PLAY_TIME_MS"];
-const keyedHistNames = ["VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE", "VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE"];
+const keyedHistNames = ["VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE", "VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE", "VIDEO_VISIBLE_PLAY_TIME_MS"];
add_task(async function setTestPref() {
await SpecialPowers.pushPrefEnv({
@@ -36,7 +37,6 @@ add_task(async function testTotalPlayTime() {
await new Promise(r => video.onloadeddata = r);
assertValueEqualTo(videoChrome, "totalPlayTime", 0);
assertValueEqualTo(videoChrome, "invisiblePlayTime", 0);
- assertValueEqualTo(videoChrome, "videoDecodeSuspendedTime", 0);
info(`start accumulating play time after media starts`);
video.autoplay = true;
@@ -44,7 +44,7 @@ add_task(async function testTotalPlayTime() {
once(video, "playing"),
once(video, "moztotalplaytimestarted"),
]);
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
assertValueKeptUnchanged(videoChrome, "invisiblePlayTime");
assertValueKeptUnchanged(videoChrome, "videoDecodeSuspendedTime");
@@ -52,6 +52,7 @@ add_task(async function testTotalPlayTime() {
video.pause();
await once(video, "moztotalplaytimepaused");
assertValueKeptUnchanged(videoChrome, "totalPlayTime");
+ assertValueEqualTo(videoChrome, "totalPlayTime", 0);
info(`should start accumulating time again`);
let rv = await Promise.all([
@@ -59,7 +60,37 @@ add_task(async function testTotalPlayTime() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started again");
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await cleanUpMediaAndCheckTelemetry(video);
+});
+
+add_task(async function testVisiblePlayTime() {
+ const video = document.createElement('video');
+ video.src = "gizmo.mp4";
+ document.body.appendChild(video);
+
+ info(`all accumulated time should be zero`);
+ const videoChrome = SpecialPowers.wrap(video);
+ await new Promise(r => video.onloadeddata = r);
+ assertValueEqualTo(videoChrome, "totalPlayTime", 0);
+ assertValueEqualTo(videoChrome, "visiblePlayTime", 0);
+ assertValueEqualTo(videoChrome, "invisiblePlayTime", 0);
+
+ info(`start accumulating play time after media starts`);
+ video.autoplay = true;
+ await Promise.all([
+ once(video, "playing"),
+ once(video, "moztotalplaytimestarted"),
+ ]);
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "visiblePlayTime");
+ assertValueKeptUnchanged(videoChrome, "invisiblePlayTime");
+
+ info(`make video invisible`);
+ document.body.removeChild(video);
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
+ assertValueKeptUnchanged(videoChrome, "visiblePlayTime");
await cleanUpMediaAndCheckTelemetry(video);
});
@@ -90,7 +121,7 @@ add_task(async function testHiddenPlayTime() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started playing");
- assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
info(`should not accumulate time for paused video`);
video.pause();
@@ -103,7 +134,7 @@ add_task(async function testHiddenPlayTime() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started again");
- assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
info(`make video visible should stop accumulating invisible related time`);
if (reason == "notInTree" || reason == "notInConnectedTree") {
@@ -138,16 +169,16 @@ add_task(async function testDecodeSuspendedTime() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started playing");
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
assertValueKeptUnchanged(videoChrome, "invisiblePlayTime");
assertValueKeptUnchanged(videoChrome, "videoDecodeSuspendedTime");
info(`make it invisible and force to suspend decoding`);
video.setVisible(false);
await once(video, "mozvideodecodesuspendedstarted");
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
- assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
- assertValueConstantlyIncreases(videoChrome, "videoDecodeSuspendedTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "invisiblePlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "videoDecodeSuspendedTime");
info(`make it visible and resume decoding`);
video.setVisible(true);
@@ -155,7 +186,7 @@ add_task(async function testDecodeSuspendedTime() {
once(video, "mozinvisibleplaytimepaused"),
once(video, "mozvideodecodesuspendedpaused"),
]);
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
assertValueKeptUnchanged(videoChrome, "invisiblePlayTime");
assertValueKeptUnchanged(videoChrome, "videoDecodeSuspendedTime");
await cleanUpMediaAndCheckTelemetry(video);
@@ -173,7 +204,7 @@ add_task(async function reuseSameElementForPlayback() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started again");
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
info(`reset its src and all accumulated value should be reset after then`);
// After setting its src to nothing, that would trigger a failed load and set
@@ -195,7 +226,7 @@ add_task(async function reuseSameElementForPlayback() {
video.play().then(_ => true, _ => false),
]);
ok(returnTrueWhenAllValuesAreTrue(rv), "video started");
- assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
+ await assertValueConstantlyIncreases(videoChrome, "totalPlayTime");
await cleanUpMediaAndCheckTelemetry(video);
});
@@ -329,16 +360,19 @@ function assertValueEqualTo(mediaChrome, checkType, expectedValue) {
is(mediaChrome[checkType], expectedValue, `${checkType} equals to ${expectedValue}`);
}
-function assertValueConstantlyIncreases(mediaChrome, checkType) {
+async function assertValueConstantlyIncreases(mediaChrome, checkType) {
assertAttributeDefined(mediaChrome, checkType);
const valueSnapshot = mediaChrome[checkType];
- ok(mediaChrome[checkType] > valueSnapshot, `${checkType} keeps increasing`);
+ // Simply wait for a short while.
+ await new Promise(r => r());
+ const current = mediaChrome[checkType];
+ ok(current > valueSnapshot, `${checkType} keeps increasing (${current} > ${valueSnapshot})`);
}
function assertValueKeptUnchanged(mediaChrome, checkType) {
assertAttributeDefined(mediaChrome, checkType);
const valueSnapshot = mediaChrome[checkType];
- ok(mediaChrome[checkType] == valueSnapshot, `${checkType} keeps unchanged`);
+ is(mediaChrome[checkType], valueSnapshot, `${checkType} keeps unchanged (${valueSnapshot})`);
}
function assertAllProbeRelatedAttributesKeptUnchanged(video) {
diff --git a/dom/media/utils/TelemetryProbesReporter.cpp b/dom/media/utils/TelemetryProbesReporter.cpp
index 6f3e71eede99..99373b309575 100644
--- a/dom/media/utils/TelemetryProbesReporter.cpp
+++ b/dom/media/utils/TelemetryProbesReporter.cpp
@@ -310,6 +310,10 @@ double TelemetryProbesReporter::GetTotalPlayTimeInSeconds() const {
return mTotalPlayTime.PeekTotal();
}
+double TelemetryProbesReporter::GetVisibleVideoPlayTimeInSeconds() const {
+ return GetTotalPlayTimeInSeconds() - GetInvisibleVideoPlayTimeInSeconds();
+}
+
double TelemetryProbesReporter::GetInvisibleVideoPlayTimeInSeconds() const {
return mInvisibleVideoPlayTime.PeekTotal();
}
diff --git a/dom/media/utils/TelemetryProbesReporter.h b/dom/media/utils/TelemetryProbesReporter.h
index 4c1451e6fb93..a50daaca6d39 100644
--- a/dom/media/utils/TelemetryProbesReporter.h
+++ b/dom/media/utils/TelemetryProbesReporter.h
@@ -45,6 +45,7 @@ class TelemetryProbesReporter final {
void OnShutdown();
double GetTotalPlayTimeInSeconds() const;
+ double GetVisibleVideoPlayTimeInSeconds() const;
double GetInvisibleVideoPlayTimeInSeconds() const;
double GetVideoDecodeSuspendedTimeInSeconds() const;
diff --git a/dom/webidl/HTMLMediaElement.webidl b/dom/webidl/HTMLMediaElement.webidl
index d7219537b381..4c9f4f138fc9 100644
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -231,6 +231,9 @@ partial interface HTMLMediaElement {
[ChromeOnly]
readonly attribute double totalPlayTime;
+ [ChromeOnly]
+ readonly attribute double visiblePlayTime;
+
[ChromeOnly]
readonly attribute double invisiblePlayTime;