Skip to content

Commit

Permalink
Remove position from SegmentReference
Browse files Browse the repository at this point in the history
As part of Period-flattening, I'm trying to remove our dependence on
the "position" field of SegmentReference.  With that eliminated, we
can more easily concatenate Arrays of SegmentReferences without
modifying them.

 - Make SegmentIndex iterable
 - Add specialized seek() and current() methods to SegmentIterator
 - Remove position from SegmentReference
 - Make positions in SegmentIndex API stable without field in
   reference
 - Remove brittle hard-coded positions in tests (except SegmentIndex
   tests, where they would be hard to avoid in testing methods
   separately)
 - Use SegmentIterator in StreamingEngine to track the next segment
   between switches

Issue #892 (refactor StreamingEngine)
Issue #1339 (period flattening)

Change-Id: I666cc21249c34ee6cbc138a59109d9f1159fa127
  • Loading branch information
joeyparrish committed Mar 23, 2020
1 parent 6202b93 commit 1ab3f9c
Show file tree
Hide file tree
Showing 32 changed files with 639 additions and 515 deletions.
2 changes: 1 addition & 1 deletion build/eslint-plugin-shaka-rules/private.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module.exports = {
return {
'ClassBody > MethodDefinition': (node) => {
const comment = source.getCommentsBefore(node).pop();
const nameIsPrivate = node.key.name.endsWith('_');
const nameIsPrivate = node.key.name && node.key.name.endsWith('_');
if (!comment) {
if (nameIsPrivate) {
ctx.report({
Expand Down
5 changes: 2 additions & 3 deletions docs/tutorials/manifest-parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,12 @@ It handles merging new segments, and expanding the list of segments for live
streams.

```js
var references = refs.map(function(r, position) {
var references = refs.map(function(r) {
// Should return an array of possible URI choices; this is used for failover
// in the event of network error. This is a function to defer calculations.
var getUris = function() { return [r.uri]; };

return new shaka.media.SegmentReference(
position,
r.start, r.end, getUris,
/* startByte */ 0,
/* endByte */ null,
Expand Down Expand Up @@ -329,7 +328,7 @@ MyManifestParser.prototype.loadReference_ =
function(position, start, end, initSegmentReference) {
var getUris = function() { return ['https://example.com/ref_' + position]; };
return new shaka.media.SegmentReference(
position, start, end, getUris,
start, end, getUris,
/* startByte */ 0,
/* endByte */ null,
initSegmentReference,
Expand Down
1 change: 0 additions & 1 deletion lib/dash/segment_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ shaka.dash.SegmentList = class {
const getUris = () => mediaUri;
references.push(
new shaka.media.SegmentReference(
i + startNumber,
periodStart + startTime,
periodStart + endTime,
getUris,
Expand Down
2 changes: 0 additions & 2 deletions lib/dash/segment_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,6 @@ shaka.dash.SegmentTemplate = class {
'Generated a segment outside of the period!');

return new shaka.media.SegmentReference(
position,
segmentStart,
segmentEnd,
getUris,
Expand Down Expand Up @@ -471,7 +470,6 @@ shaka.dash.SegmentTemplate = class {
};

references.push(new shaka.media.SegmentReference(
segmentReplacement,
periodStart + start,
periodStart + end,
createUris,
Expand Down
1 change: 0 additions & 1 deletion lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,6 @@ shaka.hls.HlsParser = class {
}

return new shaka.media.SegmentReference(
position,
startTime,
endTime,
() => [absoluteSegmentUri],
Expand Down
1 change: 0 additions & 1 deletion lib/media/mp4_segment_index_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ shaka.media.Mp4SegmentIndexParser = class {

references.push(
new shaka.media.SegmentReference(
references.length,
nativeStartTime + timestampOffset,
nativeEndTime + timestampOffset,
(() => { return uris; }),
Expand Down
110 changes: 93 additions & 17 deletions lib/media/segment_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

goog.provide('shaka.media.SegmentIndex');
goog.provide('shaka.media.SegmentIterator');

goog.require('goog.asserts');
goog.require('shaka.media.SegmentReference');
Expand All @@ -15,14 +16,14 @@ goog.require('shaka.util.Timer');
* SegmentIndex.
*
* @implements {shaka.util.IDestroyable}
* @implements {Iterable.<!shaka.media.SegmentReference>}
* @export
*/
shaka.media.SegmentIndex = class {
/**
* @param {!Array.<!shaka.media.SegmentReference>} references The list of
* SegmentReferences, which must be sorted first by their start times
* (ascending) and second by their end times (ascending). They must have
* continuous, increasing positions.
* (ascending) and second by their end times (ascending).
*/
constructor(references) {
if (goog.DEBUG) {
Expand All @@ -34,6 +35,14 @@ shaka.media.SegmentIndex = class {

/** @private {shaka.util.Timer} */
this.timer_ = null;

/**
* The number of references that have been removed from the front of the
* array. Used to create stable positions in the find/get APIs.
*
* @private {number}
*/
this.numEvicted_ = 0;
}


Expand All @@ -60,8 +69,8 @@ shaka.media.SegmentIndex = class {
* time.
*
* @param {number} time
* @return {?number} The position of the segment, or null
* if the position of the segment could not be determined.
* @return {?number} The position of the segment, or null if the position of
* the segment could not be determined.
* @export
*/
find(time) {
Expand All @@ -72,11 +81,11 @@ shaka.media.SegmentIndex = class {
const r = this.references_[i];
// Note that a segment ends immediately before the end time.
if ((time >= r.startTime) && (time < r.endTime)) {
return r.position;
return i + this.numEvicted_;
}
}
if (this.references_.length && time < this.references_[0].startTime) {
return this.references_[0].position;
return this.numEvicted_;
}

return null;
Expand All @@ -86,7 +95,7 @@ shaka.media.SegmentIndex = class {
/**
* Gets the SegmentReference for the segment at the given position.
*
* @param {number} position The position of the segment.
* @param {number} position The position of the segment as returned by find().
* @return {shaka.media.SegmentReference} The SegmentReference, or null if
* no such SegmentReference exists.
* @export
Expand All @@ -96,7 +105,7 @@ shaka.media.SegmentIndex = class {
return null;
}

const index = position - this.references_[0].position;
const index = position - this.numEvicted_;
if (index < 0 || index >= this.references_.length) {
return null;
}
Expand Down Expand Up @@ -127,8 +136,7 @@ shaka.media.SegmentIndex = class {
*
* @param {!Array.<!shaka.media.SegmentReference>} references The list of
* SegmentReferences, which must be sorted first by their start times
* (ascending) and second by their end times (ascending). They must have
* continuous, increasing positions.
* (ascending) and second by their end times (ascending).
* @export
*/
merge(references) {
Expand Down Expand Up @@ -188,7 +196,16 @@ shaka.media.SegmentIndex = class {
* @export
*/
evict(time) {
const oldSize = this.references_.length;

this.references_ = this.references_.filter((ref) => ref.endTime > time);

const newSize = this.references_.length;
const diff = oldSize - newSize;

// Tracking the number of evicted refs will keep their "positions" stable
// for the caller.
this.numEvicted_ += diff;
}


Expand Down Expand Up @@ -224,6 +241,7 @@ shaka.media.SegmentIndex = class {
const firstReference = this.references_[0];
if (firstReference.endTime <= periodStart) {
this.references_.shift();
this.numEvicted_++;
} else {
break;
}
Expand All @@ -237,7 +255,6 @@ shaka.media.SegmentIndex = class {
const lastReference = this.references_[this.references_.length - 1];
this.references_[this.references_.length - 1] =
new shaka.media.SegmentReference(
lastReference.position,
lastReference.startTime,
/* endTime= */ periodEnd,
lastReference.getUris,
Expand Down Expand Up @@ -272,6 +289,12 @@ shaka.media.SegmentIndex = class {
}


/** @return {!shaka.media.SegmentIterator} */
[Symbol.iterator]() {
return new shaka.media.SegmentIterator(this);
}


/**
* Create a SegmentIndex for a single segment of the given start time and
* duration at the given URIs.
Expand All @@ -284,7 +307,6 @@ shaka.media.SegmentIndex = class {
*/
static forSingleSegment(startTime, duration, uris) {
const reference = new shaka.media.SegmentReference(
/* position= */ 1,
/* startTime= */ startTime,
/* endTime= */ startTime + duration,
/* getUris= */ () => uris,
Expand All @@ -301,8 +323,7 @@ shaka.media.SegmentIndex = class {

if (goog.DEBUG) {
/**
* Asserts that the given SegmentReferences are sorted and have continuous,
* increasing positions.
* Asserts that the given SegmentReferences are sorted.
*
* @param {!Array.<shaka.media.SegmentReference>} references
* @private
Expand All @@ -313,9 +334,6 @@ if (goog.DEBUG) {
return true;
}
const r1 = references[i - 1];
if (r2.position != r1.position + 1) {
return false;
}
if (r1.startTime < r2.startTime) {
return true;
} else if (r1.startTime > r2.startTime) {
Expand All @@ -331,3 +349,61 @@ if (goog.DEBUG) {
};
}


/**
* An iterator over a SegmentIndex's references.
*
* @implements {Iterator.<shaka.media.SegmentReference>}
* @export
*/
shaka.media.SegmentIterator = class {
/** @param {shaka.media.SegmentIndex} segmentIndex */
constructor(segmentIndex) {
/** @private {shaka.media.SegmentIndex} */
this.segmentIndex_ = segmentIndex;

/** @private {number} */
this.nextPosition_ = 0;
}

/**
* Move the iterator to a given timestamp in the underlying SegmentIndex.
*
* @param {number} time
* @return {shaka.media.SegmentReference}
* @export
*/
seek(time) {
const position = this.segmentIndex_.find(time);
if (position == null) {
// An arbitrary, large number whose position will not find anything in the
// segment index, even when incremented.
this.nextPosition_ = 2**31;
return null;
}

this.nextPosition_ = position;
// Post-increment so that next() returns the one after this.
return this.segmentIndex_.get(this.nextPosition_++);
}

/**
* @return {shaka.media.SegmentReference}
* @export
*/
current() {
return this.segmentIndex_.get(this.nextPosition_ - 1);
}

/**
* @override
* @export
*/
next() {
const ref = this.segmentIndex_.get(this.nextPosition_++);
return {
'value': ref,
'done': !ref,
};
}
};
24 changes: 2 additions & 22 deletions lib/media/segment_reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ shaka.media.InitSegmentReference = class {
*/
shaka.media.SegmentReference = class {
/**
* @param {number} position The segment's position.
* The following should hold true between any two SegmentReferences r1 and
* r2:
* IF r2.position > r1.position THEN
* [ (r2.startTime > r1.startTime) OR
* (r2.startTime == r1.startTime AND r2.endTime >= r1.endTime) ]
* @param {number} startTime The segment's start time in seconds.
* @param {number} endTime The segment's end time in seconds. The segment
* ends the instant before this time, so |endTime| must be strictly greater
Expand Down Expand Up @@ -123,17 +117,13 @@ shaka.media.SegmentReference = class {
* MediaSource.
*/
constructor(
position, startTime, endTime, uris, startByte, endByte,
initSegmentReference, timestampOffset, appendWindowStart,
appendWindowEnd) {
startTime, endTime, uris, startByte, endByte, initSegmentReference,
timestampOffset, appendWindowStart, appendWindowEnd) {
goog.asserts.assert(startTime < endTime,
'startTime must be less than endTime');
goog.asserts.assert((startByte < endByte) || (endByte == null),
'startByte must be < endByte');

/** @const {number} */
this.position = position;

/** @type {number} */
this.startTime = startTime;

Expand Down Expand Up @@ -162,16 +152,6 @@ shaka.media.SegmentReference = class {
this.appendWindowEnd = appendWindowEnd;
}

/**
* Returns the segment's position.
*
* @return {number} The segment's position.
* @export
*/
getPosition() {
return this.position;
}

/**
* Returns the segment's start time in seconds.
*
Expand Down
Loading

0 comments on commit 1ab3f9c

Please sign in to comment.