From bc7b826e41e2cc96dbc2196d1924045489f35a74 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Tue, 1 Sep 2015 10:21:32 -0700 Subject: [PATCH] Support rewind outside of buffered area Closes #165 Change-Id: I684560b332808f9acffcfc09ad8fffcd8c164fba --- lib/player/player.js | 37 +++++++++++++++++++++++++++---------- spec/player_integration.js | 23 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/lib/player/player.js b/lib/player/player.js index 9c472ffc62..3417162bfe 100644 --- a/lib/player/player.js +++ b/lib/player/player.js @@ -98,6 +98,9 @@ shaka.player.Player = function(video) { /** @private {?number} */ this.rewindTimer_ = null; + /** @private {number} */ + this.seekRangeStart_ = 0; + /** @private {?number} */ this.watchdogTimer_ = null; @@ -281,6 +284,8 @@ shaka.player.Player.prototype.load = function(videoSource) { function() { this.videoSource_ = videoSource; this.videoSource_.setRestrictions(this.restrictions_); + this.eventManager_.listen(this.videoSource_, 'seekrangechanged', + this.onSeekRangeChanged_.bind(this)); this.emeManager_ = new shaka.media.EmeManager( this, this.video_, this.videoSource_); return this.emeManager_.initialize(); @@ -416,7 +421,17 @@ shaka.player.Player.prototype.onPause_ = function(event) { if (!isNaN(elapsed)) { this.stats_.logPlayTime(elapsed); } - this.cancelRewindTimer_(); +}; + + +/** + * Handler for seek range events. + * + * @param {!Event} event + * @private + */ +shaka.player.Player.prototype.onSeekRangeChanged_ = function(event) { + this.seekRangeStart_ = event['start']; }; @@ -881,19 +896,21 @@ shaka.player.Player.prototype.cancelWatchdogTimer_ = function() { shaka.player.Player.prototype.onRewindTimer_ = function(startVideoTime, startWallTime, rate) { shaka.asserts.assert(rate < 0); - var rewindOffset = - shaka.player.Player.REWIND_UPDATE_INTERVAL_ * Math.abs(rate); - if (this.video_.buffered.length && - this.video_.buffered.start(0) + rewindOffset < this.video_.currentTime) { - var offset = ((Date.now() - startWallTime) / 1000) * rate; - this.video_.currentTime = startVideoTime + offset; + this.rewindTimer_ = null; + + var offset = ((Date.now() - startWallTime) / 1000) * rate; + var fudge = this.isLive() ? 1 : 0.05; + if (this.video_.currentTime < this.seekRangeStart_ + fudge) { + // Hit the beginning (or near enough), so pause. + this.video_.pause(); + } else { + var goal = Math.max(this.seekRangeStart_, startVideoTime + offset); + this.video_.currentTime = goal; var callback = this.onRewindTimer_.bind( this, startVideoTime, startWallTime, rate); this.rewindTimer_ = window.setTimeout( callback, shaka.player.Player.REWIND_UPDATE_INTERVAL_ * 1000); - } else { - this.video_.pause(); } }; @@ -991,7 +1008,7 @@ shaka.player.Player.BUFFERED_FUDGE_FACTOR_ = 0.05; * @private {number} * @const */ -shaka.player.Player.REWIND_UPDATE_INTERVAL_ = 0.1; +shaka.player.Player.REWIND_UPDATE_INTERVAL_ = 0.25; /** diff --git a/spec/player_integration.js b/spec/player_integration.js index e352d784dd..ff7a01a775 100644 --- a/spec/player_integration.js +++ b/spec/player_integration.js @@ -544,6 +544,29 @@ describe('Player', function() { done(); }); }); + + it('plays in reverse past the buffered area', function(done) { + var timestamp = 10; + // Start in the second segment. + player.load(newSource(plainManifest)).then(function() { + video.currentTime = timestamp; + video.play(); + return waitForMovement(video, eventManager); + }).then(function() { + expect(video.buffered.length).toBe(1); + expect(video.buffered.start(0)).toBeGreaterThan(5); + player.setPlaybackRate(-3.0); + return waitForMovement(video, eventManager); + }).then(function() { + return delay(4.0); + }).then(function() { + expect(video.currentTime).toBeLessThan(0.1); + done(); + }).catch(function(error) { + fail(error); + done(); + }); + }); }); describe('getStats', function() {