Source: lib/media/region_timeline.js

  1. /**
  2. * @license
  3. * Copyright 2016 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. goog.provide('shaka.media.RegionTimeline');
  18. goog.require('shaka.util.IReleasable');
  19. goog.require('shaka.util.Timer');
  20. /**
  21. * The region timeline is a set of unique timeline region info entries. When
  22. * a new entry is added, the |onAddRegion| callback will be called.
  23. *
  24. * @implements {shaka.util.IReleasable}
  25. * @final
  26. */
  27. shaka.media.RegionTimeline = class {
  28. /** Constructor */
  29. constructor(getSeekRange) {
  30. /** @private {function(shaka.extern.TimelineRegionInfo)} */
  31. this.onAddRegion_ = (region) => {};
  32. /** @private {!Set.<shaka.extern.TimelineRegionInfo>} */
  33. this.regions_ = new Set();
  34. /** @private {function():{start: number, end: number}} */
  35. this.getSeekRange_ = getSeekRange;
  36. /**
  37. * Make sure all of the regions we're tracking are within the
  38. * seek range or further in the future. We don't want to store
  39. * regions that fall before the start of the seek range.
  40. *
  41. * @private {shaka.util.Timer}
  42. */
  43. this.filterTimer_ = new shaka.util.Timer(() => {
  44. this.filterBySeekRange_();
  45. }).tickEvery(
  46. /* seconds= */ shaka.media.RegionTimeline.REGION_FILTER_INTERVAL);
  47. }
  48. /** @override */
  49. release() {
  50. // Prevent us from holding onto any external references via the callback.
  51. this.onAddRegion_ = (region) => {};
  52. this.regions_.clear();
  53. this.filterTimer_.stop();
  54. }
  55. /**
  56. * Set the callbacks for events. This will override any previous calls to
  57. * |setListeners|.
  58. *
  59. * @param {function(shaka.extern.TimelineRegionInfo)} onAddRegion
  60. * Set the callback for when we add a new region. This callback will only
  61. * be called when a region is unique (we reject duplicate regions).
  62. */
  63. setListeners(onAddRegion) {
  64. this.onAddRegion_ = onAddRegion;
  65. }
  66. /**
  67. * @param {shaka.extern.TimelineRegionInfo} region
  68. */
  69. addRegion(region) {
  70. const similarRegion = this.findSimilarRegion_(region);
  71. // Make sure we don't add duplicate regions. We keep track of this here
  72. // instead of making the parser track it.
  73. if (similarRegion == null) {
  74. this.regions_.add(region);
  75. this.onAddRegion_(region);
  76. }
  77. }
  78. /**
  79. * @private
  80. */
  81. filterBySeekRange_() {
  82. const seekRange = this.getSeekRange_();
  83. for (const region of this.regions_) {
  84. // Only consider the seek range start here.
  85. // Future regions might become relevant eventually,
  86. // but regions that are in the past and can't ever be
  87. // seeked to will never come up again, and there's no
  88. // reson to store or process them.
  89. if (region.endTime < seekRange.start) {
  90. this.regions_.delete(region);
  91. }
  92. }
  93. }
  94. /**
  95. * Find a region in the timeline that has the same scheme id uri, event id,
  96. * start time and end time. If these four parameters match, we assume it
  97. * to be the same region. If no similar region can be found, |null| will be
  98. * returned.
  99. *
  100. * @param {shaka.extern.TimelineRegionInfo} region
  101. * @return {?shaka.extern.TimelineRegionInfo}
  102. * @private
  103. */
  104. findSimilarRegion_(region) {
  105. for (const existing of this.regions_) {
  106. // The same scheme ID and time range means that it is similar-enough to
  107. // be the same region.
  108. const isSimilar = existing.schemeIdUri == region.schemeIdUri &&
  109. existing.id == region.id &&
  110. existing.startTime == region.startTime &&
  111. existing.endTime == region.endTime;
  112. if (isSimilar) {
  113. return existing;
  114. }
  115. }
  116. return null;
  117. }
  118. /**
  119. * Get an iterable for all the regions in the timeline. This will allow
  120. * others to see what regions are in the timeline while not being able to
  121. * change the collection.
  122. *
  123. * @return {!Iterable.<shaka.extern.TimelineRegionInfo>}
  124. */
  125. regions() {
  126. return this.regions_;
  127. }
  128. };
  129. /** @const {number} */
  130. shaka.media.RegionTimeline.REGION_FILTER_INTERVAL = 2; // in seconds