Skip to content

Commit

Permalink
fix: Fix buffering on the end of MSS streams
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Apr 27, 2023
1 parent 330f04b commit a394641
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 11 deletions.
25 changes: 14 additions & 11 deletions lib/mss/mss_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ shaka.mss.MssParser = class {
*/
this.updatePeriod_ = 0;

/** @private {?shaka.media.PresentationTimeline} */
this.presentationTimeline_ = null;

/**
* An ewma that tracks how long updates take.
* This is to mitigate issues caused by slow parsing on embedded devices.
Expand Down Expand Up @@ -286,12 +289,8 @@ shaka.mss.MssParser = class {
manifestPreprocessor(mss);
}

/** @type {!shaka.media.PresentationTimeline} */
let presentationTimeline;
if (this.manifest_) {
presentationTimeline = this.manifest_.presentationTimeline;
} else {
presentationTimeline = new shaka.media.PresentationTimeline(
if (!this.presentationTimeline_) {
this.presentationTimeline_ = new shaka.media.PresentationTimeline(
/* presentationStartTime= */ null, /* delay= */ 0);
}

Expand All @@ -305,7 +304,7 @@ shaka.mss.MssParser = class {
shaka.util.Error.Code.MSS_LIVE_CONTENT_NOT_SUPPORTED);
}

presentationTimeline.setStatic(!isLive);
this.presentationTimeline_.setStatic(!isLive);

const timescale = XmlUtils.parseAttr(mss, 'TimeScale',
XmlUtils.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
Expand Down Expand Up @@ -342,7 +341,7 @@ shaka.mss.MssParser = class {
segmentAvailabilityDuration = Infinity;
}

presentationTimeline.setSegmentAvailabilityDuration(
this.presentationTimeline_.setSegmentAvailabilityDuration(
segmentAvailabilityDuration);

// Duration in timescale units.
Expand All @@ -352,7 +351,7 @@ shaka.mss.MssParser = class {
'Duration must be defined!');

if (!isLive) {
presentationTimeline.setDuration(duration / timescale);
this.presentationTimeline_.setDuration(duration / timescale);
}

/** @type {!shaka.mss.MssParser.Context} */
Expand All @@ -368,7 +367,7 @@ shaka.mss.MssParser = class {
// These steps are not done on manifest update.
if (!this.manifest_) {
this.manifest_ = {
presentationTimeline: presentationTimeline,
presentationTimeline: this.presentationTimeline_,
variants: context.variants,
textStreams: context.textStreams,
imageStreams: [],
Expand All @@ -382,7 +381,7 @@ shaka.mss.MssParser = class {
// This is the first point where we have a meaningful presentation start
// time, and we need to tell PresentationTimeline that so that it can
// maintain consistency from here on.
presentationTimeline.lockStartTime();
this.presentationTimeline_.lockStartTime();
} else {
// Just update the variants and text streams.
this.manifest_.variants = context.variants;
Expand Down Expand Up @@ -491,6 +490,10 @@ shaka.mss.MssParser = class {
duration = end - start;
}

const presentationDuration = this.presentationTimeline_.getDuration();
this.presentationTimeline_.setDuration(
Math.min(duration, presentationDuration));

/** @type {!shaka.extern.Stream} */
const stream = {
id: id,
Expand Down
40 changes: 40 additions & 0 deletions test/mss/mss_player_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,46 @@ describe('MSS Player', () => {
// longer than 10 seconds, fail the test.
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 5, 10);

if (video.ended) {
checkEndedTime();
} else {
// Expect that in 30 seconds of playback, we go through at least 20
// seconds of content. This allows for some buffering or network
// flake.
expect(video.currentTime).toBeGreaterThan(5);

// Since video.ended is false, we expect the current time to be before
// the video duration.
expect(video.currentTime).toBeLessThan(video.duration);

// Seek close to the end and play the rest of the content.
video.currentTime = video.duration - 5;

// Wait for the video to start playback again after seeking. If it
// takes longer than 10 seconds, fail the test.
await waiter.waitForMovementOrFailOnTimeout(video, 10);

// Play for 20 seconds, but stop early if the video ends.
await waiter.waitForEndOrTimeout(video, 20);

checkEndedTime();
}

await player.unload();
});

/**
* Check the video time for videos that we expect to have ended.
*/
function checkEndedTime() {
if (video.currentTime >= video.duration) {
// On some platforms, currentTime surpasses duration by more than 1s.
// For the purposes of this test, this is fine, so don't set any precise
// expectations on currentTime if it's larger.
} else {
// On some platforms, currentTime is less than duration, but it should be
// close.
expect(video.currentTime).toBeGreaterThan(video.duration - 1);
}
}
});

0 comments on commit a394641

Please sign in to comment.