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

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

mercurial