UNPKG

shaka-player

Version:
213 lines (185 loc) 5.54 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.media.TimeRangesUtils'); /** * @summary A set of utility functions for dealing with TimeRanges objects. */ shaka.media.TimeRangesUtils = class { /** * Returns whether the buffer is small enough to be ignored. * * @param {TimeRanges} b * @return {boolean} * @private */ static isBufferNegligible_(b) { // Workaround Safari bug: https://bit.ly/2trx6O8 // Firefox may leave <1e-4s of data in buffer after clearing all content return b.length == 1 && b.end(0) - b.start(0) < 1e-4; } /** * Gets the first timestamp in the buffer. * * @param {TimeRanges} b * @return {?number} The first buffered timestamp, in seconds, if |buffered| * is non-empty; otherwise, return null. */ static bufferStart(b) { if (!b) { return null; } if (shaka.media.TimeRangesUtils.isBufferNegligible_(b)) { return null; } // Workaround Edge bug: https://bit.ly/2JYLPeB if (b.length == 1 && b.start(0) < 0) { return 0; } return b.length ? b.start(0) : null; } /** * Gets the last timestamp in the buffer. * * @param {TimeRanges} b * @return {?number} The last buffered timestamp, in seconds, if |buffered| * is non-empty; otherwise, return null. */ static bufferEnd(b) { if (!b) { return null; } if (shaka.media.TimeRangesUtils.isBufferNegligible_(b)) { return null; } return b.length ? b.end(b.length - 1) : null; } /** * Determines if the given time is inside a buffered range. * * @param {TimeRanges} b * @param {number} time Playhead time * @return {boolean} */ static isBuffered(b, time) { if (!b || !b.length) { return false; } if (shaka.media.TimeRangesUtils.isBufferNegligible_(b)) { return false; } if (time > b.end(b.length - 1)) { return false; } return time >= b.start(0); } /** * Computes how far ahead of the given timestamp is buffered. To provide * smooth playback while jumping gaps, we don't include the gaps when * calculating this. * This only includes the amount of content that is buffered. * * @param {TimeRanges} b * @param {number} time * @return {number} The number of seconds buffered, in seconds, ahead of the * given time. */ static bufferedAheadOf(b, time) { if (!b || !b.length) { return 0; } if (shaka.media.TimeRangesUtils.isBufferNegligible_(b)) { return 0; } // We calculate the buffered amount by ONLY accounting for the content // buffered (i.e. we ignore the times of the gaps). We also buffer through // all gaps. // Therefore, we start at the end and add up all buffers until |time|. let result = 0; for (const {start, end} of shaka.media.TimeRangesUtils.getBufferedInfo(b)) { if (end > time) { result += end - Math.max(start, time); } } return result; } /** * Determines if the given time is inside a gap between buffered ranges. If * it is, this returns the index of the buffer that is *ahead* of the gap. * * @param {TimeRanges} b * @param {number} time * @param {number} threshold * @return {?number} The index of the buffer after the gap, or null if not in * a gap. */ static getGapIndex(b, time, threshold) { const TimeRangesUtils = shaka.media.TimeRangesUtils; if (!b || !b.length) { return null; } if (shaka.media.TimeRangesUtils.isBufferNegligible_(b)) { return null; } const idx = TimeRangesUtils.getBufferedInfo(b).findIndex((item, i, arr) => { return item.start > time && (i == 0 || arr[i - 1].end - time <= threshold); }); return idx >= 0 ? idx : null; } /** * @param {TimeRanges} b * @return {!Array<shaka.extern.BufferedRange>} */ static getBufferedInfo(b) { if (!b) { return []; } const ret = []; for (let i = 0; i < b.length; i++) { ret.push({start: b.start(i), end: b.end(i)}); } return ret; } /** * This operation can be potentially EXPENSIVE and should only be done in * debug builds for debugging purposes. * * @param {TimeRanges} oldRanges * @param {TimeRanges} newRanges * @return {?shaka.extern.BufferedRange} The last added range, * chronologically by presentation time. */ static computeAddedRange(oldRanges, newRanges) { const TimeRangesUtils = shaka.media.TimeRangesUtils; if (!oldRanges || !oldRanges.length) { return null; } if (!newRanges || !newRanges.length) { return TimeRangesUtils.getBufferedInfo(newRanges).pop(); } const newRangesReversed = TimeRangesUtils.getBufferedInfo(newRanges).reverse(); const oldRangesReversed = TimeRangesUtils.getBufferedInfo(oldRanges).reverse(); for (const newRange of newRangesReversed) { let foundOverlap = false; for (const oldRange of oldRangesReversed) { if (oldRange.end >= newRange.start && oldRange.end <= newRange.end) { foundOverlap = true; // If the new range goes beyond the corresponding old one, the // difference is newly-added. if (newRange.end > oldRange.end) { return {start: oldRange.end, end: newRange.end}; } } } if (!foundOverlap) { return newRange; } } return null; } };