diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js
index 399a94d282..c9bd36674d 100644
--- a/lib/media/streaming_engine.js
+++ b/lib/media/streaming_engine.js
@@ -174,7 +174,6 @@ shaka.media.StreamingEngine = function(
* stream: shakaExtern.Stream,
* lastStream: ?shakaExtern.Stream,
* lastSegmentReference: shaka.media.SegmentReference,
- * drift: ?number,
* needInitSegment: boolean,
* needRebuffering: boolean,
* needPeriodIndex: number,
@@ -198,12 +197,6 @@ shaka.media.StreamingEngine = function(
* The Stream of the last segment that was appended.
* @property {shaka.media.SegmentReference} lastSegmentReference
* The SegmentReference of the last segment that was appended.
- * @property {?number} drift
- * The number of seconds that the segments' timestamps are offset from the
- * SegmentReferences' timestamps. For example, a positive value indicates
- * that the segments are ahead of the SegmentReferences. Note that the
- * segments' timestamps are the true values; however, the drift should
- * never be very large for valid content.
* @property {boolean} needInitSegment
* True indicates that |stream|'s init segment must be inserted before the
* next media segment is appended.
@@ -555,7 +548,6 @@ shaka.media.StreamingEngine.prototype.initStreams_ = function(streamsByType) {
type: type,
lastStream: null,
lastSegmentReference: null,
- drift: null,
needInitSegment: true,
needRebuffering: false,
needPeriodIndex: needPeriodIndex,
@@ -794,14 +786,10 @@ shaka.media.StreamingEngine.prototype.update_ = function(mediaState) {
var bufferEnd = this.mediaSourceEngine_.bufferEnd(mediaState.type);
var timeNeeded = this.getTimeNeeded_(
mediaState, playheadTime, bufferedAhead, bufferEnd);
- if (timeNeeded == null)
- return null;
shaka.log.v2(logPrefix, 'timeNeeded=' + timeNeeded);
- var timeline = this.manifest_.presentationTimeline;
-
// Check if we've buffered to the end of the presentation.
- if (timeNeeded >= timeline.getDuration()) {
+ if (timeNeeded >= this.manifest_.presentationTimeline.getDuration()) {
// We shouldn't rebuffer if the playhead is close to the end of the
// presentation.
shaka.log.debug(logPrefix, 'buffered to end of presentation');
@@ -838,27 +826,17 @@ shaka.media.StreamingEngine.prototype.update_ = function(mediaState) {
return null;
}
- // Check segment availability.
- var availabilityStart = timeline.getSegmentAvailabilityStart();
- var availabilityEnd = timeline.getSegmentAvailabilityEnd();
- if ((timeNeeded < availabilityStart) || (timeNeeded > availabilityEnd)) {
- // The next segment is not available. In the usual case, this occurs when
- // we've buffered to the live-edge of a live presentation; in the
- // degenerate case, this occurs if the playhead is forced outside the
- // segment availability window; either way try another update in a second.
- shaka.log.v1(logPrefix,
- 'next segment is outside segment availability window:',
- 'playheadTime=' + playheadTime,
- 'timeNeeded=' + timeNeeded,
- 'availabilityStart=' + availabilityStart,
- 'availabilityEnd=' + availabilityEnd);
+ var reference = this.getSegmentReferenceNeeded_(
+ mediaState, playheadTime, currentPeriodIndex);
+ if (!reference) {
+ // The segment could not be found, does not exist, or is not available. In
+ // any case just try again... if the manifest is incomplete or is not being
+ // updated then we'll idle forever; otherwise, we'll end up getting a
+ // SegmentReference eventually.
return 1;
}
- var reference = this.getSegmentReference_(
- mediaState, playheadTime, currentPeriodIndex);
this.fetchAndAppend_(mediaState, playheadTime, currentPeriodIndex, reference);
-
return null;
};
@@ -872,8 +850,7 @@ shaka.media.StreamingEngine.prototype.update_ = function(mediaState) {
* @param {number} playheadTime
* @param {number} bufferedAhead
* @param {?number} bufferEnd
- * @return {?number} The next timestamp needed or null if the playhead is is in
- * an unbuffered region behind the buffer.
+ * @return {number} The next timestamp needed.
* @throws {!shaka.util.Error} if the buffer is inconsistent with our
* expectations.
* @private
@@ -886,8 +863,9 @@ shaka.media.StreamingEngine.prototype.getTimeNeeded_ = function(
// to determine this and not the actual buffer for two reasons:
// 1. actual segments end slightly before their advertised end times, so
// the next timestamp we need is actually larger than |bufferEnd|; and
- // 2. there may be drift, but we need drift free times when comparing times
- // against presentation and Period boundaries.
+ // 2. there may be drift (the timestamps in the segments are ahead/behind
+ // of the timestamps in the manifest), but we need drift free times when
+ // comparing times against presentation and Period boundaries.
if (bufferedAhead == 0) {
// The playhead is in an unbuffered region.
@@ -903,24 +881,29 @@ shaka.media.StreamingEngine.prototype.getTimeNeeded_ = function(
}
return playheadTime;
} else if (bufferEnd > playheadTime) {
- // The user agent seeked backwards but seeked() was not called or has not
- // been called yet (because it's a race). Assume seeked() will be called.
+ // We may find ourseleves in this state for two reasons:
+ // 1. there is a significant amount of positive drift; or
+ // 2. the user agent seeked backwards but seeked() was not called or has
+ // not been called yet (because it's a race).
+ // For case 1 we'll idle forever, and for case 2 we'll end up buffering a
+ // segment, removing it, and then buffering it again (note that this case
+ // should be rare).
shaka.log.debug(logPrefix,
'playhead in unbuffered region (behind buffer):',
'playheadTime=' + playheadTime,
'bufferEnd=' + bufferEnd);
- return null;
} else {
- // We may find ourseleves in this state for three reasons:
+ // We may find ourseleves in this state for four reasons:
// 1. the playhead is exactly at the end of the buffer;
// 2. the browser allowed the playhead to proceed past the end of
- // the buffer (either under normal or accelerated playback rates); or
- // 3. the user agent seeked forwards but seeked() was not called or has
+ // the buffer (either under normal or accelerated playback rates);
+ // 3. there is a significant amount of negative drift; or
+ // 4. the user agent seeked forwards but seeked() was not called or has
// not been called yet (because it's a race).
// For cases 1 and 2 we'll end up buffering the next segment we want
- // anyways, and for case 3 we'll end up buffering the next segment and
- // then just removing it and buffering it again (note that this case
- // should be rare).
+ // anyways, for case 3 we'll end up buffering behind the playhead until
+ // we catch up, and for case 4 we'll proceed as in case 2 of the previous
+ // block.
shaka.log.debug(logPrefix,
'playhead in unbuffered region (ahead of buffer):',
'playheadTime=' + playheadTime,
@@ -951,102 +934,142 @@ shaka.media.StreamingEngine.prototype.getTimeNeeded_ = function(
* @param {shaka.media.StreamingEngine.MediaState_} mediaState
* @param {number} playheadTime
* @param {number} currentPeriodIndex
- * @return {!shaka.media.SegmentReference} The SegmentReference of the
- * next segment needed.
- * @throws {!shaka.util.Error} If the next segment does not exist.
+ * @return {shaka.media.SegmentReference} The SegmentReference of the
+ * next segment needed, or null if a segment could not be found, does not
+ * exist, or is not available.
* @private
*/
-shaka.media.StreamingEngine.prototype.getSegmentReference_ = function(
+shaka.media.StreamingEngine.prototype.getSegmentReferenceNeeded_ = function(
mediaState, playheadTime, currentPeriodIndex) {
var logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
+ if (mediaState.lastSegmentReference &&
+ mediaState.stream == mediaState.lastStream) {
+ // Something is buffered from the same Stream.
+ var position = mediaState.lastSegmentReference.position + 1;
+ shaka.log.v2(logPrefix, 'next position known:', 'position=' + position);
+
+ return this.getSegmentReferenceIfAvailable_(
+ mediaState, currentPeriodIndex, position);
+ }
+
var position;
- if (!mediaState.lastSegmentReference) {
+
+ if (mediaState.lastSegmentReference) {
+ // Something is buffered from another Stream.
+ goog.asserts.assert(mediaState.lastStream, 'lastStream should not be null');
+ shaka.log.v1(logPrefix, 'next position unknown: another Stream buffered');
+ var lastPeriodIndex =
+ this.findPeriodContainingStream_(mediaState.lastStream);
+ var lastPeriod = this.manifest_.periods[lastPeriodIndex];
+ position = this.lookupSegmentPosition_(
+ mediaState,
+ lastPeriod.startTime + mediaState.lastSegmentReference.endTime,
+ currentPeriodIndex);
+ } else {
+ // Nothing is buffered.
goog.asserts.assert(!mediaState.lastStream, 'lastStream should be null');
shaka.log.v1(logPrefix, 'next position unknown: nothing buffered');
position = this.lookupSegmentPosition_(
mediaState, playheadTime, currentPeriodIndex);
- } else {
- goog.asserts.assert(mediaState.lastStream, 'lastStream should not be null');
- if (mediaState.stream == mediaState.lastStream) {
- // Something is buffered from the same Stream.
- position = mediaState.lastSegmentReference.position + 1;
- shaka.log.v2(logPrefix, 'using next position:', 'position=' + position);
- } else {
- // Something is buffered from another Stream.
- shaka.log.v1(logPrefix, 'next position unknown: another Stream buffered');
- var lastPeriodIndex =
- this.findPeriodContainingStream_(mediaState.lastStream);
- var lastPeriod = this.manifest_.periods[lastPeriodIndex];
- position = this.lookupSegmentPosition_(
- mediaState,
- lastPeriod.startTime + mediaState.lastSegmentReference.endTime,
- currentPeriodIndex);
- }
}
- var reference = mediaState.stream.getSegmentReference(position);
- if (!reference) {
- shaka.log.error(logPrefix,
- 'invalid segment index: SegmentReference does not exist:',
- 'currentPeriodIndex=' + currentPeriodIndex,
- 'position=' + position);
- throw new shaka.util.Error(
- shaka.util.Error.Category.STREAMING,
- shaka.util.Error.Code.INVALID_SEGMENT_INDEX,
- mediaState.type,
- currentPeriodIndex,
- position);
- }
+ if (position == null)
+ return null;
+ // If there's positive drift then we need to get the previous segment;
+ // however, we don't actually know how much drift there is, so we must
+ // unconditionally get the previous segment. If it turns out that there's
+ // non-positive drift then we'll just end up buffering beind the playhead a
+ // little more than we needed.
+ var optimalPosition = Math.max(0, position - 1);
+ var reference =
+ this.getSegmentReferenceIfAvailable_(
+ mediaState, currentPeriodIndex, optimalPosition) ||
+ this.getSegmentReferenceIfAvailable_(
+ mediaState, currentPeriodIndex, position);
return reference;
};
/**
- * Looks up the position of the next segment needed.
+ * Looks up the position of the segment containing the given timestamp.
*
* @param {shaka.media.StreamingEngine.MediaState_} mediaState
- * @param {number} time relative to presentation timeline
+ * @param {number} presentationTime The timestamp needed, relative to the
+ * start of the presentation.
* @param {number} currentPeriodIndex
- * @return {number}
- * @throws {!shaka.util.Error} If the next segment does not exist.
+ * @return {?number} A segment position, or null if a segment was not be found.
* @private
*/
shaka.media.StreamingEngine.prototype.lookupSegmentPosition_ = function(
- mediaState, time, currentPeriodIndex) {
+ mediaState, presentationTime, currentPeriodIndex) {
var logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
var currentPeriod = this.manifest_.periods[currentPeriodIndex];
- shaka.log.v1(logPrefix,
- 'looking up next position:',
- 'time=' + time,
- 'currentPeriod.startTime=' + currentPeriod.startTime,
- 'mediaState.drift=' + mediaState.drift);
+ shaka.log.debug(logPrefix,
+ 'looking up segment:',
+ 'presentationTime=' + presentationTime,
+ 'currentPeriod.startTime=' + currentPeriod.startTime);
- var lookupTime = time - currentPeriod.startTime - mediaState.drift;
- lookupTime = Math.max(lookupTime, 0);
+ var lookupTime = Math.max(0, presentationTime - currentPeriod.startTime);
var position = mediaState.stream.findSegmentPosition(lookupTime);
if (position == null) {
shaka.log.warning(logPrefix,
- 'next segment does not exist:',
- 'lookupTime=' + lookupTime,
- 'time=' + time,
+ 'cannot find segment:',
'currentPeriod.startTime=' + currentPeriod.startTime,
- 'mediaState.drift=' + mediaState.drift);
- throw new shaka.util.Error(
- shaka.util.Error.Category.STREAMING,
- shaka.util.Error.Code.SEGMENT_DOES_NOT_EXIST,
- mediaState.type,
- currentPeriodIndex,
- time);
+ 'lookupTime=' + lookupTime);
}
return position;
};
+/**
+ * Gets the SegmentReference at the given position if it's available.
+ *
+ * @param {shaka.media.StreamingEngine.MediaState_} mediaState
+ * @param {number} currentPeriodIndex
+ * @param {number} position
+ * @return {shaka.media.SegmentReference}
+ *
+ * @private
+ */
+shaka.media.StreamingEngine.prototype.getSegmentReferenceIfAvailable_ =
+ function(mediaState, currentPeriodIndex, position) {
+ var logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
+ var currentPeriod = this.manifest_.periods[currentPeriodIndex];
+
+ var reference = mediaState.stream.getSegmentReference(position);
+ if (!reference) {
+ shaka.log.v1(logPrefix,
+ 'segment does not exist:',
+ 'currentPeriod.startTime=' + currentPeriod.startTime,
+ 'position=' + position);
+ return null;
+ }
+
+ var timeline = this.manifest_.presentationTimeline;
+ var availabilityStart = timeline.getSegmentAvailabilityStart();
+ var availabilityEnd = timeline.getSegmentAvailabilityEnd();
+
+ if ((currentPeriod.startTime + reference.endTime < availabilityStart) ||
+ (currentPeriod.startTime + reference.startTime > availabilityEnd)) {
+ shaka.log.v2(logPrefix,
+ 'segment is not available:',
+ 'currentPeriod.startTime=' + currentPeriod.startTime,
+ 'reference.startTime=' + reference.startTime,
+ 'reference.endTime=' + reference.endTime,
+ 'availabilityStart=' + availabilityStart,
+ 'availabilityEnd=' + availabilityEnd);
+ return null;
+ }
+
+ return reference;
+};
+
+
/**
* Fetches and appends the given segment; sets up the given MediaState's
* associated SourceBuffer and evicts segments if either are required
@@ -1067,7 +1090,6 @@ shaka.media.StreamingEngine.prototype.fetchAndAppend_ = function(
'fetchAndAppend_:',
'playheadTime=' + playheadTime,
'currentPeriod.startTime=' + currentPeriod.startTime,
- 'mediaState.drift=' + mediaState.drift,
'reference.position=' + reference.position,
'reference.startTime=' + reference.startTime,
'reference.endTime=' + reference.endTime);
@@ -1110,12 +1132,6 @@ shaka.media.StreamingEngine.prototype.fetchAndAppend_ = function(
stream,
reference,
results[1]);
- }.bind(this)).then(function() {
- if (this.destroyed_) return;
- return this.handleDrift_(mediaState,
- playheadTime,
- currentPeriodIndex,
- reference);
}.bind(this)).then(function() {
mediaState.performingUpdate = false;
@@ -1277,54 +1293,6 @@ shaka.media.StreamingEngine.prototype.evict_ = function(
};
-/**
- * Handles drift.
- *
- * @param {!shaka.media.StreamingEngine.MediaState_} mediaState
- * @param {number} playheadTime
- * @param {number} currentPeriodIndex
- * @param {!shaka.media.SegmentReference} reference
- * @return {!Promise}
- * @private
- */
-shaka.media.StreamingEngine.prototype.handleDrift_ = function(
- mediaState, playheadTime, currentPeriodIndex, reference) {
- if (mediaState.drift != null)
- return Promise.resolve();
-
- var logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
- var currentPeriod = this.manifest_.periods[currentPeriodIndex];
-
- var bufferStart = this.mediaSourceEngine_.bufferStart(mediaState.type);
- if (bufferStart == null) {
- // The segment did not contain any actual media content.
- shaka.log.error(logPrefix, 'bad segment');
- return Promise.reject(new shaka.util.Error(
- shaka.util.Error.Category.STREAMING,
- shaka.util.Error.Code.BAD_SEGMENT,
- mediaState.type));
- }
-
- mediaState.drift =
- bufferStart - reference.startTime - currentPeriod.startTime;
- shaka.log.debug(logPrefix, 'drift=', mediaState.drift);
-
- // If there is positive drift or large negative drift then the playhead
- // may not be within the segment we just appended.
- var bufferedAhead = this.mediaSourceEngine_.bufferedAheadOf(
- mediaState.type, playheadTime);
- if (bufferedAhead == 0) {
- // Clear the buffer and try again.
- shaka.log.debug(logPrefix,
- 'playhead outside first segment:',
- 'bufferStart=' + bufferStart);
- mediaState.waitingToClearBuffer = true;
- }
-
- return Promise.resolve();
-};
-
-
/**
* Sets up all known Periods when startup completes; otherwise, does nothing.
*
@@ -1346,17 +1314,8 @@ shaka.media.StreamingEngine.prototype.handleStartup_ = function(
var mediaStates = MapUtils.values(this.mediaStates_);
this.startupComplete_ = mediaStates.every(function(ms) {
// Startup completes once we have buffered at least one segment from each
- // MediaState, have handled positive or large negative drift, and are not
- // clearing the buffer. Hence, the following three cases:
- // 1. if |drift| is null then we never appended anything;
- // 2. if |drift| is non-null but we're clearing the buffer then either
- // there was positive or large negative drift, or the user agent
- // seeked; and
- // 3. if |drift| is non-null and we're not clearing the buffer but the
- // buffer is empty then there was positive or large negative
- // drift but we never recovered.
- return ms.drift != null &&
- !ms.waitingToClearBuffer &&
+ // MediaState.
+ return !ms.waitingToClearBuffer &&
!ms.clearingBuffer &&
ms.lastSegmentReference;
});
diff --git a/lib/util/error.js b/lib/util/error.js
index 580a7cb0c7..f7f376d7d6 100644
--- a/lib/util/error.js
+++ b/lib/util/error.js
@@ -347,7 +347,6 @@ shaka.util.Error.Code = {
*/
'UNPLAYABLE_PERIOD': 4011,
-
/**
* The StreamingEngine appended a segment but the SourceBuffer is empty, or
* the StreamingEngine removed all segments and the SourceBuffer is
@@ -359,49 +358,6 @@ shaka.util.Error.Code = {
*/
'INCONSISTENT_BUFFER_STATE': 5000,
- /**
- * The StreamingEngine cannot append the next segment because the segment's
- * corresponding SegmentReference does not exist (i.e., findSegmentPosition()
- * succeeded but getSegmentReference() failed) or the segment's corresponding
- * SegmentReference has an invalid time range.
- *
- * This is a non-recoverable error.
- *
- *
error.data[0] is the type of content which caused the error.
- *
error.data[1] is the index of the Period.
- *
error.data[2] is the position of the segment.
- */
- 'INVALID_SEGMENT_INDEX': 5001,
-
- /**
- * The StreamingEngine cannot append the next segment because the next
- * segment does not exist (i.e., findSegmentPosition() failed). This can
- * occur for three reasons:
- * 1. there is positive drift (the segments' timestamps are ahead of
- * the manifest's timestamps) and the playhead is outside the drifted
- * segment availability window.
- * 2. the manifest is not updating fast enough for live presentations; or
- * 3. the manifest is not complete.
- * The first case is a recoverable error; recovery may be attempted by
- * repositioning the playhead under a segment.
- *
- *
error.data[0] is the type of content which caused the error.
- *
error.data[1] is the index of the Period.
- *
error.data[2] is the timestamp needed.
- */
- 'SEGMENT_DOES_NOT_EXIST': 5002,
-
- /**
- * The StreamingEngine inserted a media segment, but the segment did not
- * contain any actual media content.
- *
- * This is likely a non-recoverable error; however, recovery may be attempted
- * by seeking forwards or backwards (e.g., nudging the playhead).
- *
- *
error.data[0] is the type of content which caused the error.
- */
- 'BAD_SEGMENT': 5004,
-
/**
* The StreamingEngine called onChooseStreams() but the callback receiver
* did not return the correct number or type of Streams.
diff --git a/test/streaming_engine_integration.js b/test/streaming_engine_integration.js
index f1a89e8507..076891c10b 100644
--- a/test/streaming_engine_integration.js
+++ b/test/streaming_engine_integration.js
@@ -440,9 +440,10 @@ describe('StreamingEngine', function() {
onStartupComplete.and.callFake(function() {
// firstSegmentNumber =
// [(segmentAvailabilityEnd - rebufferingGoal) / segmentDuration] + 1
+ // Then -1 to account for drift safe buffering.
var segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
- netEngine.expectRequest('1_video_24', segmentType);
- netEngine.expectRequest('1_audio_29', segmentType);
+ netEngine.expectRequest('1_video_23', segmentType);
+ netEngine.expectRequest('1_audio_28', segmentType);
video.play();
});
diff --git a/test/streaming_engine_unit.js b/test/streaming_engine_unit.js
index 3bcae98618..5e9b8c3cd9 100644
--- a/test/streaming_engine_unit.js
+++ b/test/streaming_engine_unit.js
@@ -531,15 +531,15 @@ describe('StreamingEngine', function() {
text: []
});
- // Since we started playback from segment 11, the first 10 segments
- // should not be buffered.
- for (var i = 0; i <= 9; ++i) {
+ // Since we started playback from segment 11, segments 10 through 14
+ // should be buffered.
+ for (var i = 0; i <= 8; ++i) {
expect(mediaSourceEngine.segments.audio[i]).toBeFalsy();
expect(mediaSourceEngine.segments.video[i]).toBeFalsy();
expect(mediaSourceEngine.segments.text[i]).toBeFalsy();
}
- for (var i = 10; i <= 13; ++i) {
+ for (var i = 9; i <= 13; ++i) {
expect(mediaSourceEngine.segments.audio[i]).toBeTruthy();
expect(mediaSourceEngine.segments.video[i]).toBeTruthy();
expect(mediaSourceEngine.segments.text[i]).toBeTruthy();
@@ -866,9 +866,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, false, false],
- video: [false, true, false, false],
- text: [false, true, false, false]
+ audio: [true, true, false, false],
+ video: [true, true, false, false],
+ text: [true, true, false, false]
});
onChooseStreams.and.throwError(new Error());
@@ -891,9 +891,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, true, true],
- video: [false, true, true, true],
- text: [false, true, true, true]
+ audio: [true, true, true, true],
+ video: [true, true, true, true],
+ text: [true, true, true, true]
});
return streamingEngine.destroy();
@@ -955,9 +955,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, false, false],
- video: [false, true, false, false],
- text: [false, true, false, false]
+ audio: [true, true, false, false],
+ video: [true, true, false, false],
+ text: [true, true, false, false]
});
onChooseStreams.and.throwError(new Error());
@@ -985,9 +985,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, true, true],
- video: [false, true, true, true],
- text: [false, true, true, true]
+ audio: [true, true, true, true],
+ video: [true, true, true, true],
+ text: [true, true, true, true]
});
return streamingEngine.destroy();
@@ -1024,9 +1024,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, false, false],
- video: [false, true, false, false],
- text: [false, true, false, false]
+ audio: [true, true, false, false],
+ video: [true, true, false, false],
+ text: [true, true, false, false]
});
onChooseStreams.and.throwError(new Error());
@@ -1053,9 +1053,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, true, true, true],
- video: [false, true, true, true],
- text: [false, true, true, true]
+ audio: [true, true, true, true],
+ video: [true, true, true, true],
+ text: [true, true, true, true]
});
return streamingEngine.destroy();
@@ -1100,9 +1100,9 @@ describe('StreamingEngine', function() {
text: []
});
expect(mediaSourceEngine.segments).toEqual({
- audio: [false, false, false, true],
- video: [false, false, false, true],
- text: [false, false, false, true]
+ audio: [false, false, true, true],
+ video: [false, false, true, true],
+ text: [false, false, true, true]
});
return streamingEngine.destroy();
@@ -1171,7 +1171,10 @@ describe('StreamingEngine', function() {
});
it('outside segment availability window', function(done) {
- playhead.getTime.and.returnValue(100);
+ timeline.segmentAvailabilityStart = 90;
+ timeline.segmentAvailabilityEnd = 110;
+
+ playhead.getTime.and.returnValue(90);
onChooseStreams.and.callFake(function(period) {
expect(period).toBe(manifest.periods[0]);
@@ -1183,16 +1186,15 @@ describe('StreamingEngine', function() {
});
onStartupComplete.and.callFake(function() {
- setupFakeGetTime(100);
+ setupFakeGetTime(90);
// Seek forward to an unbuffered and unavailable region in the second
- // Period (note: |availabilityEnd| defaults to 120).
- // Set playing to false since the playhead can't move at the seek
- // target.
- expect(playhead.getTime()).toBe(100);
- expect(timeline.getSegmentAvailabilityStart()).toBe(100);
- expect(timeline.getSegmentAvailabilityEnd()).toBe(120);
- playheadTime += 25;
+ // Period; set playing to false since the playhead can't move at the
+ // seek target.
+ expect(playhead.getTime()).toBe(90);
+ expect(timeline.getSegmentAvailabilityStart()).toBe(90);
+ expect(timeline.getSegmentAvailabilityEnd()).toBe(110);
+ playheadTime += 35;
playing = false;
streamingEngine.seeked();
@@ -1205,15 +1207,16 @@ describe('StreamingEngine', function() {
return defaultOnChooseStreams(period);
});
- // Eventually StreamingEngine should request the first segment of the
- // second Period when it becomes available.
+ // Eventually StreamingEngine should request the first segment (since
+ // it needs the second segment) of the second Period when it becomes
+ // available.
var originalAppendBuffer =
shaka.test.FakeMediaSourceEngine.prototype.appendBuffer;
mediaSourceEngine.appendBuffer.and.callFake(
function(type, data, startTime, endTime) {
expect(playhead.getTime()).toBe(125);
- expect(timeline.getSegmentAvailabilityStart()).toBe(105);
- expect(timeline.getSegmentAvailabilityEnd()).toBe(125);
+ expect(timeline.getSegmentAvailabilityStart()).toBe(100);
+ expect(timeline.getSegmentAvailabilityEnd()).toBe(120);
playing = true;
var p = originalAppendBuffer.call(
mediaSourceEngine, type, data, startTime, endTime);
@@ -1475,7 +1478,6 @@ describe('StreamingEngine', function() {
});
});
- // TODO: Add tests for eviction with drift.
describe('VOD drift', function() {
beforeEach(function() {
setupVod();
@@ -1587,38 +1589,23 @@ describe('StreamingEngine', function() {
expect(mediaSourceEngine.endOfStream).toHaveBeenCalled();
// Verify buffers.
- // Note: after appending segment 11 of the first Period,
- // StreamingEngine should clear the buffer and then append segment 12
- // of the first Period as the playhead is under that Segment when
- // accounting for drift. When transitioning into the second Period,
- // StreamingEngine should append the second segment of the second
- // Period. It should not append the first segment of the second Period
- // since that segment exists entirely outside the second Period's
- // boundaries.
expect(mediaSourceEngine.initSegments).toEqual({
audio: [false, true],
video: [false, true],
text: []
});
- for (var i = 0; i <= 10; ++i) {
+ for (var i = 0; i <= 8; ++i) {
expect(mediaSourceEngine.segments.audio[i]).toBeFalsy();
expect(mediaSourceEngine.segments.video[i]).toBeFalsy();
expect(mediaSourceEngine.segments.text[i]).toBeFalsy();
}
- // Second segment of first Period.
- expect(mediaSourceEngine.segments.audio[11]).toBeTruthy();
- expect(mediaSourceEngine.segments.video[11]).toBeTruthy();
- expect(mediaSourceEngine.segments.text[11]).toBeTruthy();
-
- expect(mediaSourceEngine.segments.audio[12]).toBeFalsy();
- expect(mediaSourceEngine.segments.video[12]).toBeFalsy();
- expect(mediaSourceEngine.segments.text[12]).toBeFalsy();
-
- expect(mediaSourceEngine.segments.audio[13]).toBeTruthy();
- expect(mediaSourceEngine.segments.video[13]).toBeTruthy();
- expect(mediaSourceEngine.segments.text[13]).toBeTruthy();
+ for (var i = 9; i <= 13; ++i) {
+ expect(mediaSourceEngine.segments.audio[i]).toBeTruthy();
+ expect(mediaSourceEngine.segments.video[i]).toBeTruthy();
+ expect(mediaSourceEngine.segments.text[i]).toBeTruthy();
+ }
return streamingEngine.destroy();
}).catch(fail).then(done);