Source: lib/media/region_timeline.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.media.RegionTimeline');
  7. goog.require('shaka.util.FakeEvent');
  8. goog.require('shaka.util.FakeEventTarget');
  9. goog.require('shaka.util.IReleasable');
  10. goog.require('shaka.util.Timer');
  11. /**
  12. * The region timeline is a set of unique timeline region info entries. When
  13. * a new entry is added, the 'regionadd' event will be fired. When an entry is
  14. * deleted, the 'regionremove' event will be fired.
  15. *
  16. * @implements {shaka.util.IReleasable}
  17. * @template T
  18. * @final
  19. */
  20. shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
  21. /**
  22. * @param {!function():{start: number, end: number}} getSeekRange
  23. */
  24. constructor(getSeekRange) {
  25. super();
  26. /** @private {!Set<T>} */
  27. this.regions_ = new Set();
  28. /** @private {!function():{start: number, end: number}} */
  29. this.getSeekRange_ = getSeekRange;
  30. /**
  31. * Make sure all of the regions we're tracking are within the
  32. * seek range or further in the future. We don't want to store
  33. * regions that fall before the start of the seek range.
  34. *
  35. * @private {shaka.util.Timer}
  36. */
  37. this.filterTimer_ = new shaka.util.Timer(() => {
  38. this.filterBySeekRange_();
  39. }).tickEvery(
  40. /* seconds= */ shaka.media.RegionTimeline.REGION_FILTER_INTERVAL);
  41. }
  42. /** @override */
  43. release() {
  44. this.regions_.clear();
  45. this.filterTimer_.stop();
  46. super.release();
  47. }
  48. /**
  49. * @param {T} region
  50. */
  51. addRegion(region) {
  52. const similarRegion = this.findSimilarRegion_(region);
  53. // Make sure we don't add duplicate regions. We keep track of this here
  54. // instead of making the parser track it.
  55. if (similarRegion == null) {
  56. this.regions_.add(region);
  57. const event = new shaka.util.FakeEvent('regionadd', new Map([
  58. ['region', region],
  59. ]));
  60. this.dispatchEvent(event);
  61. }
  62. }
  63. /**
  64. * @private
  65. */
  66. filterBySeekRange_() {
  67. const seekRange = this.getSeekRange_();
  68. for (const region of this.regions_) {
  69. // Only consider the seek range start here.
  70. // Future regions might become relevant eventually,
  71. // but regions that are in the past and can't ever be
  72. // seeked to will never come up again, and there's no
  73. // reason to store or process them.
  74. if (region.endTime < seekRange.start) {
  75. this.regions_.delete(region);
  76. const event = new shaka.util.FakeEvent('regionremove', new Map([
  77. ['region', region],
  78. ]));
  79. this.dispatchEvent(event);
  80. }
  81. }
  82. }
  83. /**
  84. * Find a region in the timeline that has the same scheme id uri, event id,
  85. * start time and end time. If these four parameters match, we assume it
  86. * to be the same region. If no similar region can be found, |null| will be
  87. * returned.
  88. *
  89. * @param {T} region
  90. * @return {?T}
  91. * @private
  92. */
  93. findSimilarRegion_(region) {
  94. const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.1;
  95. for (const existing of this.regions_) {
  96. // The same scheme ID and time range means that it is similar-enough to
  97. // be the same region.
  98. const isSimilar = existing.schemeIdUri == region.schemeIdUri &&
  99. existing.id == region.id &&
  100. isDiffNegligible(existing.startTime, region.startTime) &&
  101. isDiffNegligible(existing.endTime, region.endTime);
  102. if (isSimilar) {
  103. return existing;
  104. }
  105. }
  106. return null;
  107. }
  108. /**
  109. * Get an iterable for all the regions in the timeline. This will allow
  110. * others to see what regions are in the timeline while not being able to
  111. * change the collection.
  112. *
  113. * @return {!Iterable<T>}
  114. */
  115. regions() {
  116. return this.regions_;
  117. }
  118. };
  119. /** @const {number} */
  120. shaka.media.RegionTimeline.REGION_FILTER_INTERVAL = 2; // in seconds