Source: lib/media/buffering_observer.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.media.BufferingObserver');
  7. /**
  8. * The buffering observer watches how much content has been buffered and raises
  9. * events when the state changes (enough => not enough or vice versa).
  10. *
  11. * @final
  12. */
  13. shaka.media.BufferingObserver = class {
  14. /**
  15. * @param {number} thresholdWhenStarving
  16. * @param {number} thresholdWhenSatisfied
  17. */
  18. constructor(thresholdWhenStarving, thresholdWhenSatisfied) {
  19. const State = shaka.media.BufferingObserver.State;
  20. /** @private {shaka.media.BufferingObserver.State} */
  21. this.previousState_ = State.SATISFIED;
  22. /** @private {!Map<shaka.media.BufferingObserver.State, number>} */
  23. this.thresholds_ = new Map()
  24. .set(State.SATISFIED, thresholdWhenSatisfied)
  25. .set(State.STARVING, thresholdWhenStarving);
  26. /** @private {number} */
  27. this.lastRebufferTime_ = 0;
  28. }
  29. /**
  30. * @param {number} thresholdWhenStarving
  31. * @param {number} thresholdWhenSatisfied
  32. */
  33. setThresholds(thresholdWhenStarving, thresholdWhenSatisfied) {
  34. const State = shaka.media.BufferingObserver.State;
  35. this.thresholds_
  36. .set(State.SATISFIED, thresholdWhenSatisfied)
  37. .set(State.STARVING, thresholdWhenStarving);
  38. }
  39. /**
  40. * Update the observer by telling it how much content has been buffered (in
  41. * seconds) and if we are buffered to the end of the presentation. If the
  42. * controller believes the state has changed, it will return |true|.
  43. *
  44. * @param {number} bufferLead
  45. * @param {boolean} bufferedToEnd
  46. * @return {boolean}
  47. */
  48. update(bufferLead, bufferedToEnd) {
  49. const State = shaka.media.BufferingObserver.State;
  50. /**
  51. * Our threshold for how much we need before we declare ourselves as
  52. * starving is based on whether or not we were just starving. If we
  53. * were just starving, we are more likely to starve again, so we require
  54. * more content to be buffered than if we were not just starving.
  55. *
  56. * @type {number}
  57. */
  58. const threshold = this.thresholds_.get(this.previousState_);
  59. const oldState = this.previousState_;
  60. const newState =
  61. (bufferedToEnd || (bufferLead >= threshold && bufferLead > 0)) ?
  62. (State.SATISFIED) :
  63. (State.STARVING);
  64. // Save the new state now so that calls to |getState| from any callbacks
  65. // will be accurate.
  66. this.previousState_ = newState;
  67. // Return |true| only when the state has changed.
  68. const stateChanged = oldState != newState;
  69. if (stateChanged && newState === State.SATISFIED) {
  70. this.lastRebufferTime_ = Date.now();
  71. }
  72. return stateChanged;
  73. }
  74. /**
  75. * Set which state that the observer should think playback was in.
  76. *
  77. * @param {shaka.media.BufferingObserver.State} state
  78. */
  79. setState(state) {
  80. this.previousState_ = state;
  81. }
  82. /**
  83. * Get the state that the observer last thought playback was in.
  84. *
  85. * @return {shaka.media.BufferingObserver.State}
  86. */
  87. getState() {
  88. return this.previousState_;
  89. }
  90. /**
  91. * Return the last time that the state went from |STARVING| to |SATISFIED|.
  92. * @return {number}
  93. */
  94. getLastRebufferTime() {
  95. return this.lastRebufferTime_;
  96. }
  97. /**
  98. * Reset the last rebuffer time to zero.
  99. */
  100. resetLastRebufferTime() {
  101. this.lastRebufferTime_ = 0;
  102. }
  103. };
  104. /**
  105. * Rather than using booleans to communicate what state we are in, we have this
  106. * enum.
  107. *
  108. * @enum {number}
  109. */
  110. shaka.media.BufferingObserver.State = {
  111. STARVING: 0,
  112. SATISFIED: 1,
  113. };