Bug 1193075 - test that streams still play after constraints have been applied. r=pehrsons

--HG--
extra : transplant_source : %E1x%FB%D0%EE%3C%FD%AD%5C%ED%12%29%FB%19%C8%22%DD9%F40
This commit is contained in:
Jan-Ivar Bruaroey 2015-10-28 15:15:22 -04:00
parent 1959f55fd4
commit f17cf50dc6
3 changed files with 64 additions and 72 deletions

View File

@ -390,6 +390,18 @@ function waitUntil(func, time) {
var timeout = (promise, time, msg) =>
Promise.race([promise, wait(time).then(() => Promise.reject(new Error(msg)))]);
/** Use event listener to call passed-in function on fire until it returns true */
var listenUntil = (target, eventName, onFire) => {
return new Promise(resolve => target.addEventListener(eventName,
function callback() {
var result = onFire();
if (result) {
target.removeEventListener(eventName, callback, false);
resolve(result);
}
}, false));
};
/*** Test control flow methods */
/**

View File

@ -2,13 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const TIMEUPDATE_TIMEOUT_LENGTH = 10000;
const ENDED_TIMEOUT_LENGTH = 30000;
/* Time we wait for the canplaythrough event to fire
/* The time we wait depends primarily on the canplaythrough event firing
* Note: this needs to be at least 30s because the
* B2G emulator in VMs is really slow. */
const CANPLAYTHROUGH_TIMEOUT_LENGTH = 60000;
const VERIFYPLAYING_TIMEOUT_LENGTH = 60000;
/**
* This class manages playback of a HTMLMediaElement with a MediaStream.
@ -34,7 +33,8 @@ MediaStreamPlayback.prototype = {
* from a previous run
*/
playMedia : function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.stopMediaElement());
},
@ -45,28 +45,34 @@ MediaStreamPlayback.prototype = {
* is being resumed from a previous run
*/
startMedia : function(isResume) {
var canPlayThroughFired = false;
// If we're playing this media element for the first time,
// check that the time is zero.
// If we're playing media element for the first time, check that time is zero.
if (!isResume) {
is(this.mediaElement.currentTime, 0,
"Before starting the media element, currentTime = 0");
}
this.canPlayThroughFired = listenUntil(this.mediaElement, 'canplaythrough',
() => true);
return new Promise((resolve, reject) => {
/**
* Callback fired when the canplaythrough event is fired. We only
* run the logic of this function once, as this event can fire
* multiple times while a HTMLMediaStream is playing content from
* a real-time MediaStream.
*/
var canPlayThroughCallback = () => {
// Disable the canplaythrough event listener to prevent multiple calls
canPlayThroughFired = true;
this.mediaElement.removeEventListener('canplaythrough',
canPlayThroughCallback, false);
// Hooks up the media stream to the media element and starts playing it
this.mediaElement.srcObject = this.mediaStream;
this.mediaElement.play();
},
/**
* Verifies that media is playing.
*/
verifyPlaying : function() {
var lastStreamTime = this.mediaStream.currentTime;
var lastElementTime = this.mediaElement.currentTime;
var mediaTimeProgressed = listenUntil(this.mediaElement, 'timeupdate',
() => this.mediaStream.currentTime > lastStreamTime &&
this.mediaElement.currentTime > lastElementTime);
return timeout(Promise.all([this.canPlayThroughFired, mediaTimeProgressed]),
VERIFYPLAYING_TIMEOUT_LENGTH, "verifyPlaying timed out")
.then(() => {
is(this.mediaElement.paused, false,
"Media element should be playing");
is(this.mediaElement.duration, Number.POSITIVE_INFINITY,
@ -93,45 +99,7 @@ MediaStreamPlayback.prototype = {
is(this.mediaElement.src, "", "No src should be defined");
is(this.mediaElement.currentSrc, "",
"Current src should still be an empty string");
var timeUpdateCallback = () => {
if (this.mediaStream.currentTime > 0 &&
this.mediaElement.currentTime > 0) {
this.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
resolve();
}
};
// When timeupdate fires, we validate time has passed and move
// onto the success condition
this.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
false);
// If timeupdate doesn't fire in enough time, we fail the test
setTimeout(() => {
this.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
reject(new Error("timeUpdate event never fired"));
}, TIMEUPDATE_TIMEOUT_LENGTH);
};
// Adds a listener intended to be fired when playback is available
// without further buffering.
this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
false);
// Hooks up the media stream to the media element and starts playing it
this.mediaElement.srcObject = this.mediaStream;
this.mediaElement.play();
// If canplaythrough doesn't fire in enough time, we fail the test
setTimeout(() => {
this.mediaElement.removeEventListener('canplaythrough',
canPlayThroughCallback, false);
reject(new Error("canplaythrough event never fired"));
}, CANPLAYTHROUGH_TIMEOUT_LENGTH);
});
});
},
/**
@ -172,7 +140,8 @@ LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype
*/
playMediaWithMediaStreamTracksStop: {
value: function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.stopTracksForStreamInMediaPlayback())
.then(() => this.stopMediaElement());
}
@ -217,7 +186,8 @@ LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype
*/
playMediaWithDeprecatedStreamStop : {
value: function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.deprecatedStopStreamInMediaPlayback())
.then(() => this.stopMediaElement());
}

View File

@ -25,26 +25,36 @@
var testVideo = createMediaElement('video', 'testVideo');
return Promise.resolve()
.then(() => getUserMedia({
video: { mediaSource: "browser", scrollWithPage: true },
fake: false
}))
.then(() => getUserMedia({ video: { mediaSource: "browser",
scrollWithPage: true } }))
.then(stream => {
var playback = new LocalMediaStreamPlayback(testVideo, stream);
return playback.playMediaWithDeprecatedStreamStop(false);
})
.then(() => getUserMedia({
video: { mediaSource: "browser" },
fake: false
video: {
mediaSource: "browser",
viewportOffsetX: 0,
viewportOffsetY: 0,
viewportWidth: 100,
viewportHeight: 100
}
}))
.then(stream => {
var playback = new LocalMediaStreamPlayback(testVideo, stream);
return playback.startMedia(false)
.then(() => testVideo.srcObject.getVideoTracks()[0].applyConstraints({
mediaSource: "browser",
viewportOffsetX: 10,
viewportOffsetY: 50
}))
playback.startMedia(false);
return playback.verifyPlaying()
.then(() => Promise.all([
() => testVideo.srcObject.getVideoTracks()[0].applyConstraints({
mediaSource: "browser",
viewportOffsetX: 10,
viewportOffsetY: 50,
viewportWidth: 90,
viewportHeight: 50
}),
() => listenUntil(testVideo, "resize", () => true)
]))
.then(() => playback.verifyPlaying()) // still playing
.then(() => playback.deprecatedStopStreamInMediaPlayback())
.then(() => playback.stopMediaElement());
});