Bug 1937776 - Part 2: Add mochitest to verify VideoFrameCallbackMetadata receiveTime/captureTime alignment. r=pehrsons a=pascalc
Differential Revision: https://phabricator.services.mozilla.com/D252022
This commit is contained in:
@@ -77,6 +77,8 @@ run-sequentially = "sets prefs that may disrupt other tests"
|
||||
["test_ondevicechange_resistfingerprinting.html"]
|
||||
run-sequentially = "sets prefs that may disrupt other tests"
|
||||
|
||||
["test_rvfc_timestamp_alignment.html"]
|
||||
|
||||
["test_setSinkId-echoCancellation.html"]
|
||||
skip-if = ["os == 'android'"] # bug 1473346 - no setSinkId()
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="pc.js" type="application/javascript"></script>
|
||||
<script src="/tests/dom/canvas/test/captureStream_common.js" type="application/javascript"></script>
|
||||
<script src="stats.js" type="application/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "1937776",
|
||||
title: "Verify VideoFrameCallback receiveTime/captureTime alignment",
|
||||
});
|
||||
|
||||
const {AppConstants} = SpecialPowers.ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
runNetworkTest(async function(options) {
|
||||
async function WAIT_FOR_SYNCED_RTCP(test, count) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await waitForSyncedRtcp(test.pcLocal._pc);
|
||||
await waitForSyncedRtcp(test.pcRemote._pc);
|
||||
}
|
||||
}
|
||||
|
||||
async function CHECK_TIMESTAMP_ALIGNMENT(test, ok_or_todo, header_text) {
|
||||
const video = test.pcRemote.remoteMediaElements[0];
|
||||
const maxFrames = 10;
|
||||
const maxRecvDelta = 250;
|
||||
const maxCaptDelta = 250;
|
||||
let frameCount = 0;
|
||||
|
||||
// macOS fixes for high jitter on try
|
||||
const recvOffset = AppConstants.platform == "macosx" ? -200 : 0;
|
||||
const captOffset = AppConstants.platform == "macosx" ? -200 : 0;
|
||||
|
||||
// captureTime may not be present for several frames
|
||||
const maxMissedCaptCount = 8;
|
||||
let missedCaptCount = 0;
|
||||
|
||||
info(`***** RUNNING ${header_text} ALIGNMENT TEST`);
|
||||
info(`***** USING ASSERT ${ok_or_todo == ok ? "ok" : "todo"}() FOR captureTime`);
|
||||
info(`***** APPLYING ${recvOffset} ms OFFSET TO receiveTime ASSERTS`);
|
||||
|
||||
while (++frameCount <= maxFrames) {
|
||||
info(`Checking frame #${frameCount} of ${maxFrames}`);
|
||||
const {now, metadata} =
|
||||
await new Promise(r => video.requestVideoFrameCallback(
|
||||
(now, metadata) => r({now, metadata})));
|
||||
const {presentationTime, receiveTime, captureTime} = metadata;
|
||||
|
||||
ok(receiveTime, "receiveTime is present");
|
||||
if (receiveTime) {
|
||||
const dt = presentationTime - receiveTime;
|
||||
ok(dt > recvOffset, `receiveTime (${receiveTime.toFixed(2)} ms) < `
|
||||
+ `presentationTime (${presentationTime.toFixed(2)} ms)`);
|
||||
if (dt > recvOffset) {
|
||||
ok(dt <= maxRecvDelta, `receiveTime delta (${dt.toFixed(2)} ms) <= ${maxRecvDelta} ms`);
|
||||
}
|
||||
}
|
||||
|
||||
if (captureTime) {
|
||||
ok_or_todo(captureTime, `captureTime is present`);
|
||||
const dt = presentationTime - captureTime;
|
||||
ok(dt > captOffset, `captureTime (${captureTime.toFixed(2)} ms) < `
|
||||
+ `presentationTime (${presentationTime.toFixed(2)} ms)`);
|
||||
if (dt > captOffset) {
|
||||
ok(dt <= maxCaptDelta, `captureTime delta (${dt.toFixed(2)} ms) <= ${maxCaptDelta} ms`);
|
||||
}
|
||||
} else {
|
||||
info(`!!!!! Missing captureTime!`);
|
||||
// Skip additional asserts if captureTime isn't expected to avoid
|
||||
// overcomplicating the logic that allows for a limited number of misses.
|
||||
if (ok_or_todo == todo) {
|
||||
todo(captureTime, `captureTime is present`);
|
||||
continue;
|
||||
}
|
||||
ok(++missedCaptCount <= maxMissedCaptCount,
|
||||
`Miss #${missedCaptCount} <= allowed misses (${maxMissedCaptCount})`);
|
||||
ok(missedCaptCount == frameCount,
|
||||
"missing captureTime occurred sequentially from test start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function arg_wrapper(fn, argsArr) {
|
||||
return async function wrap() {
|
||||
return fn(...argsArr);
|
||||
};
|
||||
}
|
||||
|
||||
SimpleTest.requestCompleteLog();
|
||||
await pushPrefs(
|
||||
["media.video_loopback_dev", ""],
|
||||
["media.navigator.streams.fake", true]
|
||||
);
|
||||
|
||||
// Note we use two RTCP syncs in these tests as we currently need two
|
||||
// RTCP measurements in order to estimate a NTP timestamp.
|
||||
// See: Use of rtp_to_ntp_.Estimate(rtp_timestamp) in
|
||||
// RemoteNtpTimeEstimator::EstimateNtp() and
|
||||
// the two measurement requirement in
|
||||
// RtpToNtpEstimator::UpdateParameters()
|
||||
// (as of 618678dd32e52dbaa8a56d72c6655032f48068b3)
|
||||
|
||||
// Unidirectional captureTime is currently BROKEN.
|
||||
// See: bug 1971078 , bug 1971117
|
||||
const test_uni = new PeerConnectionTest(options);
|
||||
test_uni.setMediaConstraints([{ video: true }], []);
|
||||
test_uni.chain.append([
|
||||
arg_wrapper(WAIT_FOR_SYNCED_RTCP, [test_uni, 2]),
|
||||
arg_wrapper(CHECK_TIMESTAMP_ALIGNMENT, [test_uni, todo, "UNIDIRECTIONAL"]),
|
||||
]);
|
||||
await test_uni.run();
|
||||
|
||||
// Bidirectional test should pass.
|
||||
const test_bi = new PeerConnectionTest(options);
|
||||
test_bi.setMediaConstraints([{ video: true }], [{ video: true }]);
|
||||
test_bi.chain.append([
|
||||
arg_wrapper(WAIT_FOR_SYNCED_RTCP, [test_bi, 2]),
|
||||
arg_wrapper(CHECK_TIMESTAMP_ALIGNMENT, [test_bi, ok, "BIDIRECTIONAL"]),
|
||||
]);
|
||||
await test_bi.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user