UNPKG

shaka-player

Version:
197 lines (172 loc) 5.32 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.media.PlayRateController'); goog.require('goog.asserts'); goog.require('shaka.log'); goog.require('shaka.util.IReleasable'); goog.require('shaka.util.Timer'); /** * The play rate controller controls the playback rate on the media element. * This provides some missing functionality (e.g. negative playback rate). If * the playback rate on the media element can change outside of the controller, * the playback controller will need to be updated to stay in-sync. * * @implements {shaka.util.IReleasable} * @final */ shaka.media.PlayRateController = class { /** * @param {shaka.media.PlayRateController.Harness} harness */ constructor(harness) { /** @private {?shaka.media.PlayRateController.Harness} */ this.harness_ = harness; /** @private {boolean} */ this.isBuffering_ = false; /** @private {number} */ this.rate_ = this.harness_.getRate(); /** @private {number} */ this.pollRate_ = 0.25; /** @private {shaka.util.Timer} */ this.timer_ = new shaka.util.Timer(() => { this.harness_.movePlayhead(this.rate_ * this.pollRate_); }); } /** @override */ release() { this.set(this.getDefaultRate()); if (this.timer_) { this.timer_.stop(); this.timer_ = null; } this.harness_ = null; } /** * Sets the buffering flag, which controls the effective playback rate. * * @param {boolean} isBuffering If true, forces playback rate to 0 internally. */ setBuffering(isBuffering) { this.isBuffering_ = isBuffering; this.apply_(); } /** * Set the playback rate. This rate will only be used as provided when the * player is not buffering. You should never set the rate to 0. * * @param {number} rate */ set(rate) { goog.asserts.assert(rate != 0, 'Should never set rate of 0 explicitly!'); this.rate_ = rate; this.apply_(); } /** * Get the real rate of the playback. This means that if we are using trick * play, this will report the trick play rate. If playback is occurring as * normal, this will report 1. * * @return {number} */ getRealRate() { return this.rate_; } /** * Get the default play rate of the playback. * * @return {number} */ getDefaultRate() { return this.harness_.getDefaultRate(); } /** * Reapply the effects of |this.rate_| and |this.active_| to the media * element. This will only update the rate via the harness if the desired rate * has changed. * * @private */ apply_() { // Always stop the timer. We may not start it again. this.timer_.stop(); /** @type {number} */ const rate = this.calculateCurrentRate_(); shaka.log.v1('Changing effective playback rate to', rate); if (rate >= 0) { try { this.applyRate_(rate); return; } catch (e) { // Fall through to the next clause. // // Fast forward is accomplished through setting video.playbackRate. // If the play rate value is not supported by the browser (too big), // the browsers will throw. // Use this as a cue to fall back to fast forward through repeated // seeking, which is what we do for rewind as well. } } // When moving backwards or forwards in large steps, // set the playback rate to 0 so that we can manually // seek backwards with out fighting the playhead. this.timer_.tickEvery(this.pollRate_); this.applyRate_(0); } /** * Calculate the rate that the controller wants the media element to have * based on the current state of the controller. * * @return {number} * @private */ calculateCurrentRate_() { return this.isBuffering_ ? 0 : this.rate_; } /** * If the new rate is different than the media element's playback rate, this * will change the playback rate. If the rate does not need to change, it will * not be set. This will avoid unnecessary ratechange events. * * @param {number} newRate * @return {boolean} * @private */ applyRate_(newRate) { const oldRate = this.harness_.getRate(); if (oldRate != newRate) { this.harness_.setRate(newRate); } return oldRate != newRate; } }; /** * @typedef {{ * getRate: function():number, * getDefaultRate: function():number, * setRate: function(number), * movePlayhead: function(number) * }} * * @description * A layer of abstraction between the controller and what it is controlling. * In tests this will be implemented with spies. In production this will be * implemented using a media element. * * @property {function():number} getRate * Get the current playback rate being seen by the user. * * @property {function():number} getDefaultRate * Get the default playback rate that the user should see. * * @property {function(number)} setRate * Set the playback rate that the user should see. * * @property {function(number)} movePlayhead * Move the playhead N seconds. If N is positive, the playhead will move * forward abs(N) seconds. If N is negative, the playhead will move backwards * abs(N) seconds. */ shaka.media.PlayRateController.Harness;