diff --git a/static/script-tests/tests/devices/mediaplayer/html5commontests.js b/static/script-tests/tests/devices/mediaplayer/html5commontests.js index f7b1f2c6..c9616eeb 100644 --- a/static/script-tests/tests/devices/mediaplayer/html5commontests.js +++ b/static/script-tests/tests/devices/mediaplayer/html5commontests.js @@ -262,6 +262,17 @@ window.commonTests.mediaPlayer.html5.mixinTests = function (testCase, mediaPlaye assert(self._eventCallback.notCalled); }; + var assertEventTypeHasBeenFiredASpecificNumberOfTimes = function (self, eventType, expectedNumberOfCalls) { + var numberOfCalls = 0; + + for( i = 0; i < self._eventCallback.args.length; i++) { + if(eventType === self._eventCallback.args[i][0].type) { + numberOfCalls++; + } + } + assertEquals(expectedNumberOfCalls, numberOfCalls); + }; + var clearEvents = function(self) { self._eventCallback.reset(); }; @@ -1124,6 +1135,7 @@ window.commonTests.mediaPlayer.html5.mixinTests = function (testCase, mediaPlaye clearEvents(self); fireSentinels(self); fireSentinels(self); + fireSentinels(self); assertEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); assertEvent(self, MediaPlayer.EVENT.BUFFERING); @@ -1254,6 +1266,136 @@ window.commonTests.mediaPlayer.html5.mixinTests = function (testCase, mediaPlaye }); }; + var ensureEnterBufferingSentinelIsNotCalledWhenZeroesCannotBeTrusted = function(self, MediaPlayer) { + var i; + for (i = 0; i<3; i++) { + advancePlayTime(self); + fireSentinels(self); + } + + for (i=0; i<2; i++) { + stubCreateElementResults.video.currentTime = 0; + fireSentinels(self); + } + assertNoEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); + assertState(self, MediaPlayer.STATE.PLAYING); + }; + + var ensureEnterBufferingSentinelIsCalledWhenZeroesCanBeTrusted = function(self, MediaPlayer) { + for (var i=0; i<2; i++) { + stubCreateElementResults.video.currentTime = 0; + fireSentinels(self); + } + assertEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); + assertState(self, MediaPlayer.STATE.BUFFERING); + }; + + var testForThreeIntervalsOfNormalPlaybackTwoIntervalsOfZeroesAndOneIntervalOfTimeIncreaseBelowSentinelTolerance = function(self, MediaPlayer) { + var i; + + for (i = 0; i<3; i++) { + advancePlayTime(self); + fireSentinels(self); + } + + for (i=0; i<2; i++) { + stubCreateElementResults.video.currentTime = 0; + fireSentinels(self); + } + + assertNoEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); + + stubCreateElementResults.video.currentTime = 0.01; + fireSentinels(self); + + assertNoEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); + }; + + mixins.testEnterBufferingSentinelDoesNothingWhenDeviceTimeIsReportedAsZeroDuringPlayback = function(queue) { + expectAsserts(2); + var self = this; + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlaying(self, MediaPlayer, 0); + clearEvents(self); + ensureEnterBufferingSentinelIsNotCalledWhenZeroesCannotBeTrusted(self, MediaPlayer); + }); + }; + + mixins.testEnterBufferingSentinelDoesNothingWhenBeginPlaybackIsCalledAndDeviceTimeIsReportedAsZeroForAtLeastTwoIntervals = function (queue) { + expectAsserts(2); + var self = this; + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlayingWithBeginPlayback(self, MediaPlayer, 20); + clearEvents(self); + ensureEnterBufferingSentinelIsNotCalledWhenZeroesCannotBeTrusted(self, MediaPlayer); + }); + }; + + mixins.testEnterBufferingSentinelFiresWhenBeginPlaybackFromZeroIsCalledAndDeviceTimeDoesNotAdvance = function(queue) { + expectAsserts(2); + var self = this; + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlaying(self, MediaPlayer, 0); + clearEvents(self); + ensureEnterBufferingSentinelIsCalledWhenZeroesCanBeTrusted(self, MediaPlayer); + }); + }; + + mixins.testEnterBufferingSentinelFiresWhenBeginPlaybackIsCalledAndDeviceTimeDoesNotAdvance = function(queue) { + expectAsserts(2); + var self = this; + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlayingWithBeginPlayback(self, MediaPlayer, 0); + clearEvents(self); + ensureEnterBufferingSentinelIsCalledWhenZeroesCanBeTrusted(self, MediaPlayer); + }); + }; + + mixins.testEnterBufferingSentinelFiresWhenSeekedToZeroAndDeviceTimeIsReportedAsZeroForAtLeastTwoIntervals = function(queue) { + expectAsserts(1); + var self = this; + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlaying(self, MediaPlayer, 20); + clearEvents(self); + + self._mediaPlayer.playFrom(0); + deviceMockingHooks.finishBuffering(self._mediaPlayer); + fireSentinels(self); + fireSentinels(self); + + assertEvent(self, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); + }); + }; + + mixins.testEnterBufferingSentinelOnlyFiresOnSecondAttemptWhenDeviceReportsTimeAsNotChangingWithinTolerance = function(queue) { + expectAsserts(3); + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlaying(this, MediaPlayer, 0); + clearEvents(this); + + testForThreeIntervalsOfNormalPlaybackTwoIntervalsOfZeroesAndOneIntervalOfTimeIncreaseBelowSentinelTolerance(this, MediaPlayer); + + stubCreateElementResults.video.currentTime = 0.01; + fireSentinels(this); + + stubCreateElementResults.video.currentTime = 0.01; + fireSentinels(this); + + assertEventTypeHasBeenFiredASpecificNumberOfTimes(this, MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING, 1); + }); + }; + + mixins.testEnterBufferingSentinelDoesNotFireOnTwoNonConsecutiveOccurrencesOfDeviceReportingTimeAsNotChangingWithinTolerance = function(queue) { + expectAsserts(4); + runMediaPlayerTest(this, queue, function (MediaPlayer) { + getToPlaying(this, MediaPlayer, 0); + clearEvents(this); + + testForThreeIntervalsOfNormalPlaybackTwoIntervalsOfZeroesAndOneIntervalOfTimeIncreaseBelowSentinelTolerance(this, MediaPlayer); + testForThreeIntervalsOfNormalPlaybackTwoIntervalsOfZeroesAndOneIntervalOfTimeIncreaseBelowSentinelTolerance(this, MediaPlayer); + }); + }; + mixins.testExitBufferingSentinelCausesTransitionToPlayingWhenPlaybackStarts = function(queue) { expectAsserts(3); var self = this; @@ -1880,6 +2022,21 @@ window.commonTests.mediaPlayer.html5.mixinTests = function (testCase, mediaPlaye }); }; + mixins.testPauseSentinelDoesNotFireWhenDeviceTimeAdvancesByLessThanSentinelTolerance = function(queue) { + expectAsserts(1); + var self = this; + runMediaPlayerTest(this, queue, function(MediaPlayer) { + getToPlaying(self, MediaPlayer, 20); + clearEvents(self); + + self._mediaPlayer.pause(); + stubCreateElementResults.video.currentTime += 0.01; + fireSentinels(self); + + assertNoEvent(self, MediaPlayer.EVENT.SENTINEL_PAUSE); + }); + }; + mixins.testPlayFromNearCurrentTimeWillNotCauseFinishBufferingToPerformSeekLater = function(queue) { expectAsserts(1); runMediaPlayerTest(this, queue, function (MediaPlayer) { diff --git a/static/script/devices/mediaplayer/html5.js b/static/script/devices/mediaplayer/html5.js index 0fbc4031..8a610ac5 100644 --- a/static/script/devices/mediaplayer/html5.js +++ b/static/script/devices/mediaplayer/html5.js @@ -49,6 +49,7 @@ require.def( this._super(); this._setSentinelLimits(); this._state = MediaPlayer.STATE.EMPTY; + }, /** @@ -56,6 +57,7 @@ require.def( */ setSource: function(mediaType, url, mimeType) { if (this.getState() === MediaPlayer.STATE.EMPTY) { + this._trustZeroes = false; this._type = mediaType; this._source = url; this._mimeType = mimeType; @@ -119,6 +121,7 @@ require.def( switch (this.getState()) { case MediaPlayer.STATE.PAUSED: case MediaPlayer.STATE.COMPLETE: + this._trustZeroes = true; this._toBuffering(); this._playFromIfReady(); break; @@ -128,6 +131,7 @@ require.def( break; case MediaPlayer.STATE.PLAYING: + this._trustZeroes = true; this._toBuffering(); this._targetSeekTime = this._getClampedTimeForPlayFrom(seconds); if (this._isNearToCurrentTime(this._targetSeekTime)) { @@ -152,6 +156,7 @@ require.def( this._sentinelSeekTime = undefined; switch (this.getState()) { case MediaPlayer.STATE.STOPPED: + this._trustZeroes = true; this._toBuffering(); this._mediaElement.play(); break; @@ -172,6 +177,7 @@ require.def( switch (this.getState()) { case MediaPlayer.STATE.STOPPED: + this._trustZeroes = true; this._toBuffering(); this._playFromIfReady(); break; @@ -411,7 +417,6 @@ require.def( } else if (this._postBufferingState === MediaPlayer.STATE.PAUSED) { this._toPaused(); - } else { this._toPlaying(); } @@ -577,12 +582,39 @@ require.def( }, _enterBufferingSentinel: function() { - var notFirstSentinelActivationSinceStateChange = this._sentinelIntervalNumber > 1; - if(!this._hasSentinelTimeChanged && !this._nearEndOfMedia && notFirstSentinelActivationSinceStateChange) { + var sentinelShouldFire = !this._hasSentinelTimeChangedWithinTolerance && !this._nearEndOfMedia ; + + if (this.getCurrentTime() === 0) { + sentinelShouldFire = this._trustZeroes && sentinelShouldFire; + } + + if (this._enterBufferingSentinelAttemptCount === undefined) { + this._enterBufferingSentinelAttemptCount = 0; + } + + if(sentinelShouldFire) { + this._enterBufferingSentinelAttemptCount++; + } else { + this._enterBufferingSentinelAttemptCount = 0; + } + + if (this._enterBufferingSentinelAttemptCount === 1) { + sentinelShouldFire = false; + } + + if(sentinelShouldFire) { this._emitEvent(MediaPlayer.EVENT.SENTINEL_ENTER_BUFFERING); this._toBuffering(); + /* Resetting the sentinel attempt count to zero means that the sentinel will only fire once + even if multiple iterations result in the same conditions. + This should not be needed as the second iteration, when the enter buffering sentinel is fired + will cause the media player to go into the buffering state. The enter buffering sentinel is not fired + when in buffering state + */ + this._enterBufferingSentinelAttemptCount = 0; return true; } + return false; }, @@ -597,7 +629,7 @@ require.def( return fireExitBufferingSentinel(this); } - if (this._hasSentinelTimeChanged) { + if (this._hasSentinelTimeChangedWithinTolerance) { return fireExitBufferingSentinel(this); } return false; @@ -627,7 +659,7 @@ require.def( _shouldBePausedSentinel: function() { var sentinelActionTaken = false; - if (this._hasSentinelTimeChanged) { + if (this._hasSentinelTimeChangedWithinTolerance) { var mediaElement = this._mediaElement; sentinelActionTaken = this._nextSentinelAttempt(this._sentinelLimits.pause, function() { mediaElement.pause(); @@ -658,7 +690,7 @@ require.def( }, _endOfMediaSentinel: function() { - if (!this._hasSentinelTimeChanged && this._nearEndOfMedia) { + if (!this._hasSentinelTimeChangedWithinTolerance && this._nearEndOfMedia) { this._emitEvent(MediaPlayer.EVENT.SENTINEL_COMPLETE); this._onEndOfMedia(); return true; @@ -678,16 +710,25 @@ require.def( this._sentinelInterval = setInterval(function() { self._sentinelIntervalNumber += 1; var newTime = self.getCurrentTime(); - self._hasSentinelTimeChanged = (Math.abs(newTime - self._lastSentinelTime) > 0.2); + + self._hasSentinelTimeChangedWithinTolerance = (Math.abs(newTime - self._lastSentinelTime) > 0.2); self._nearEndOfMedia = (self.getDuration() - (newTime || self._lastSentinelTime)) <= 1; self._lastSentinelTime = newTime; + for (var i = 0; i < sentinels.length; i++) { var sentinelActivated = sentinels[i].call(self); + + if (self.getCurrentTime() > 0) { + self._trustZeroes = false; + } + if(sentinelActivated) { break; } } }, 1100); + + }, _isReadyToPlayFrom: function() {