Skip to content

Commit

Permalink
fix: Fix potential AV sync issues after seek or adaptation (#4886)
Browse files Browse the repository at this point in the history
After seeking or adaptation, StreamingEngine will create a new
SegmentIterator. However, if the buffered ranges are different enough
from the references in the iterator, the lookup in the index can produce
a duplicate segment. If this happens when sequence mode is active,
StreamingEngine will append the duplicate segment instead of overwriting
the end of the original one.

Any time a new iterator is created, we force the stream to resync, so
that unaligned segments or slightly-inaccurate segments are written to
the correct place in the buffer.

Issue #4589
  • Loading branch information
joeyparrish committed Jan 13, 2023
1 parent 13d29fd commit ac3afb4
Showing 1 changed file with 15 additions and 3 deletions.
18 changes: 15 additions & 3 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ shaka.media.StreamingEngine = class {
// The playhead might be seeking on startup, if a start time is set, so
// start "seeked" as true.
seeked: true,
needsResync: false,
recovering: false,
hasError: false,
operation: null,
Expand Down Expand Up @@ -1155,6 +1156,11 @@ shaka.media.StreamingEngine = class {
shaka.log.v1(
logPrefix, 'looking up segment from new stream endTime:', time);

// Using a new iterator means we need to resync the stream in sequence
// mode. The buffered range might not align perfectly with the last
// segment end time, so we may end up repeating a segment. Resyncing
// makes this safe to do.
mediaState.needsResync = true;
mediaState.segmentIterator =
mediaState.stream.segmentIndex.getIteratorForTime(time);
const ref = mediaState.segmentIterator &&
Expand Down Expand Up @@ -1660,7 +1666,9 @@ shaka.media.StreamingEngine = class {
const lastDiscontinuitySequence =
mediaState.lastSegmentReference ?
mediaState.lastSegmentReference.discontinuitySequence : null;
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
if (reference.discontinuitySequence != lastDiscontinuitySequence ||
mediaState.needsResync) {
mediaState.needsResync = false;
operations.push(this.playerInterface_.mediaSourceEngine.resync(
mediaState.type, reference.startTime));
}
Expand Down Expand Up @@ -2151,6 +2159,7 @@ shaka.media.StreamingEngine.PlayerInterface;
* clearBufferSafeMargin: number,
* clearingBuffer: boolean,
* seeked: boolean,
* needsResync: boolean,
* adaptation: boolean,
* recovering: boolean,
* hasError: boolean,
Expand Down Expand Up @@ -2196,10 +2205,13 @@ shaka.media.StreamingEngine.PlayerInterface;
* The amount of buffer to retain when clearing the buffer after the update.
* @property {boolean} clearingBuffer
* True indicates that the buffer is being cleared.
* @property {boolean} seeked
* @property {boolean} seeked
* True indicates that the presentation just seeked.
* @property {boolean} adaptation
* @property {boolean} adaptation
* True indicates that the presentation just automatically switched variants.
* @property {boolean} needsResync
* True indicates that the stream needs to be resynced in sequence mode,
* regardless of discontinuity sequence.
* @property {boolean} recovering
* True indicates that the last segment was not appended because it could not
* fit in the buffer.
Expand Down

0 comments on commit ac3afb4

Please sign in to comment.