Source: lib/media/segment_index.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.media.MetaSegmentIndex');
  7. goog.provide('shaka.media.SegmentIndex');
  8. goog.provide('shaka.media.SegmentIterator');
  9. goog.require('goog.asserts');
  10. goog.require('shaka.log');
  11. goog.require('shaka.media.SegmentReference');
  12. goog.require('shaka.util.IReleasable');
  13. goog.require('shaka.util.Timer');
  14. /**
  15. * SegmentIndex.
  16. *
  17. * @implements {shaka.extern.SegmentIndex}
  18. * @implements {shaka.util.IReleasable}
  19. * @implements {Iterable<!shaka.media.SegmentReference>}
  20. * @export
  21. */
  22. shaka.media.SegmentIndex = class {
  23. /**
  24. * @param {!Array<!shaka.media.SegmentReference>} references The list of
  25. * SegmentReferences, which must be sorted first by their start times
  26. * (ascending) and second by their end times (ascending).
  27. */
  28. constructor(references) {
  29. if (goog.DEBUG) {
  30. shaka.media.SegmentIndex.assertCorrectReferences_(references);
  31. }
  32. /** @protected {!Array<!shaka.media.SegmentReference>} */
  33. this.references = references;
  34. /** @private {shaka.util.Timer} */
  35. this.timer_ = null;
  36. /**
  37. * The number of references that have been removed from the front of the
  38. * array. Used to create stable positions in the find/get APIs.
  39. *
  40. * @protected {number}
  41. */
  42. this.numEvicted_ = 0;
  43. /** @private {boolean} */
  44. this.immutable_ = false;
  45. }
  46. /**
  47. * Get immutability
  48. *
  49. * @return {boolean}
  50. */
  51. getIsImmutable() {
  52. return this.immutable_;
  53. }
  54. /**
  55. * @override
  56. * @export
  57. */
  58. getNumReferences() {
  59. return this.references.length;
  60. }
  61. /**
  62. * @override
  63. * @export
  64. */
  65. getNumEvicted() {
  66. return this.numEvicted_;
  67. }
  68. /**
  69. * @override
  70. * @export
  71. */
  72. release() {
  73. if (this.immutable_) {
  74. return;
  75. }
  76. this.references = [];
  77. if (this.timer_) {
  78. this.timer_.stop();
  79. }
  80. this.timer_ = null;
  81. }
  82. /**
  83. * Marks the index as immutable. Segments cannot be added or removed after
  84. * this point. This doesn't affect the references themselves. This also
  85. * makes the destroy/release methods do nothing.
  86. *
  87. * This is mainly for testing.
  88. *
  89. * @export
  90. */
  91. markImmutable() {
  92. this.immutable_ = true;
  93. }
  94. /**
  95. * Iterates over all top-level segment references in this segment index.
  96. * @param {function(!shaka.media.SegmentReference)} fn
  97. */
  98. forEachTopLevelReference(fn) {
  99. for (const reference of this.references) {
  100. fn(reference);
  101. }
  102. }
  103. /**
  104. * Return the earliest reference, or null if empty.
  105. * @return {shaka.media.SegmentReference}
  106. */
  107. earliestReference() {
  108. return this.references[0] || null;
  109. }
  110. /**
  111. * Drop the first N references.
  112. * Used in early HLS synchronization, and does not count as eviction.
  113. * @param {number} n
  114. */
  115. dropFirstReferences(n) {
  116. this.references.splice(0, n);
  117. }
  118. /**
  119. * @override
  120. * @export
  121. */
  122. find(time) {
  123. // For live streams, searching from the end is faster. For VOD, it balances
  124. // out either way. In both cases, references.length is small enough that
  125. // the difference isn't huge.
  126. const lastReferenceIndex = this.references.length - 1;
  127. for (let i = lastReferenceIndex; i >= 0; --i) {
  128. const r = this.references[i];
  129. const start = r.startTime;
  130. // A rounding error can cause /time/ to equal e.endTime or fall in between
  131. // the references by a fraction of a second. To account for this, we use
  132. // the start of the next segment as /end/, unless this is the last
  133. // reference, in which case we use its end time as /end/.
  134. const end = i < lastReferenceIndex ?
  135. this.references[i + 1].startTime : r.endTime;
  136. // Note that a segment ends immediately before the end time.
  137. if ((time >= start) && (time < end)) {
  138. return i + this.numEvicted_;
  139. }
  140. }
  141. if (this.references.length && time < this.references[0].startTime) {
  142. return this.numEvicted_;
  143. }
  144. return null;
  145. }
  146. /**
  147. * @override
  148. * @export
  149. */
  150. get(position) {
  151. if (this.references.length == 0) {
  152. return null;
  153. }
  154. const index = position - this.numEvicted_;
  155. if (index < 0 || index >= this.references.length) {
  156. return null;
  157. }
  158. return this.references[index];
  159. }
  160. /**
  161. * Offset all segment references by a fixed amount.
  162. *
  163. * @param {number} offset The amount to add to each segment's start and end
  164. * times.
  165. * @export
  166. */
  167. offset(offset) {
  168. if (!this.immutable_) {
  169. for (const ref of this.references) {
  170. ref.offset(offset);
  171. }
  172. }
  173. }
  174. /**
  175. * Merges the given SegmentReferences. Supports extending the original
  176. * references only. Will replace old references with equivalent new ones, and
  177. * keep any unique old ones.
  178. *
  179. * Used, for example, by the DASH and HLS parser, where manifests may not list
  180. * all available references, so we must keep available references in memory to
  181. * fill the availability window.
  182. *
  183. * @param {!Array<!shaka.media.SegmentReference>} references The list of
  184. * SegmentReferences, which must be sorted first by their start times
  185. * (ascending) and second by their end times (ascending).
  186. */
  187. merge(references) {
  188. if (goog.DEBUG) {
  189. shaka.media.SegmentIndex.assertCorrectReferences_(references);
  190. }
  191. if (this.immutable_) {
  192. return;
  193. }
  194. if (!references.length) {
  195. return;
  196. }
  197. // Partial segments are used for live edge, and should be removed when they
  198. // get older. Remove the old SegmentReferences after the first new
  199. // reference's start time.
  200. // Use times rounded to the millisecond for filtering purposes, so that
  201. // tiny rounding errors will not result in duplicate segments in the index.
  202. const firstStartTime = Math.round(references[0].startTime * 1000) / 1000;
  203. this.references = this.references.filter((r) => {
  204. return (Math.round(r.startTime * 1000) / 1000) < firstStartTime;
  205. });
  206. this.references.push(...references);
  207. if (goog.DEBUG) {
  208. shaka.media.SegmentIndex.assertCorrectReferences_(this.references);
  209. }
  210. }
  211. /**
  212. * Merges the given SegmentReferences and evicts the ones that end before the
  213. * given time. Supports extending the original references only.
  214. * Will not replace old references or interleave new ones.
  215. * Used, for example, by the DASH and HLS parser, where manifests may not list
  216. * all available references, so we must keep available references in memory to
  217. * fill the availability window.
  218. *
  219. * @param {!Array<!shaka.media.SegmentReference>} references The list of
  220. * SegmentReferences, which must be sorted first by their start times
  221. * (ascending) and second by their end times (ascending).
  222. * @param {number} windowStart The start of the availability window to filter
  223. * out the references that are no longer available.
  224. * @export
  225. */
  226. mergeAndEvict(references, windowStart) {
  227. // Filter out the references that are no longer available to avoid
  228. // repeatedly evicting them and messing up eviction count.
  229. references = references.filter((r) => {
  230. return r.endTime > windowStart &&
  231. (this.references.length == 0 ||
  232. r.endTime > this.references[0].startTime);
  233. });
  234. const oldFirstRef = this.references[0];
  235. this.merge(references);
  236. const newFirstRef = this.references[0];
  237. if (oldFirstRef) {
  238. // We don't compare the actual ref, since the object could legitimately be
  239. // replaced with an equivalent. Even the URIs could change due to
  240. // load-balancing actions taken by the server. However, if the time
  241. // changes, its not an equivalent reference.
  242. goog.asserts.assert(oldFirstRef.startTime == newFirstRef.startTime,
  243. 'SegmentIndex.merge should not change the first reference time!');
  244. }
  245. this.evict(windowStart);
  246. }
  247. /**
  248. * Removes all SegmentReferences that end before the given time.
  249. *
  250. * @param {number} time The time in seconds.
  251. * @export
  252. */
  253. evict(time) {
  254. if (this.immutable_) {
  255. return;
  256. }
  257. const oldSize = this.references.length;
  258. this.references = this.references.filter((ref) => ref.endTime > time);
  259. const newSize = this.references.length;
  260. const diff = oldSize - newSize;
  261. // Tracking the number of evicted refs will keep their "positions" stable
  262. // for the caller.
  263. this.numEvicted_ += diff;
  264. }
  265. /**
  266. * Drops references that start after windowEnd, or end before windowStart,
  267. * and contracts the last reference so that it ends at windowEnd.
  268. *
  269. * Do not call on the last period of a live presentation (unknown duration).
  270. * It is okay to call on the other periods of a live presentation, where the
  271. * duration is known and another period has been added.
  272. *
  273. * @param {number} windowStart
  274. * @param {?number} windowEnd
  275. * @param {boolean=} isNew Whether this is a new SegmentIndex and we shouldn't
  276. * update the number of evicted elements.
  277. * @export
  278. */
  279. fit(windowStart, windowEnd, isNew = false) {
  280. goog.asserts.assert(windowEnd != null,
  281. 'Content duration must be known for static content!');
  282. goog.asserts.assert(windowEnd != Infinity,
  283. 'Content duration must be finite for static content!');
  284. if (this.immutable_) {
  285. return;
  286. }
  287. // Trim out references we will never use.
  288. while (this.references.length) {
  289. const lastReference = this.references[this.references.length - 1];
  290. if (lastReference.startTime >= windowEnd) {
  291. this.references.pop();
  292. } else {
  293. break;
  294. }
  295. }
  296. while (this.references.length) {
  297. const firstReference = this.references[0];
  298. if (firstReference.endTime <= windowStart) {
  299. this.references.shift();
  300. if (!isNew) {
  301. this.numEvicted_++;
  302. }
  303. } else {
  304. break;
  305. }
  306. }
  307. if (this.references.length == 0) {
  308. return;
  309. }
  310. // Adjust the last SegmentReference.
  311. const lastReference = this.references[this.references.length - 1];
  312. const newReference = new shaka.media.SegmentReference(
  313. lastReference.startTime,
  314. /* endTime= */ windowEnd,
  315. lastReference.getUrisInner,
  316. lastReference.startByte,
  317. lastReference.endByte,
  318. lastReference.initSegmentReference,
  319. lastReference.timestampOffset,
  320. lastReference.appendWindowStart,
  321. lastReference.appendWindowEnd,
  322. lastReference.partialReferences,
  323. lastReference.tilesLayout,
  324. lastReference.tileDuration,
  325. lastReference.syncTime,
  326. lastReference.status,
  327. lastReference.aesKey,
  328. );
  329. newReference.mimeType = lastReference.mimeType;
  330. newReference.codecs = lastReference.codecs;
  331. newReference.discontinuitySequence = lastReference.discontinuitySequence;
  332. this.references[this.references.length - 1] = newReference;
  333. }
  334. /**
  335. * Updates the references every so often. Stops when the references list
  336. * returned by the callback is null.
  337. *
  338. * @param {number} interval The interval in seconds.
  339. * @param {function(): Array<shaka.media.SegmentReference>} updateCallback
  340. * @export
  341. */
  342. updateEvery(interval, updateCallback) {
  343. goog.asserts.assert(!this.timer_, 'SegmentIndex timer already started!');
  344. if (this.immutable_) {
  345. return;
  346. }
  347. if (this.timer_) {
  348. this.timer_.stop();
  349. }
  350. this.timer_ = new shaka.util.Timer(() => {
  351. const references = updateCallback();
  352. if (references) {
  353. this.references.push(...references);
  354. } else {
  355. this.timer_.stop();
  356. this.timer_ = null;
  357. }
  358. });
  359. this.timer_.tickEvery(interval);
  360. }
  361. /** @return {!shaka.media.SegmentIterator} */
  362. [Symbol.iterator]() {
  363. const iter = this.getIteratorForTime(0);
  364. goog.asserts.assert(iter != null, 'Iterator for 0 should never be null!');
  365. return iter;
  366. }
  367. /**
  368. * Returns a new iterator that initially points to the segment that contains
  369. * the given time, or the nearest independent segment before it.
  370. *
  371. * Like the normal iterator, next() must be called first to get to the first
  372. * element. Returns null if we do not find a segment at the
  373. * requested time.
  374. *
  375. * The first segment returned by the iterator _MUST_ be an independent
  376. * segment. Assumes that only partial references can be dependent, based on
  377. * RFC 8216 rev 13, section 8.1: "Each (non-Partial) Media Segment in a Media
  378. * Playlist will contain at least one independent frame."
  379. *
  380. * @param {number} time
  381. * @param {boolean=} allowNonIndependent
  382. * @param {boolean=} reverse
  383. * @return {?shaka.media.SegmentIterator}
  384. * @export
  385. */
  386. getIteratorForTime(time, allowNonIndependent = false, reverse = false) {
  387. let index = this.find(time);
  388. if (index == null) {
  389. return null;
  390. }
  391. const ref = this.get(index);
  392. // Adjust index to point to previous or next index (if reversed), so first
  393. // next() call will traverse in proper direction.
  394. if (!reverse) {
  395. index--;
  396. } else {
  397. index++;
  398. }
  399. let partialSegmentIndex = -1;
  400. if (ref && ref.hasPartialSegments()) {
  401. // Look for a partial SegmentReference.
  402. for (let i = ref.partialReferences.length - 1; i >= 0; --i) {
  403. let r = ref.partialReferences[i];
  404. // Note that a segment ends immediately before the end time.
  405. if ((time >= r.startTime) && (time < r.endTime)) {
  406. if (!allowNonIndependent) {
  407. // Find an independent partial segment by moving backwards.
  408. while (i && (!r.isIndependent())) {
  409. i--;
  410. r = ref.partialReferences[i];
  411. }
  412. if (!r.isIndependent()) {
  413. shaka.log.alwaysError('No independent partial segment found!');
  414. return null;
  415. }
  416. }
  417. // Call to next() should move the partial segment, not the full
  418. // segment.
  419. if (reverse) {
  420. index--;
  421. } else {
  422. index++;
  423. }
  424. partialSegmentIndex = i - 1;
  425. break;
  426. }
  427. }
  428. }
  429. return new shaka.media.SegmentIterator(
  430. this, index, partialSegmentIndex, reverse);
  431. }
  432. /**
  433. * @return {boolean}
  434. */
  435. isEmpty() {
  436. return this.getNumReferences() == 0;
  437. }
  438. /**
  439. * Create a SegmentIndex for a single segment of the given start time and
  440. * duration at the given URIs.
  441. *
  442. * @param {number} startTime
  443. * @param {number} duration
  444. * @param {!Array<string>} uris
  445. * @return {!shaka.media.SegmentIndex}
  446. * @export
  447. */
  448. static forSingleSegment(startTime, duration, uris) {
  449. const reference = new shaka.media.SegmentReference(
  450. /* startTime= */ startTime,
  451. /* endTime= */ startTime + duration,
  452. /* getUris= */ () => uris,
  453. /* startByte= */ 0,
  454. /* endByte= */ null,
  455. /* initSegmentReference= */ null,
  456. /* presentationTimeOffset= */ startTime,
  457. /* appendWindowStart= */ startTime,
  458. /* appendWindowEnd= */ startTime + duration);
  459. return new shaka.media.SegmentIndex([reference]);
  460. }
  461. };
  462. if (goog.DEBUG) {
  463. /**
  464. * Asserts that the given SegmentReferences are sorted.
  465. *
  466. * @param {!Array<shaka.media.SegmentReference>} references
  467. * @private
  468. */
  469. shaka.media.SegmentIndex.assertCorrectReferences_ = (references) => {
  470. goog.asserts.assert(references.every((r2, i) => {
  471. if (i == 0) {
  472. return true;
  473. }
  474. const r1 = references[i - 1];
  475. if (r1.startTime < r2.startTime) {
  476. return true;
  477. } else if (r1.startTime > r2.startTime) {
  478. return false;
  479. } else {
  480. if (r1.endTime <= r2.endTime) {
  481. return true;
  482. } else {
  483. return false;
  484. }
  485. }
  486. }), 'SegmentReferences are incorrect');
  487. };
  488. }
  489. /**
  490. * An iterator over a SegmentIndex's references.
  491. *
  492. * @implements {Iterator<shaka.media.SegmentReference>}
  493. * @export
  494. */
  495. shaka.media.SegmentIterator = class {
  496. /**
  497. * @param {shaka.media.SegmentIndex} segmentIndex
  498. * @param {number} index
  499. * @param {number} partialSegmentIndex
  500. * @param {boolean} reverse
  501. */
  502. constructor(segmentIndex, index, partialSegmentIndex, reverse) {
  503. /** @private {shaka.media.SegmentIndex} */
  504. this.segmentIndex_ = segmentIndex;
  505. /** @private {number} */
  506. this.currentPosition_ = index;
  507. /** @private {number} */
  508. this.currentPartialPosition_ = partialSegmentIndex;
  509. /** @private {boolean} */
  510. this.reverse = reverse;
  511. }
  512. /**
  513. * @param {boolean} reverse
  514. * @export
  515. */
  516. setReverse(reverse) {
  517. this.reverse = reverse;
  518. }
  519. /**
  520. * @return {number}
  521. * @export
  522. */
  523. currentPosition() {
  524. return this.currentPosition_;
  525. }
  526. /**
  527. * @return {shaka.media.SegmentReference}
  528. * @export
  529. */
  530. current() {
  531. let ref = this.segmentIndex_.get(this.currentPosition_);
  532. // When we advance past the end of partial references in next(), then add
  533. // new references in merge(), the pointers may not make sense any more.
  534. // This adjusts the invalid pointer values to point to the next newly added
  535. // segment or partial segment.
  536. if (ref && ref.hasPartialSegments() && ref.hasAllPartialSegments() &&
  537. this.currentPartialPosition_ >= ref.partialReferences.length) {
  538. this.currentPosition_++;
  539. this.currentPartialPosition_ = 0;
  540. ref = this.segmentIndex_.get(this.currentPosition_);
  541. }
  542. // If the regular segment contains partial segments, get the current
  543. // partial SegmentReference.
  544. if (ref && ref.hasPartialSegments()) {
  545. const partial = ref.partialReferences[this.currentPartialPosition_];
  546. return partial;
  547. }
  548. return ref;
  549. }
  550. /**
  551. * @override
  552. * @export
  553. */
  554. next() {
  555. const ref = this.segmentIndex_.get(this.currentPosition_);
  556. if (!this.reverse) {
  557. if (ref && ref.hasPartialSegments()) {
  558. // If the regular segment contains partial segments, move to the next
  559. // partial SegmentReference.
  560. this.currentPartialPosition_++;
  561. // If the current regular segment has been published completely, and
  562. // we've reached the end of its partial segments list, move to the next
  563. // regular segment.
  564. // If the Partial Segments list is still on the fly, do not move to
  565. // the next regular segment.
  566. if (ref.hasAllPartialSegments() &&
  567. this.currentPartialPosition_ == ref.partialReferences.length) {
  568. this.currentPosition_++;
  569. this.currentPartialPosition_ = 0;
  570. }
  571. } else {
  572. // If the regular segment doesn't contain partial segments, move to the
  573. // next regular segment.
  574. this.currentPosition_++;
  575. this.currentPartialPosition_ = 0;
  576. }
  577. } else {
  578. if (ref && ref.hasPartialSegments()) {
  579. // If the regular segment contains partial segments, move to the
  580. // previous partial SegmentReference.
  581. this.currentPartialPosition_--;
  582. if (this.currentPartialPosition_ < 0) {
  583. this.currentPosition_--;
  584. const prevRef = this.segmentIndex_.get(this.currentPosition_);
  585. if (prevRef && prevRef.hasPartialSegments()) {
  586. this.currentPartialPosition_ = prevRef.partialReferences.length - 1;
  587. } else {
  588. this.currentPartialPosition_ = 0;
  589. }
  590. }
  591. } else {
  592. // If the regular segment doesn't contain partial segments, move to the
  593. // previous regular segment.
  594. this.currentPosition_--;
  595. this.currentPartialPosition_ = 0;
  596. }
  597. }
  598. const res = this.current();
  599. return {
  600. 'value': res,
  601. 'done': !res,
  602. };
  603. }
  604. /**
  605. * @export
  606. */
  607. resetToLastIndependent() {
  608. const current = this.current();
  609. if (current.isPartial() && !current.isIndependent()) {
  610. const ref = this.segmentIndex_.get(this.currentPosition_);
  611. if (ref && ref.hasPartialSegments()) {
  612. let partial = ref.partialReferences[this.currentPartialPosition_];
  613. while (partial.isIndependent()) {
  614. if (this.currentPartialPosition_ <= 0) {
  615. break;
  616. }
  617. this.currentPartialPosition_--;
  618. partial = ref.partialReferences[this.currentPartialPosition_];
  619. }
  620. }
  621. }
  622. }
  623. };
  624. /**
  625. * A meta-SegmentIndex composed of multiple other SegmentIndexes.
  626. * Used in constructing multi-Period Streams for DASH.
  627. *
  628. * @extends shaka.media.SegmentIndex
  629. * @implements {shaka.util.IReleasable}
  630. * @implements {Iterable<!shaka.media.SegmentReference>}
  631. * @export
  632. */
  633. shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
  634. /** */
  635. constructor() {
  636. super([]);
  637. /** @private {!Array<!shaka.media.SegmentIndex>} */
  638. this.indexes_ = [];
  639. }
  640. /** Evicts all old SegmentIndexes in this MetaSegmentIndex that are empty. */
  641. evictEmpty() {
  642. while (this.indexes_.length > 1 && this.indexes_[0].isEmpty()) {
  643. const index = this.indexes_.shift();
  644. // In the case of this class, this.numEvicted_ represents the evicted
  645. // segments that were in indexes that were entirely evicted.
  646. this.numEvicted_ += index.getNumEvicted();
  647. index.release();
  648. }
  649. }
  650. /**
  651. * Append a SegmentIndex to this MetaSegmentIndex. This effectively stitches
  652. * the underlying Stream onto the end of the multi-Period Stream represented
  653. * by this MetaSegmentIndex.
  654. *
  655. * @param {!shaka.media.SegmentIndex} segmentIndex
  656. */
  657. appendSegmentIndex(segmentIndex) {
  658. goog.asserts.assert(
  659. this.indexes_.length == 0 || segmentIndex.getNumEvicted() == 0,
  660. 'Should not append a new segment index with already-evicted segments');
  661. this.indexes_.push(segmentIndex);
  662. }
  663. /**
  664. * Create a clone of this MetaSegmentIndex containing all the same indexes.
  665. *
  666. * @return {!shaka.media.MetaSegmentIndex}
  667. */
  668. clone() {
  669. const clone = new shaka.media.MetaSegmentIndex();
  670. // Be careful to clone the Array. We don't want to share the reference with
  671. // our clone and affect each other accidentally.
  672. clone.indexes_ = this.indexes_.slice();
  673. clone.numEvicted_ = this.numEvicted_;
  674. return clone;
  675. }
  676. /**
  677. * @override
  678. * @export
  679. */
  680. release() {
  681. for (const index of this.indexes_) {
  682. index.release();
  683. }
  684. this.indexes_ = [];
  685. }
  686. /**
  687. * @override
  688. * @export
  689. */
  690. forEachTopLevelReference(fn) {
  691. for (const index of this.indexes_) {
  692. index.forEachTopLevelReference(fn);
  693. }
  694. }
  695. /**
  696. * @override
  697. * @export
  698. */
  699. find(time) {
  700. let numPassedInEarlierIndexes = this.numEvicted_;
  701. for (const index of this.indexes_) {
  702. const position = index.find(time);
  703. if (position != null) {
  704. return position + numPassedInEarlierIndexes;
  705. }
  706. numPassedInEarlierIndexes += index.getNumEvicted() +
  707. index.getNumReferences();
  708. }
  709. return null;
  710. }
  711. /**
  712. * @override
  713. * @export
  714. */
  715. get(position) {
  716. let numPassedInEarlierIndexes = this.numEvicted_;
  717. let sawSegments = false;
  718. for (const index of this.indexes_) {
  719. goog.asserts.assert(
  720. !sawSegments || index.getNumEvicted() == 0,
  721. 'Should not see evicted segments after available segments');
  722. const reference = index.get(position - numPassedInEarlierIndexes);
  723. if (reference) {
  724. return reference;
  725. }
  726. const num = index.getNumReferences();
  727. numPassedInEarlierIndexes += index.getNumEvicted() + num;
  728. sawSegments = sawSegments || num != 0;
  729. }
  730. return null;
  731. }
  732. /**
  733. * @override
  734. * @export
  735. */
  736. offset(offset) {
  737. // offset() is only used by HLS, and MetaSegmentIndex is only used for DASH.
  738. goog.asserts.assert(
  739. false, 'offset() should not be used in MetaSegmentIndex!');
  740. }
  741. /**
  742. * @override
  743. * @export
  744. */
  745. merge(references) {
  746. // merge() is only used internally by the DASH and HLS parser on
  747. // SegmentIndexes, but never on MetaSegmentIndex.
  748. goog.asserts.assert(
  749. false, 'merge() should not be used in MetaSegmentIndex!');
  750. }
  751. /**
  752. * @override
  753. * @export
  754. */
  755. evict(time) {
  756. for (const index of this.indexes_) {
  757. index.evict(time);
  758. }
  759. this.evictEmpty();
  760. }
  761. /**
  762. * @override
  763. * @export
  764. */
  765. mergeAndEvict(references, windowStart) {
  766. // mergeAndEvict() is only used internally by the DASH and HLS parser on
  767. // SegmentIndexes, but never on MetaSegmentIndex.
  768. goog.asserts.assert(
  769. false, 'mergeAndEvict() should not be used in MetaSegmentIndex!');
  770. }
  771. /**
  772. * @override
  773. * @export
  774. */
  775. fit(windowStart, windowEnd) {
  776. // fit() is only used internally by manifest parsers on SegmentIndexes, but
  777. // never on MetaSegmentIndex.
  778. goog.asserts.assert(false, 'fit() should not be used in MetaSegmentIndex!');
  779. }
  780. /**
  781. * @override
  782. * @export
  783. */
  784. updateEvery(interval, updateCallback) {
  785. // updateEvery() is only used internally by the DASH parser on
  786. // SegmentIndexes, but never on MetaSegmentIndex.
  787. goog.asserts.assert(
  788. false, 'updateEvery() should not be used in MetaSegmentIndex!');
  789. }
  790. };