Skip to content

Commit

Permalink
Add several features about playback rate.
Browse files Browse the repository at this point in the history
* Move trick play logic from Player to Playhead.
* Add a method on Player to get the playback rate.
* Use native playback rate when possible.
* Support changes to video.playbackRate while trick play.
* Support setting video.playbackRate to negative values.

Closes #344

Change-Id: I984cc735a27d700cee84984ed295a7e8118b808f
  • Loading branch information
TheModMaker committed Apr 22, 2016
1 parent f9552a5 commit 97b2980
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 35 deletions.
83 changes: 67 additions & 16 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ shaka.media.Playhead = function(
this.buffering_ = false;

/** @private {number} */
this.lastPlaybackRate_ = 0;
this.playbackRate_ = 1;

/** @private {?number} */
this.trickPlayIntervalId_ = null;

// Check if the video has already loaded some metadata.
if (video.readyState > 0) {
Expand All @@ -89,6 +92,8 @@ shaka.media.Playhead = function(
this.eventManager_.listen(
video, 'loadedmetadata', this.onLoadedMetadata_.bind(this));
}

this.eventManager_.listen(video, 'ratechange', this.onRateChange_.bind(this));
};


Expand All @@ -97,6 +102,11 @@ shaka.media.Playhead.prototype.destroy = function() {
var p = this.eventManager_.destroy();
this.eventManager_ = null;

if (this.trickPlayIntervalId_ != null) {
window.clearInterval(this.trickPlayIntervalId_);
this.trickPlayIntervalId_ = null;
}

this.video_ = null;
this.timeline_ = null;
this.onBuffering_ = null;
Expand Down Expand Up @@ -164,21 +174,62 @@ shaka.media.Playhead.prototype.getStartTime_ = function() {
* continue.
*/
shaka.media.Playhead.prototype.setBuffering = function(buffering) {
if (buffering && !this.buffering_) {
this.lastPlaybackRate_ = this.video_.playbackRate;
this.video_.playbackRate = 0;
this.buffering_ = true;
this.onBuffering_(true);
} else if (!buffering && this.buffering_) {
if (this.video_.playbackRate == 0) {
// The app hasn't set a new playback rate, so restore the old one.
this.video_.playbackRate = this.lastPlaybackRate_;
} else {
// There's nothing we could have done to stop the app from setting a new
// rate, so we don't need to do anything here.
}
this.buffering_ = false;
this.onBuffering_(false);
if (buffering != this.buffering_) {
this.buffering_ = buffering;
this.setPlaybackRate(this.playbackRate_);
this.onBuffering_(buffering);
}
};


/**
* Gets the current effective playback rate. This may be negative even if the
* browser does not directly support rewinding.
* @return {number}
*/
shaka.media.Playhead.prototype.getPlaybackRate = function() {
return this.playbackRate_;
};


/**
* Sets the playback rate.
* @param {number} rate
*/
shaka.media.Playhead.prototype.setPlaybackRate = function(rate) {
if (this.trickPlayIntervalId_ != null) {
window.clearInterval(this.trickPlayIntervalId_);
this.trickPlayIntervalId_ = null;
}

this.playbackRate_ = rate;
// All major browsers support playback rates above zero. Only need fake
// trick play for negative rates.
this.video_.playbackRate = (this.buffering_ || rate < 0) ? 0 : rate;

if (!this.buffering_ && rate < 0) {
// Defer creating the timer until we stop buffering. This function will be
// called again from setBuffering().
this.trickPlayIntervalId_ = window.setInterval(function() {
this.video_.currentTime += rate / 4;
}.bind(this), 250);
}
};


/**
* Handles a 'ratechange' event.
*
* @private
*/
shaka.media.Playhead.prototype.onRateChange_ = function() {
// NOTE: This will not allow explicitly setting the playback rate to 0 while
// the playback rate is negative. Pause will still work.
var expectedRate =
this.buffering_ || this.playbackRate_ < 0 ? 0 : this.playbackRate_;
if (this.video_.playbackRate != expectedRate) {
shaka.log.debug('Video playback rate changed to', this.video_.playbackRate);
this.setPlaybackRate(this.video_.playbackRate);
}
};

Expand Down
33 changes: 16 additions & 17 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@ shaka.Player = function(video) {
/** @private {?shakaExtern.PlayerConfiguration} */
this.config_ = this.defaultConfig_();

/** @private {?number} */
this.trickPlayIntervalId_ = null;

/** @private {!Array.<shakaExtern.StreamChoice>} */
this.switchHistory_ = [];

Expand Down Expand Up @@ -682,6 +679,18 @@ shaka.Player.prototype.unload = function() {
};


/**
* Gets the current effective playback rate. If using trick play, it will
* return the current trick play rate; otherwise, it will return the video
* playback rate.
* @return {number}
* @export
*/
shaka.Player.prototype.getPlaybackRate = function() {
return this.playhead_ ? this.playhead_.getPlaybackRate() : 0;
};


/**
* Skip through the content without playing. Simulated using repeated seeks.
*
Expand All @@ -694,15 +703,8 @@ shaka.Player.prototype.unload = function() {
* @export
*/
shaka.Player.prototype.trickPlay = function(rate) {
this.cancelTrickPlay();
if (rate == 1) {
return;
}

this.video_.playbackRate = 0;
this.trickPlayIntervalId_ = window.setInterval(function() {
this.video_.currentTime += rate / 4;
}.bind(this), 250);
if (this.playhead_)
this.playhead_.setPlaybackRate(rate);
};


Expand All @@ -711,11 +713,8 @@ shaka.Player.prototype.trickPlay = function(rate) {
* @export
*/
shaka.Player.prototype.cancelTrickPlay = function() {
this.video_.playbackRate = 1;
if (this.trickPlayIntervalId_ != null) {
window.clearInterval(this.trickPlayIntervalId_);
}
this.trickPlayIntervalId_ = null;
if (this.playhead_)
this.playhead_.setPlaybackRate(1);
};


Expand Down
10 changes: 8 additions & 2 deletions test/playhead_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ describe('Playhead', function() {
// Callback to Playhead to simulate 'seeking' event from |video|.
var videoOnSeeking;

// Callback to Playhead to simulate 'ratechange' event from |video|.
var videoOnRateChange;

// Callback to us from Playhead when the buffering state changes.
var onBuffering;

Expand All @@ -47,6 +50,8 @@ describe('Playhead', function() {
videoOnLoadedMetadata = f;
} else if (eventName == 'seeking') {
videoOnSeeking = f;
} else if (eventName == 'ratechange') {
videoOnRateChange = f;
} else {
throw new Error('Unexpected event:' + eventName);
}
Expand Down Expand Up @@ -77,7 +82,7 @@ describe('Playhead', function() {

expect(video.addEventListener).toHaveBeenCalledWith(
'loadedmetadata', videoOnLoadedMetadata, false);
expect(video.addEventListener.calls.count()).toBe(1);
expect(video.addEventListener.calls.count()).toBe(2);

expect(playhead.getTime()).toBe(5);
expect(video.currentTime).toBe(0);
Expand All @@ -87,7 +92,7 @@ describe('Playhead', function() {

expect(video.addEventListener).toHaveBeenCalledWith(
'seeking', videoOnSeeking, false);
expect(video.addEventListener.calls.count()).toBe(2);
expect(video.addEventListener.calls.count()).toBe(3);

expect(playhead.getTime()).toBe(5);
expect(video.currentTime).toBe(5);
Expand Down Expand Up @@ -132,6 +137,7 @@ describe('Playhead', function() {

// Set to 2 to ensure Playhead restores the correct rate.
video.playbackRate = 2;
videoOnRateChange();

playhead.setBuffering(false);
expect(onBuffering).not.toHaveBeenCalled();
Expand Down

0 comments on commit 97b2980

Please sign in to comment.