Skip to content

Commit

Permalink
Move next-segment tracking to SegmentIndex
Browse files Browse the repository at this point in the history
As part of Period-flattening, I'm trying to reduce our dependence on
the "position" field of SegmentReference.  If it can be eliminated, we
can more easily concatenate Arrays of SegmentReferences without
modifying them.

SegmentIndex can now track the last reference you asked for and
iterate through the list of references.  This means we don't need the
"position" field of SegmentReference, which means we don't need to
know positions in advance or globally.  StreamingEngine will no longer
use position to request segments.

The old methods find(time):position and get(position):SegmentReference
have been replaced with seek(time), current(), and next(), all of
which return a SegmentReference and maintain an internal pointer to
the "current" reference.  Care has been taken to maintain that pointer
during the evict() and fit() operations.  Recent changes to merge()
made sure that the pointer does not need to change during that
operation.

All test updates are related to the SegmentIndex API change, not
changing expectations or behavior.

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

Change-Id: I1682dcc2dd625c6e390711538e46d31e6eb6cea8
  • Loading branch information
joeyparrish committed Mar 11, 2020
1 parent 3cdb9c8 commit 235e4e1
Show file tree
Hide file tree
Showing 19 changed files with 334 additions and 327 deletions.
119 changes: 92 additions & 27 deletions lib/media/segment_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,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).
*/
constructor(references) {
if (goog.DEBUG) {
Expand All @@ -34,6 +33,9 @@ shaka.media.SegmentIndex = class {

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

/** @protected {number} */
this.currentIndex_ = 0;
}


Expand All @@ -54,54 +56,69 @@ shaka.media.SegmentIndex = class {


/**
* Finds the position of the segment for the given time, in seconds, relative
* to the start of the presentation. Returns the position of the segment
* with the largest end time if more than one segment is known for the given
* time.
* Moves the internal pointer to the current reference so that it points to
* the segment reference for the given time, in seconds, relative to the start
* of the presentation.
*
* See also current() and next(), which can be used to iterate sequentially
* through the references after a seek().
*
* This should only be used by StreamingEngine.
*
* @param {number} time
* @return {?number} The position of the segment, or null
* if the position of the segment could not be determined.
* @return {shaka.media.SegmentReference} The SegmentReference, or null if
* no such SegmentReference exists.
* @export
*/
find(time) {
// For live streams, searching from the end is faster. For VOD, it balances
// out either way. In both cases, references_.length is small enough that
// the difference isn't huge.
seek(time) {
// For live streams, searching from the end is typically faster. For VOD,
// it balances out either way on average. In both cases, references_.length
// is small enough that the difference isn't huge.
for (let i = this.references_.length - 1; i >= 0; --i) {
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;
this.currentIndex_ = i;
return r;
}
}
if (this.references_.length && time < this.references_[0].startTime) {
return this.references_[0].position;
this.currentIndex_ = 0;
return this.references_[0];
}

this.currentIndex_ = this.references_.length;
return null;
}


/**
* Gets the SegmentReference for the segment at the given position.
* Return the current SegmentReference according to the internal pointer
* maintained by seek() and next().
*
* @param {number} position The position of the segment.
* @return {shaka.media.SegmentReference} The SegmentReference, or null if
* no such SegmentReference exists.
* This should only be used by StreamingEngine.
*
* @return {shaka.media.SegmentReference} The current SegmentReference, or
* null if no such SegmentReference exists.
* @export
*/
get(position) {
if (this.references_.length == 0) {
return null;
}
current() {
return this.references_[this.currentIndex_] || null;
}

const index = position - this.references_[0].position;
if (index < 0 || index >= this.references_.length) {
return null;
}

return this.references_[index];
/**
* Advances the internal pointer to the next segment.
*
* This should only be used by StreamingEngine.
*
* @return {shaka.media.SegmentReference} The next SegmentReference, or
* null if no such SegmentReference exists.
* @export
*/
next() {
this.currentIndex_++;
return this.current();
}


Expand Down Expand Up @@ -188,7 +205,30 @@ shaka.media.SegmentIndex = class {
* @export
*/
evict(time) {
const oldSize = this.references_.length;
let oldCurrent;
if (goog.DEBUG) {
oldCurrent = this.references_[this.currentIndex_];
}

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

// Maintain the current index.
const newSize = this.references_.length;
const diff = oldSize - newSize;
this.currentIndex_ = this.currentIndex_ - diff;

// So long as the current reference wasn't evicted, this should hold.
if (goog.DEBUG && this.currentIndex_ >= 0) {
const newCurrent = this.references_[this.currentIndex_];
goog.asserts.assert(oldCurrent == newCurrent,
'Current segment reference changed on evict!');
}

// If the current reference was evicted, point to a valid reference.
if (this.currentIndex_ < 0) {
this.currentIndex_ = 0;
}
}


Expand All @@ -210,6 +250,11 @@ shaka.media.SegmentIndex = class {
goog.asserts.assert(periodEnd != Infinity,
'Period duration must be finite for static content!');

let oldCurrent;
if (goog.DEBUG) {
oldCurrent = this.references_[this.currentIndex_];
}

// Trim out references we will never use.
while (this.references_.length) {
const lastReference = this.references_[this.references_.length - 1];
Expand All @@ -224,11 +269,24 @@ shaka.media.SegmentIndex = class {
const firstReference = this.references_[0];
if (firstReference.endTime <= periodStart) {
this.references_.shift();
this.currentIndex_--;
} else {
break;
}
}

// So long as the current reference wasn't evicted, this should hold.
if (goog.DEBUG && this.currentIndex_ >= 0) {
const newCurrent = this.references_[this.currentIndex_];
goog.asserts.assert(oldCurrent == newCurrent,
'Current segment reference changed on evict!');
}

// If the current reference was evicted, point to a valid reference.
if (this.currentIndex_ < 0) {
this.currentIndex_ = 0;
}

if (this.references_.length == 0) {
return;
}
Expand All @@ -247,6 +305,10 @@ shaka.media.SegmentIndex = class {
lastReference.timestampOffset,
lastReference.appendWindowStart,
lastReference.appendWindowEnd);

if (goog.DEBUG) {
shaka.media.SegmentIndex.assertCorrectReferences_(this.references_);
}
}


Expand All @@ -267,6 +329,9 @@ shaka.media.SegmentIndex = class {
this.timer_.stop();
this.timer_ = null;
}
if (goog.DEBUG) {
shaka.media.SegmentIndex.assertCorrectReferences_(this.references_);
}
});
this.timer_.tickEvery(interval);
}
Expand Down
Loading

0 comments on commit 235e4e1

Please sign in to comment.