yanchengPowerSupply/node_modules/videojs-contrib-hls/es5/ranges.js

379 lines
13 KiB
JavaScript

/**
* ranges
*
* Utilities for working with TimeRanges.
*
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _videoJs = require('video.js');
var _videoJs2 = _interopRequireDefault(_videoJs);
// Fudge factor to account for TimeRanges rounding
var TIME_FUDGE_FACTOR = 1 / 30;
// Comparisons between time values such as current time and the end of the buffered range
// can be misleading because of precision differences or when the current media has poorly
// aligned audio and video, which can cause values to be slightly off from what you would
// expect. This value is what we consider to be safe to use in such comparisons to account
// for these scenarios.
var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
/**
* Clamps a value to within a range
* @param {Number} num - the value to clamp
* @param {Number} start - the start of the range to clamp within, inclusive
* @param {Number} end - the end of the range to clamp within, inclusive
* @return {Number}
*/
var clamp = function clamp(num, _ref) {
var _ref2 = _slicedToArray(_ref, 2);
var start = _ref2[0];
var end = _ref2[1];
return Math.min(Math.max(start, num), end);
};
var filterRanges = function filterRanges(timeRanges, predicate) {
var results = [];
var i = undefined;
if (timeRanges && timeRanges.length) {
// Search for ranges that match the predicate
for (i = 0; i < timeRanges.length; i++) {
if (predicate(timeRanges.start(i), timeRanges.end(i))) {
results.push([timeRanges.start(i), timeRanges.end(i)]);
}
}
}
return _videoJs2['default'].createTimeRanges(results);
};
/**
* Attempts to find the buffered TimeRange that contains the specified
* time.
* @param {TimeRanges} buffered - the TimeRanges object to query
* @param {number} time - the time to filter on.
* @returns {TimeRanges} a new TimeRanges object
*/
var findRange = function findRange(buffered, time) {
return filterRanges(buffered, function (start, end) {
return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
});
};
/**
* Returns the TimeRanges that begin later than the specified time.
* @param {TimeRanges} timeRanges - the TimeRanges object to query
* @param {number} time - the time to filter on.
* @returns {TimeRanges} a new TimeRanges object.
*/
var findNextRange = function findNextRange(timeRanges, time) {
return filterRanges(timeRanges, function (start) {
return start - TIME_FUDGE_FACTOR >= time;
});
};
/**
* Returns gaps within a list of TimeRanges
* @param {TimeRanges} buffered - the TimeRanges object
* @return {TimeRanges} a TimeRanges object of gaps
*/
var findGaps = function findGaps(buffered) {
if (buffered.length < 2) {
return _videoJs2['default'].createTimeRanges();
}
var ranges = [];
for (var i = 1; i < buffered.length; i++) {
var start = buffered.end(i - 1);
var end = buffered.start(i);
ranges.push([start, end]);
}
return _videoJs2['default'].createTimeRanges(ranges);
};
/**
* Search for a likely end time for the segment that was just appened
* based on the state of the `buffered` property before and after the
* append. If we fin only one such uncommon end-point return it.
* @param {TimeRanges} original - the buffered time ranges before the update
* @param {TimeRanges} update - the buffered time ranges after the update
* @returns {Number|null} the end time added between `original` and `update`,
* or null if one cannot be unambiguously determined.
*/
var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) {
var i = undefined;
var start = undefined;
var end = undefined;
var result = [];
var edges = [];
// In order to qualify as a possible candidate, the end point must:
// 1) Not have already existed in the `original` ranges
// 2) Not result from the shrinking of a range that already existed
// in the `original` ranges
// 3) Not be contained inside of a range that existed in `original`
var overlapsCurrentEnd = function overlapsCurrentEnd(span) {
return span[0] <= end && span[1] >= end;
};
if (original) {
// Save all the edges in the `original` TimeRanges object
for (i = 0; i < original.length; i++) {
start = original.start(i);
end = original.end(i);
edges.push([start, end]);
}
}
if (update) {
// Save any end-points in `update` that are not in the `original`
// TimeRanges object
for (i = 0; i < update.length; i++) {
start = update.start(i);
end = update.end(i);
if (edges.some(overlapsCurrentEnd)) {
continue;
}
// at this point it must be a unique non-shrinking end edge
result.push(end);
}
}
// we err on the side of caution and return null if didn't find
// exactly *one* differing end edge in the search above
if (result.length !== 1) {
return null;
}
return result[0];
};
/**
* Calculate the intersection of two TimeRanges
* @param {TimeRanges} bufferA
* @param {TimeRanges} bufferB
* @returns {TimeRanges} The interesection of `bufferA` with `bufferB`
*/
var bufferIntersection = function bufferIntersection(bufferA, bufferB) {
var start = null;
var end = null;
var arity = 0;
var extents = [];
var ranges = [];
if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
return _videoJs2['default'].createTimeRange();
}
// Handle the case where we have both buffers and create an
// intersection of the two
var count = bufferA.length;
// A) Gather up all start and end times
while (count--) {
extents.push({ time: bufferA.start(count), type: 'start' });
extents.push({ time: bufferA.end(count), type: 'end' });
}
count = bufferB.length;
while (count--) {
extents.push({ time: bufferB.start(count), type: 'start' });
extents.push({ time: bufferB.end(count), type: 'end' });
}
// B) Sort them by time
extents.sort(function (a, b) {
return a.time - b.time;
});
// C) Go along one by one incrementing arity for start and decrementing
// arity for ends
for (count = 0; count < extents.length; count++) {
if (extents[count].type === 'start') {
arity++;
// D) If arity is ever incremented to 2 we are entering an
// overlapping range
if (arity === 2) {
start = extents[count].time;
}
} else if (extents[count].type === 'end') {
arity--;
// E) If arity is ever decremented to 1 we leaving an
// overlapping range
if (arity === 1) {
end = extents[count].time;
}
}
// F) Record overlapping ranges
if (start !== null && end !== null) {
ranges.push([start, end]);
start = null;
end = null;
}
}
return _videoJs2['default'].createTimeRanges(ranges);
};
/**
* Calculates the percentage of `segmentRange` that overlaps the
* `buffered` time ranges.
* @param {TimeRanges} segmentRange - the time range that the segment
* covers adjusted according to currentTime
* @param {TimeRanges} referenceRange - the original time range that the
* segment covers
* @param {Number} currentTime - time in seconds where the current playback
* is at
* @param {TimeRanges} buffered - the currently buffered time ranges
* @returns {Number} percent of the segment currently buffered
*/
var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) {
var referenceDuration = referenceRange.end(0) - referenceRange.start(0);
var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
var bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
var adjustedIntersection = bufferIntersection(adjustedRange, buffered);
var referenceIntersection = bufferIntersection(referenceRange, buffered);
var adjustedOverlap = 0;
var referenceOverlap = 0;
var count = adjustedIntersection.length;
while (count--) {
adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count);
// If the current overlap segment starts at currentTime, then increase the
// overlap duration so that it actually starts at the beginning of referenceRange
// by including the difference between the two Range's durations
// This is a work around for the way Flash has no buffer before currentTime
if (adjustedIntersection.start(count) === currentTime) {
adjustedOverlap += bufferMissingFromAdjusted;
}
}
count = referenceIntersection.length;
while (count--) {
referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count);
}
// Use whichever value is larger for the percentage-buffered since that value
// is likely more accurate because the only way
return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
};
/**
* Return the amount of a range specified by the startOfSegment and segmentDuration
* overlaps the current buffered content.
*
* @param {Number} startOfSegment - the time where the segment begins
* @param {Number} segmentDuration - the duration of the segment in seconds
* @param {Number} currentTime - time in seconds where the current playback
* is at
* @param {TimeRanges} buffered - the state of the buffer
* @returns {Number} percentage of the segment's time range that is
* already in `buffered`
*/
var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) {
var endOfSegment = startOfSegment + segmentDuration;
// The entire time range of the segment
var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]);
// The adjusted segment time range that is setup such that it starts
// no earlier than currentTime
// Flash has no notion of a back-buffer so adjustedSegmentRange adjusts
// for that and the function will still return 100% if a only half of a
// segment is actually in the buffer as long as the currentTime is also
// half-way through the segment
var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]);
// This condition happens when the currentTime is beyond the segment's
// end time
if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) {
return 0;
}
var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered);
// If the segment is reported as having a zero duration, return 0%
// since it is likely that we will need to fetch the segment
if (isNaN(percent) || percent === Infinity || percent === -Infinity) {
return 0;
}
return percent;
};
/**
* Gets a human readable string for a TimeRange
*
* @param {TimeRange} range
* @returns {String} a human readable string
*/
var printableRange = function printableRange(range) {
var strArr = [];
if (!range || !range.length) {
return '';
}
for (var i = 0; i < range.length; i++) {
strArr.push(range.start(i) + ' => ' + range.end(i));
}
return strArr.join(', ');
};
/**
* Calculates the amount of time left in seconds until the player hits the end of the
* buffer and causes a rebuffer
*
* @param {TimeRange} buffered
* The state of the buffer
* @param {Numnber} currentTime
* The current time of the player
* @param {Number} playbackRate
* The current playback rate of the player. Defaults to 1.
* @return {Number}
* Time until the player has to start rebuffering in seconds.
* @function timeUntilRebuffer
*/
var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
var playbackRate = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
return (bufferedEnd - currentTime) / playbackRate;
};
exports['default'] = {
findRange: findRange,
findNextRange: findNextRange,
findGaps: findGaps,
findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd,
getSegmentBufferedPercent: getSegmentBufferedPercent,
TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR,
SAFE_TIME_DELTA: SAFE_TIME_DELTA,
printableRange: printableRange,
timeUntilRebuffer: timeUntilRebuffer
};
module.exports = exports['default'];