зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1809985 - part1 : add a test to ensure that playback can be recovered from crash. r=jolin
Differential Revision: https://phabricator.services.mozilla.com/D166721
This commit is contained in:
Родитель
9ebf1f03a8
Коммит
b6fd1283a6
|
@ -98,6 +98,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||||
"mediacontrol/tests/browser/browser.ini",
|
"mediacontrol/tests/browser/browser.ini",
|
||||||
"mediasession/test/browser.ini",
|
"mediasession/test/browser.ini",
|
||||||
"test/browser/browser.ini",
|
"test/browser/browser.ini",
|
||||||
|
"test/browser/wmfme/browser.ini",
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG["MOZ_WEBRTC"]:
|
if CONFIG["MOZ_WEBRTC"]:
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
[DEFAULT]
|
||||||
|
subsuite = media-bc
|
||||||
|
tags = media-engine-compatible
|
||||||
|
run-if = wmfme
|
||||||
|
support-files =
|
||||||
|
head.js
|
||||||
|
file_video.html
|
||||||
|
../../gizmo.mp4
|
||||||
|
|
||||||
|
[browser_wmfme_crash.js]
|
|
@ -0,0 +1,52 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test aims to ensure that the media engine playback will recover from a
|
||||||
|
* crash and keep playing without any problem.
|
||||||
|
*/
|
||||||
|
add_task(async function setupTestingPref() {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [
|
||||||
|
["media.wmf.media-engine.enabled", true],
|
||||||
|
["media.wmf.media-engine.channel-decoder.enabled", true],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const VIDEO_PAGE = GetTestWebBasedURL("file_video.html");
|
||||||
|
|
||||||
|
add_task(async function testPlaybackRecoveryFromCrash() {
|
||||||
|
info(`Create a tab and load test page`);
|
||||||
|
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||||
|
window.gBrowser,
|
||||||
|
"about:blank"
|
||||||
|
);
|
||||||
|
BrowserTestUtils.loadURI(tab.linkedBrowser, VIDEO_PAGE);
|
||||||
|
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||||
|
|
||||||
|
await playVideo(tab);
|
||||||
|
|
||||||
|
info("Ensure video is running via the media engine framework");
|
||||||
|
await assertRunningProcessAndDecoderName(tab, {
|
||||||
|
expectedProcess: "Utility MF Media Engine CDM",
|
||||||
|
expectedDecoder: "media engine video stream",
|
||||||
|
});
|
||||||
|
|
||||||
|
const pidBeforeCrash = await getMFCDMProcessId();
|
||||||
|
await crashUtilityProcess(pidBeforeCrash);
|
||||||
|
|
||||||
|
info("The CDM process should be recreated which makes media keep playing");
|
||||||
|
await assertRunningProcessAndDecoderName(tab, {
|
||||||
|
expectedProcess: "Utility MF Media Engine CDM",
|
||||||
|
expectedDecoder: "media engine video stream",
|
||||||
|
});
|
||||||
|
|
||||||
|
const pidAfterCrash = await getMFCDMProcessId();
|
||||||
|
isnot(
|
||||||
|
pidBeforeCrash,
|
||||||
|
pidAfterCrash,
|
||||||
|
`new process ${pidAfterCrash} is not previous crashed one ${pidBeforeCrash}`
|
||||||
|
);
|
||||||
|
|
||||||
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>video</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<video id="v" src="gizmo.mp4" controls loop></video>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,163 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a web-based URL for a given file based on the testing directory.
|
||||||
|
* @param {String} fileName
|
||||||
|
* file that caller wants its web-based url
|
||||||
|
* @param {Boolean} cors [optional]
|
||||||
|
* if set, then return a url with different origin
|
||||||
|
*/
|
||||||
|
function GetTestWebBasedURL(fileName) {
|
||||||
|
const origin = "https://example.com";
|
||||||
|
return (
|
||||||
|
getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
|
||||||
|
fileName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current process Id for the Media Foundation CDM process.
|
||||||
|
*/
|
||||||
|
async function getMFCDMProcessId() {
|
||||||
|
const process = (await ChromeUtils.requestProcInfo()).children.find(
|
||||||
|
p =>
|
||||||
|
p.type === "utility" &&
|
||||||
|
p.utilityActors.find(a => a.actorName === "mfMediaEngineCDM")
|
||||||
|
);
|
||||||
|
return process.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the utility process with given process id crash.
|
||||||
|
* @param {int} pid
|
||||||
|
* the process id for the process which is going to crash
|
||||||
|
*/
|
||||||
|
async function crashUtilityProcess(utilityPid) {
|
||||||
|
info(`Crashing process ${utilityPid}`);
|
||||||
|
SimpleTest.expectChildProcessCrash();
|
||||||
|
|
||||||
|
const crashMan = Services.crashmanager;
|
||||||
|
const utilityProcessGone = TestUtils.topicObserved(
|
||||||
|
"ipc:utility-shutdown",
|
||||||
|
(subject, data) => {
|
||||||
|
info(`ipc:utility-shutdown: data=${data} subject=${subject}`);
|
||||||
|
return parseInt(data, 10) === utilityPid;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Prune any previous crashes");
|
||||||
|
const future = new Date(Date.now() + 1000 * 60 * 60 * 24);
|
||||||
|
await crashMan.pruneOldCrashes(future);
|
||||||
|
|
||||||
|
info("Crash Utility Process");
|
||||||
|
const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
|
||||||
|
Ci.nsIProcessToolsService
|
||||||
|
);
|
||||||
|
|
||||||
|
info(`Crash Utility Process ${utilityPid}`);
|
||||||
|
ProcessTools.crash(utilityPid);
|
||||||
|
|
||||||
|
info(`Waiting for utility process ${utilityPid} to go away.`);
|
||||||
|
let [subject, data] = await utilityProcessGone;
|
||||||
|
ok(
|
||||||
|
parseInt(data, 10) === utilityPid,
|
||||||
|
`Should match the crashed PID ${utilityPid} with ${data}`
|
||||||
|
);
|
||||||
|
ok(
|
||||||
|
subject instanceof Ci.nsIPropertyBag2,
|
||||||
|
"Subject needs to be a nsIPropertyBag2 to clean up properly"
|
||||||
|
);
|
||||||
|
|
||||||
|
const dumpID = subject.getPropertyAsAString("dumpID");
|
||||||
|
ok(dumpID, "There should be a dumpID");
|
||||||
|
|
||||||
|
await crashMan.ensureCrashIsPresent(dumpID);
|
||||||
|
await crashMan.getCrashes().then(crashes => {
|
||||||
|
is(crashes.length, 1, "There should be only one record");
|
||||||
|
const crash = crashes[0];
|
||||||
|
ok(
|
||||||
|
crash.isOfType(
|
||||||
|
crashMan.processTypes[Ci.nsIXULRuntime.PROCESS_TYPE_UTILITY],
|
||||||
|
crashMan.CRASH_TYPE_CRASH
|
||||||
|
),
|
||||||
|
"Record should be a utility process crash"
|
||||||
|
);
|
||||||
|
ok(crash.id === dumpID, "Record should have an ID");
|
||||||
|
});
|
||||||
|
|
||||||
|
let minidumpDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||||
|
minidumpDirectory.append("minidumps");
|
||||||
|
|
||||||
|
let dumpfile = minidumpDirectory.clone();
|
||||||
|
dumpfile.append(dumpID + ".dmp");
|
||||||
|
if (dumpfile.exists()) {
|
||||||
|
info(`Removal of ${dumpfile.path}`);
|
||||||
|
dumpfile.remove(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let extrafile = minidumpDirectory.clone();
|
||||||
|
extrafile.append(dumpID + ".extra");
|
||||||
|
info(`Removal of ${extrafile.path}`);
|
||||||
|
if (extrafile.exists()) {
|
||||||
|
extrafile.remove(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make video in the tab play.
|
||||||
|
* @param {object} tab
|
||||||
|
* the tab contains at least one video element
|
||||||
|
*/
|
||||||
|
async function playVideo(tab) {
|
||||||
|
return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
|
||||||
|
const video = content.document.querySelector("video");
|
||||||
|
ok(
|
||||||
|
await video.play().then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
),
|
||||||
|
"video started playing"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the video playback is performed in the right process and right decoder.
|
||||||
|
* @param {object} tab
|
||||||
|
* the tab which has a playing video
|
||||||
|
* @param {string} expectedProcess
|
||||||
|
* the expected process name
|
||||||
|
* @param {string} expectedDecoder
|
||||||
|
* the expected decoder name
|
||||||
|
*/
|
||||||
|
async function assertRunningProcessAndDecoderName(
|
||||||
|
tab,
|
||||||
|
{ expectedProcess, expectedDecoder } = {}
|
||||||
|
) {
|
||||||
|
return SpecialPowers.spawn(
|
||||||
|
tab.linkedBrowser,
|
||||||
|
[expectedProcess, expectedDecoder],
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
async (expectedProcess, expectedDecoder) => {
|
||||||
|
const video = content.document.querySelector("video");
|
||||||
|
ok(!video.paused, "checking a playing video");
|
||||||
|
|
||||||
|
const debugInfo = await SpecialPowers.wrap(video).mozRequestDebugInfo();
|
||||||
|
const videoDecoderName = debugInfo.decoder.reader.videoDecoderName;
|
||||||
|
|
||||||
|
const isExpectedDecoder =
|
||||||
|
videoDecoderName.indexOf(`${expectedDecoder}`) == 0;
|
||||||
|
ok(
|
||||||
|
isExpectedDecoder,
|
||||||
|
`Playback running by decoder '${videoDecoderName}', expected '${expectedDecoder}'`
|
||||||
|
);
|
||||||
|
|
||||||
|
const isExpectedProcess =
|
||||||
|
videoDecoderName.indexOf(`(${expectedProcess} remote)`) > 0;
|
||||||
|
ok(
|
||||||
|
isExpectedProcess,
|
||||||
|
`Playback running in process '${videoDecoderName}', expected '${expectedProcess}'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче