Skip to content

Commit

Permalink
fix(Tizen): Fix stalling playback on Tizen 3
Browse files Browse the repository at this point in the history
Playback stalls on Tizen can be corrected by pausing and playing
again.  For Tizen and similar platforms, this is superior to the old
way of seeking to unstall playback, because seeking is expensive on
those platforms.

To facilitate pausing and playing again instead of seeking, a
stallSkip value of 0 will trigger the pause/play workaround instead of
the seeking workaround.  Further, we will make this the default on
Tizen, WebOS, and Chromecast.

An internal detail which is changing is that GapJumpingController will
now call StallDetector first and inhibit gap jumping when the stall
detector has something to do, rather than the other way around.  This
allows the StallDetector to be in charge of deciding what is buffered,
rather than having to pass GapJumpingController's interpretation of
buffered ranges first.

Fixes #2620

Change-Id: I5ad7aad42aa185b2837c830970e54218b4e55a21
  • Loading branch information
joeyparrish committed Jul 23, 2020
1 parent 892da72 commit f5d58f6
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 19 deletions.
11 changes: 7 additions & 4 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -785,15 +785,18 @@ shaka.extern.ManifestConfiguration;
* jump in the stream skipping more content. This is helpful for lower
* bandwidth scenarios. Defaults to 5 if not provided.
* @property {boolean} stallEnabled
* When set to <code>true</code>, the stall detector logic will run, skipping
* forward <code>stallSkip</code> seconds whenever the playhead stops moving
* for <code>stallThreshold</code> seconds.
* When set to <code>true</code>, the stall detector logic will run. If the
* playhead stops moving for <code>stallThreshold</code> seconds, the player
* will either seek or pause/play to resolve the stall, depending on the value
* of <code>stallSkip</code>.
* @property {number} stallThreshold
* The maximum number of seconds that may elapse without the playhead moving
* (when playback is expected) before it will be labeled as a stall.
* @property {number} stallSkip
* The number of seconds that the player will skip forward when a stall has
* been detected.
* been detected. If 0, the player will pause and immediately play instead of
* seeking. A value of 0 is recommended and provided as default on TV
* platforms (WebOS, Tizen, Chromecast, etc).
* @property {boolean} useNativeHlsOnSafari
* Desktop Safari has both MediaSource and their native HLS implementation.
* Depending on the application's needs, it may prefer one over the other.
Expand Down
10 changes: 6 additions & 4 deletions lib/media/gap_jumping_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ shaka.media.GapJumpingController = class {
this.prevReadyState_ = this.video_.readyState;
}

if (this.stallDetector_ && this.stallDetector_.poll()) {
// Some action was taken by StallDetector, so don't do anything yet.
return;
}


const smallGapLimit = this.config_.smallGapLimit;
const currentTime = this.video_.currentTime;
const buffered = this.video_.buffered;
Expand All @@ -175,10 +181,6 @@ shaka.media.GapJumpingController = class {

// The current time is unbuffered or is too far from a gap.
if (gapIndex == null) {
if (this.stallDetector_) {
this.stallDetector_.poll();
}

return;
}

Expand Down
14 changes: 9 additions & 5 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,16 @@ shaka.media.MediaSourcePlayhead = class {
threshold);

detector.onStall((at, duration) => {
shaka.log.debug([
'Stall detected at', at, 'for', duration, 'seconds. Seeking forward',
skip, 'seconds.',
].join(' '));
shaka.log.debug(`Stall detected at ${at} for ${duration} seconds.`);

mediaElement.currentTime += skip;
if (skip) {
shaka.log.debug(`Seeking forward ${skip} seconds to break stall.`);
mediaElement.currentTime += skip;
} else {
shaka.log.debug('Pausing and unpausing to break stall.');
mediaElement.pause();
mediaElement.play();
}
});

return detector;
Expand Down
8 changes: 7 additions & 1 deletion lib/media/stall_detector.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ shaka.media.StallDetector = class {
/**
* Have the detector update itself and fire the "on stall" callback if a stall
* was detected.
*
* @return {boolean} True if action was taken.
*/
poll() {
const impl = this.implementation_;
Expand Down Expand Up @@ -99,6 +101,8 @@ shaka.media.StallDetector = class {
// value so we don't think that was an update.
this.value_ = impl.getPresentationSeconds();
}

return triggerCallback;
}
};

Expand Down Expand Up @@ -191,9 +195,11 @@ shaka.media.StallDetector.MediaElementImplementation = class {
static hasContentFor_(buffered, timeInSeconds) {
const TimeRangesUtils = shaka.media.TimeRangesUtils;
for (const {start, end} of TimeRangesUtils.getBufferedInfo(buffered)) {
if (timeInSeconds < start) {
// Can be as much as 100ms before the range
if (timeInSeconds < start - 0.1) {
continue;
}
// Must be at least 500ms inside the range
if (timeInSeconds > end - 0.5) {
continue;
}
Expand Down
13 changes: 8 additions & 5 deletions lib/util/player_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,14 @@ shaka.util.PlayerConfiguration = class {
inaccurateManifestTolerance: 2,
};

// WebOS has a long hardware pipeline that responds slowly, making it easy
// to misidentify stalls. To avoid this, by default disable stall detection
// on WebOS.
if (shaka.util.Platform.isWebOS()) {
streaming.stallEnabled = false;
// WebOS, Tizen, and Chromecast have long hardware pipelines that respond
// slowly to seeking. Therefore we should not seek when we detect a stall
// on one of these platforms. Instead, default stallSkip to 0 to force the
// stall detector to pause and play instead.
if (shaka.util.Platform.isWebOS() ||
shaka.util.Platform.isTizen() ||
shaka.util.Platform.isChromecast()) {
streaming.stallSkip = 0;
}

const offline = {
Expand Down

0 comments on commit f5d58f6

Please sign in to comment.