dom/media/tests/mochitest/mediaStreamPlayback.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 const TIMEUPDATE_TIMEOUT_LENGTH = 10000;
michael@0 6 const ENDED_TIMEOUT_LENGTH = 10000;
michael@0 7
michael@0 8 /* Time we wait for the canplaythrough event to fire
michael@0 9 * Note: this needs to be at least 30s because the
michael@0 10 * B2G emulator in VMs is really slow. */
michael@0 11 const CANPLAYTHROUGH_TIMEOUT_LENGTH = 60000;
michael@0 12
michael@0 13 /**
michael@0 14 * This class manages playback of a HTMLMediaElement with a MediaStream.
michael@0 15 * When constructed by a caller, an object instance is created with
michael@0 16 * a media element and a media stream object.
michael@0 17 *
michael@0 18 * @param {HTMLMediaElement} mediaElement the media element for playback
michael@0 19 * @param {MediaStream} mediaStream the media stream used in
michael@0 20 * the mediaElement for playback
michael@0 21 */
michael@0 22 function MediaStreamPlayback(mediaElement, mediaStream) {
michael@0 23 this.mediaElement = mediaElement;
michael@0 24 this.mediaStream = mediaStream;
michael@0 25 }
michael@0 26
michael@0 27 MediaStreamPlayback.prototype = {
michael@0 28
michael@0 29 /**
michael@0 30 * Starts media with a media stream, runs it until a canplaythrough and
michael@0 31 * timeupdate event fires, and stops the media.
michael@0 32 *
michael@0 33 * @param {Boolean} isResume specifies if this media element is being resumed
michael@0 34 * from a previous run
michael@0 35 * @param {Function} onSuccess the success callback if the media playback
michael@0 36 * start and stop cycle completes successfully
michael@0 37 * @param {Function} onError the error callback if the media playback
michael@0 38 * start and stop cycle fails
michael@0 39 */
michael@0 40 playMedia : function MSP_playMedia(isResume, onSuccess, onError) {
michael@0 41 var self = this;
michael@0 42
michael@0 43 this.startMedia(isResume, function() {
michael@0 44 self.stopMediaElement();
michael@0 45 onSuccess();
michael@0 46 }, onError);
michael@0 47 },
michael@0 48
michael@0 49 /**
michael@0 50 * Starts the media with the associated stream.
michael@0 51 *
michael@0 52 * @param {Boolean} isResume specifies if the media element playback
michael@0 53 * is being resumed from a previous run
michael@0 54 * @param {Function} onSuccess the success function call back
michael@0 55 * if media starts correctly
michael@0 56 * @param {Function} onError the error function call back
michael@0 57 * if media fails to start
michael@0 58 */
michael@0 59 startMedia : function MSP_startMedia(isResume, onSuccess, onError) {
michael@0 60 var self = this;
michael@0 61 var canPlayThroughFired = false;
michael@0 62
michael@0 63 // If we're initially running this media, check that the time is zero
michael@0 64 if (!isResume) {
michael@0 65 is(this.mediaStream.currentTime, 0,
michael@0 66 "Before starting the media element, currentTime = 0");
michael@0 67 }
michael@0 68
michael@0 69 /**
michael@0 70 * Callback fired when the canplaythrough event is fired. We only
michael@0 71 * run the logic of this function once, as this event can fire
michael@0 72 * multiple times while a HTMLMediaStream is playing content from
michael@0 73 * a real-time MediaStream.
michael@0 74 */
michael@0 75 var canPlayThroughCallback = function() {
michael@0 76 // Disable the canplaythrough event listener to prevent multiple calls
michael@0 77 canPlayThroughFired = true;
michael@0 78 self.mediaElement.removeEventListener('canplaythrough',
michael@0 79 canPlayThroughCallback, false);
michael@0 80
michael@0 81 is(self.mediaElement.paused, false,
michael@0 82 "Media element should be playing");
michael@0 83 is(self.mediaElement.duration, Number.POSITIVE_INFINITY,
michael@0 84 "Duration should be infinity");
michael@0 85
michael@0 86 // When the media element is playing with a real-time stream, we
michael@0 87 // constantly switch between having data to play vs. queuing up data,
michael@0 88 // so we can only check that the ready state is one of those two values
michael@0 89 ok(self.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA ||
michael@0 90 self.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA,
michael@0 91 "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA");
michael@0 92
michael@0 93 is(self.mediaElement.seekable.length, 0,
michael@0 94 "Seekable length shall be zero");
michael@0 95 is(self.mediaElement.buffered.length, 0,
michael@0 96 "Buffered length shall be zero");
michael@0 97
michael@0 98 is(self.mediaElement.seeking, false,
michael@0 99 "MediaElement is not seekable with MediaStream");
michael@0 100 ok(isNaN(self.mediaElement.startOffsetTime),
michael@0 101 "Start offset time shall not be a number");
michael@0 102 is(self.mediaElement.loop, false, "Loop shall be false");
michael@0 103 is(self.mediaElement.preload, "", "Preload should not exist");
michael@0 104 is(self.mediaElement.src, "", "No src should be defined");
michael@0 105 is(self.mediaElement.currentSrc, "",
michael@0 106 "Current src should still be an empty string");
michael@0 107
michael@0 108 var timeUpdateFired = false;
michael@0 109
michael@0 110 var timeUpdateCallback = function() {
michael@0 111 if (self.mediaStream.currentTime > 0 &&
michael@0 112 self.mediaElement.currentTime > 0) {
michael@0 113 timeUpdateFired = true;
michael@0 114 self.mediaElement.removeEventListener('timeupdate',
michael@0 115 timeUpdateCallback, false);
michael@0 116 onSuccess();
michael@0 117 }
michael@0 118 };
michael@0 119
michael@0 120 // When timeupdate fires, we validate time has passed and move
michael@0 121 // onto the success condition
michael@0 122 self.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
michael@0 123 false);
michael@0 124
michael@0 125 // If timeupdate doesn't fire in enough time, we fail the test
michael@0 126 setTimeout(function() {
michael@0 127 if (!timeUpdateFired) {
michael@0 128 self.mediaElement.removeEventListener('timeupdate',
michael@0 129 timeUpdateCallback, false);
michael@0 130 onError("timeUpdate event never fired");
michael@0 131 }
michael@0 132 }, TIMEUPDATE_TIMEOUT_LENGTH);
michael@0 133 };
michael@0 134
michael@0 135 // Adds a listener intended to be fired when playback is available
michael@0 136 // without further buffering.
michael@0 137 this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
michael@0 138 false);
michael@0 139
michael@0 140 // Hooks up the media stream to the media element and starts playing it
michael@0 141 this.mediaElement.mozSrcObject = this.mediaStream;
michael@0 142 this.mediaElement.play();
michael@0 143
michael@0 144 // If canplaythrough doesn't fire in enough time, we fail the test
michael@0 145 setTimeout(function() {
michael@0 146 if (!canPlayThroughFired) {
michael@0 147 self.mediaElement.removeEventListener('canplaythrough',
michael@0 148 canPlayThroughCallback, false);
michael@0 149 onError("canplaythrough event never fired");
michael@0 150 }
michael@0 151 }, CANPLAYTHROUGH_TIMEOUT_LENGTH);
michael@0 152 },
michael@0 153
michael@0 154 /**
michael@0 155 * Stops the media with the associated stream.
michael@0 156 *
michael@0 157 * Precondition: The media stream and element should both be actively
michael@0 158 * being played.
michael@0 159 */
michael@0 160 stopMediaElement : function MSP_stopMediaElement() {
michael@0 161 this.mediaElement.pause();
michael@0 162 this.mediaElement.mozSrcObject = null;
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166
michael@0 167 /**
michael@0 168 * This class is basically the same as MediaStreamPlayback except
michael@0 169 * ensures that the instance provided startMedia is a MediaStream.
michael@0 170 *
michael@0 171 * @param {HTMLMediaElement} mediaElement the media element for playback
michael@0 172 * @param {LocalMediaStream} mediaStream the media stream used in
michael@0 173 * the mediaElement for playback
michael@0 174 */
michael@0 175 function LocalMediaStreamPlayback(mediaElement, mediaStream) {
michael@0 176 ok(mediaStream instanceof LocalMediaStream,
michael@0 177 "Stream should be a LocalMediaStream");
michael@0 178 MediaStreamPlayback.call(this, mediaElement, mediaStream);
michael@0 179 }
michael@0 180
michael@0 181 LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype, {
michael@0 182
michael@0 183 /**
michael@0 184 * Starts media with a media stream, runs it until a canplaythrough and
michael@0 185 * timeupdate event fires, and calls stop() on the stream.
michael@0 186 *
michael@0 187 * @param {Boolean} isResume specifies if this media element is being resumed
michael@0 188 * from a previous run
michael@0 189 * @param {Function} onSuccess the success callback if the media element
michael@0 190 * successfully fires ended on a stop() call
michael@0 191 * on the stream
michael@0 192 * @param {Function} onError the error callback if the media element fails
michael@0 193 * to fire an ended callback on a stop() call
michael@0 194 * on the stream
michael@0 195 */
michael@0 196 playMediaWithStreamStop : {
michael@0 197 value: function (isResume, onSuccess, onError) {
michael@0 198 var self = this;
michael@0 199
michael@0 200 this.startMedia(isResume, function() {
michael@0 201 self.stopStreamInMediaPlayback(function() {
michael@0 202 self.stopMediaElement();
michael@0 203 onSuccess();
michael@0 204 }, onError);
michael@0 205 }, onError);
michael@0 206 }
michael@0 207 },
michael@0 208
michael@0 209 /**
michael@0 210 * Stops the local media stream while it's currently in playback in
michael@0 211 * a media element.
michael@0 212 *
michael@0 213 * Precondition: The media stream and element should both be actively
michael@0 214 * being played.
michael@0 215 *
michael@0 216 * @param {Function} onSuccess the success callback if the media element
michael@0 217 * fires an ended event from stop() being called
michael@0 218 * @param {Function} onError the error callback if the media element
michael@0 219 * fails to fire an ended event from stop() being
michael@0 220 * called
michael@0 221 */
michael@0 222 stopStreamInMediaPlayback : {
michael@0 223 value: function (onSuccess, onError) {
michael@0 224 var endedFired = false;
michael@0 225 var self = this;
michael@0 226
michael@0 227 /**
michael@0 228 * Callback fired when the ended event fires when stop() is called on the
michael@0 229 * stream.
michael@0 230 */
michael@0 231 var endedCallback = function() {
michael@0 232 endedFired = true;
michael@0 233 self.mediaElement.removeEventListener('ended', endedCallback, false);
michael@0 234 ok(true, "ended event successfully fired");
michael@0 235 onSuccess();
michael@0 236 };
michael@0 237
michael@0 238 this.mediaElement.addEventListener('ended', endedCallback, false);
michael@0 239 this.mediaStream.stop();
michael@0 240
michael@0 241 // If ended doesn't fire in enough time, then we fail the test
michael@0 242 setTimeout(function() {
michael@0 243 if (!endedFired) {
michael@0 244 onError("ended event never fired");
michael@0 245 }
michael@0 246 }, ENDED_TIMEOUT_LENGTH);
michael@0 247 }
michael@0 248 }
michael@0 249 });

mercurial