6ad09e5d by Jon-Carlos Rivera

Merge pull request #463 from videojs/simplify-stuff

Simplify stuff
2 parents d702ed19 b7b5d4d1
......@@ -422,7 +422,6 @@
i,
segment,
originalTime = time,
targetDuration = this.media_.targetDuration || 10,
numSegments = this.media_.segments.length,
lastSegment = numSegments - 1,
startIndex,
......@@ -442,70 +441,36 @@
// find segments with known timing information that bound the
// target time
// Walk backward until we find the first segment with timeline
// information that is earlier than `time`
for (i = lastSegment; i >= 0; i--) {
segment = this.media_.segments[i];
if (segment.end !== undefined && segment.end <= time) {
startIndex = i + 1;
knownStart = segment.end;
if (startIndex >= numSegments) {
// The last segment claims to end *before* the time we are
// searching for so just return it
return numSegments;
}
break;
}
if (segment.start !== undefined && segment.start <= time) {
if (segment.end !== undefined && segment.end > time) {
// we've found the target segment exactly
return i;
}
startIndex = i;
knownStart = segment.start;
break;
}
}
// Walk forward until we find the first segment with timeline
// information that is greater than `time`
for (i = 0; i < numSegments; i++) {
segment = this.media_.segments[i];
if (segment.start !== undefined && segment.start > time) {
endIndex = i - 1;
knownEnd = segment.start;
if (endIndex < 0) {
// The first segment claims to start *after* the time we are
// searching for so the target segment must no longer be
// available
return -1;
}
break;
}
if (segment.end !== undefined && segment.end > time) {
endIndex = i;
if (segment.end) {
if (segment.end > time) {
knownEnd = segment.end;
endIndex = i;
break;
} else {
knownStart = segment.end;
startIndex = i + 1;
}
}
}
// use the bounds we just found and playlist information to
// estimate the segment that contains the time we are looking for
if (startIndex !== undefined) {
// We have a known-start point that is before our desired time so
// walk from that point forwards
time = time - knownStart;
for (i = startIndex; i < (endIndex || numSegments); i++) {
segment = this.media_.segments[i];
time -= segment.duration || targetDuration;
time -= segment.duration;
if (time < 0) {
return i;
}
}
if (i === endIndex) {
if (i >= endIndex) {
// We haven't found a segment but we did hit a known end point
// so fallback to interpolating between the segment index
// based on the known span of the timeline we are dealing with
......@@ -523,24 +488,32 @@
time = knownEnd - time;
for (i = endIndex; i >= 0; i--) {
segment = this.media_.segments[i];
time -= segment.duration || targetDuration;
time -= segment.duration;
if (time < 0) {
return i;
}
}
// We haven't found a segment so load the first one
// We haven't found a segment so load the first one if time is zero
if (time === 0) {
return 0;
} else {
return -1;
}
} else {
// We known nothing so walk from the front of the playlist,
// subtracting durations until we find a segment that contains
// time and return it
time = time - this.expired_;
if (time < 0) {
return -1;
}
for (i = 0; i < numSegments; i++) {
segment = this.media_.segments[i];
time -= segment.duration || targetDuration;
time -= segment.duration;
if (time < 0) {
return i;
}
......
......@@ -10,8 +10,8 @@ var
// A fudge factor to apply to advertised playlist bitrates to account for
// temporary flucations in client bandwidth
bandwidthVariance = 1.2,
blacklistDuration = 5 * 60 * 1000, // 2 minute blacklist
TIME_FUDGE_FACTOR = 1 / 60, // Fudge factor to account for TimeRanges rounding
blacklistDuration = 5 * 60 * 1000, // 5 minute blacklist
TIME_FUDGE_FACTOR = 1 / 30, // Fudge factor to account for TimeRanges rounding
Component = videojs.getComponent('Component'),
// The amount of time to wait between checking the state of the buffer
......@@ -242,61 +242,57 @@ videojs.HlsHandler.prototype.handleSourceOpen = function() {
}
};
// Returns the array of time range edge objects that were additively
// modified between two TimeRanges.
videojs.Hls.bufferedAdditions_ = function(original, update) {
var result = [], edges = [],
i, inOriginalRanges;
// if original or update are falsey, return an empty list of
// additions
if (!original || !update) {
return result;
}
// 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 found only one such uncommon end-point return it.
videojs.Hls.findSoleUncommonTimeRangesEnd_ = function(original, update) {
var
i, start, end,
result = [],
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`
overlapsCurrentEnd = function(span) {
return (span[0] <= end && span[1] >= end);
};
// create a sorted array of time range start and end times
if (original) {
// Save all the edges in the `original` TimeRanges object
for (i = 0; i < original.length; i++) {
edges.push({ original: true, start: original.start(i) });
edges.push({ original: true, end: original.end(i) });
}
for (i = 0; i < update.length; i++) {
edges.push({ start: update.start(i) });
edges.push({ end: update.end(i) });
}
edges.sort(function(left, right) {
var leftTime, rightTime;
leftTime = left.start !== undefined ? left.start : left.end;
rightTime = right.start !== undefined ? right.start : right.end;
start = original.start(i);
end = original.end(i);
// when two times are equal, ensure the original edge covers the
// update
if (leftTime === rightTime) {
if (left.original) {
return left.start !== undefined ? -1 : 1;
edges.push([start, end]);
}
return right.start !== undefined ? -1 : 1;
}
return leftTime - rightTime;
});
// filter out all time range edges that occur during a period that
// was already covered by `original`
inOriginalRanges = false;
for (i = 0; i < edges.length; i++) {
// if this is a transition point for `original`, track whether
// subsequent edges are additions
if (edges[i].original) {
inOriginalRanges = edges[i].start !== undefined;
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;
}
// if we're in a time range that was in `original`, ignore this edge
if (inOriginalRanges) {
continue;
// at this point it must be a unique non-shrinking end edge
result.push(end);
}
// this edge occurred outside the range of `original`
result.push(edges[i]);
}
return result;
// 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];
};
var parseCodecs = function(codecs) {
......@@ -743,10 +739,6 @@ videojs.HlsHandler.prototype.checkBuffer_ = function() {
* append bytes into the SourceBuffer.
*/
videojs.HlsHandler.prototype.startCheckingBuffer_ = function() {
// if the player ever stalls, check if there is video data available
// to append immediately
this.tech_.on('waiting', (this.drainBuffer).bind(this));
this.checkBuffer_();
};
......@@ -759,7 +751,6 @@ videojs.HlsHandler.prototype.stopCheckingBuffer_ = function() {
window.clearTimeout(this.checkBufferTimeout_);
this.checkBufferTimeout_ = null;
}
this.tech_.off('waiting', this.drainBuffer);
};
var filterBufferedRanges = function(predicate) {
......@@ -1008,6 +999,12 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
// decrease in network performance or a server issue.
timeout: (segment.duration * 1.5) * 1000
}, function(error, request) {
// This is a timeout of a previously aborted segment request
// so simply ignore it
if (!self.segmentXhr_ || request !== self.segmentXhr_) {
return;
}
// the segment request is no longer outstanding
self.segmentXhr_ = null;
......@@ -1019,7 +1016,6 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
// otherwise, trigger a network error
if (!request.aborted && error) {
self.pendingSegment_ = null;
return self.blacklistCurrentPlaylist_({
status: request.status,
message: 'HLS segment request error at URL: ' + segmentInfo.uri,
......@@ -1040,7 +1036,9 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
} else {
segmentInfo.bytes = new Uint8Array(request.response);
}
self.pendingSegment_ = segmentInfo;
self.tech_.trigger('progress');
self.drainBuffer();
......@@ -1048,6 +1046,7 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
// with the updated bandwidth information
self.playlists.media(self.selectPlaylist());
});
};
videojs.HlsHandler.prototype.drainBuffer = function(event) {
......@@ -1170,7 +1169,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
currentMediaIndex,
currentBuffered,
seekable,
timelineUpdates;
timelineUpdate;
this.pendingSegment_ = null;
......@@ -1211,15 +1210,13 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
}
}
timelineUpdates = videojs.Hls.bufferedAdditions_(segmentInfo.buffered,
timelineUpdate = videojs.Hls.findSoleUncommonTimeRangesEnd_(segmentInfo.buffered,
this.tech_.buffered());
timelineUpdates.forEach(function (update) {
if (segment) {
if (update.end !== undefined) {
segment.end = update.end;
}
if (timelineUpdate && segment) {
segment.end = timelineUpdate;
}
});
// if we've buffered to the end of the video, let the MediaSource know
if (this.playlists.media().endList &&
......@@ -1230,7 +1227,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
return;
}
if (timelineUpdates.length ||
if (timelineUpdate !== null ||
segmentInfo.buffered.length !== this.tech_.buffered().length) {
this.updateDuration(playlist);
// check if it's time to download the next segment
......
......@@ -811,7 +811,7 @@
'1001.ts\n' +
'#EXTINF:5,\n' +
'1002.ts\n');
loader.media().segments[0].start = 150;
loader.media().segments[0].end = 154;
equal(loader.getMediaIndexForTime_(0), -1, 'the lowest returned value is negative one');
equal(loader.getMediaIndexForTime_(45), -1, 'expired content returns negative one');
......@@ -819,6 +819,7 @@
equal(loader.getMediaIndexForTime_(50 + 100), 0, 'calculates the earliest available position');
equal(loader.getMediaIndexForTime_(50 + 100 + 2), 0, 'calculates within the first segment');
equal(loader.getMediaIndexForTime_(50 + 100 + 2), 0, 'calculates within the first segment');
equal(loader.getMediaIndexForTime_(50 + 100 + 4), 1, 'calculates within the second segment');
equal(loader.getMediaIndexForTime_(50 + 100 + 4.5), 1, 'calculates within the second segment');
equal(loader.getMediaIndexForTime_(50 + 100 + 6), 1, 'calculates within the second segment');
});
......@@ -837,9 +838,9 @@
loader.expired_ = 160;
// annotate the first segment with a start time
// this number would be coming from the Source Buffer in practice
loader.media().segments[0].start = 150;
loader.media().segments[0].end = 150;
equal(loader.getMediaIndexForTime_(151), 0, 'prefers the value on the first segment');
equal(loader.getMediaIndexForTime_(149), 0, 'prefers the value on the first segment');
clock.tick(10 * 1000); // trigger a playlist refresh
requests.shift().respond(200, null,
......
......@@ -2745,47 +2745,72 @@ test('does not download segments if preload option set to none', function() {
module('Buffer Inspection');
test('detects time range edges added by updates', function() {
var edges;
test('detects time range end-point changed by updates', function() {
var edge;
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]),
// Single-range changes
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[0, 11]]));
deepEqual(edges, [{ end: 11 }], 'detected a forward addition');
strictEqual(edge, 11, 'detected a forward addition');
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[5, 10]]),
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[5, 10]]),
videojs.createTimeRange([[0, 10]]));
deepEqual(edges, [{ start: 0 }], 'detected a backward addition');
strictEqual(edge, null, 'ignores backward addition');
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[5, 10]]),
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[5, 10]]),
videojs.createTimeRange([[0, 11]]));
deepEqual(edges, [
{ start: 0 }, { end: 11 }
], 'detected forward and backward additions');
strictEqual(edge, 11, 'detected a forward addition & ignores a backward addition');
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[0, 10]]));
deepEqual(edges, [], 'detected no addition');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[0, 9]]));
strictEqual(edge, null, 'ignores a backwards addition resulting from a shrinking range');
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([]),
videojs.createTimeRange([[0, 10]]));
deepEqual(edges, [
{ start: 0 },
{ end: 10 }
], 'detected an initial addition');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[2, 7]]));
strictEqual(edge, null, 'ignores a forward & backwards addition resulting from a shrinking range');
edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[0, 10], [20, 30]]));
deepEqual(edges, [
{ start: 20 },
{ end: 30}
], 'detected a non-contiguous addition');
});
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[2, 10]]),
videojs.createTimeRange([[0, 7]]));
strictEqual(edge, null, 'ignores a forward & backwards addition resulting from a range shifted backward');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[2, 10]]),
videojs.createTimeRange([[5, 15]]));
strictEqual(edge, 15, 'detected a forwards addition resulting from a range shifted foward');
// Multiple-range changes
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10]]),
videojs.createTimeRange([[0, 11], [12, 15]]));
strictEqual(edge, null, 'ignores multiple new forward additions');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10], [20, 40]]),
videojs.createTimeRange([[20, 50]]));
strictEqual(edge, 50, 'detected a forward addition & ignores range removal');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10], [20, 40]]),
videojs.createTimeRange([[0, 50]]));
strictEqual(edge, 50, 'detected a forward addition & ignores merges');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 10], [20, 40]]),
videojs.createTimeRange([[0, 40]]));
strictEqual(edge, null, 'ignores merges');
// Empty input
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange(),
videojs.createTimeRange([[0, 11]]));
strictEqual(edge, 11, 'handle an empty original TimeRanges object');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 11]]),
videojs.createTimeRange());
strictEqual(edge, null, 'handle an empty update TimeRanges object');
test('treats null buffered ranges as no addition', function() {
var edges = videojs.Hls.bufferedAdditions_(null,
// Null input
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(null,
videojs.createTimeRange([[0, 11]]));
strictEqual(edge, 11, 'treat null original buffer as an empty TimeRanges object');
equal(edges.length, 0, 'no additions');
edge = videojs.Hls.findSoleUncommonTimeRangesEnd_(videojs.createTimeRange([[0, 11]]),
null);
strictEqual(edge, null, 'treat null update buffer as an empty TimeRanges object');
});
})(window, window.videojs);
......