-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add safe margin parameter to avoid rebuffering interruptions when clearing the buffer #1154
Conversation
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
I signed it! |
CLAs look good, thanks! |
The trouble with this is that clearing media ahead of the playhead causes stuttering issues on some browsers. That is why we don't stopped doing that in v2. Have you tested this on multiple browsers? Which browsers does your application intend to support? |
Thanks a lot for your reply. These few days I've spent some time testing it on every browser for desktop. Issues occur when you clear too close to the playhead. As long as the margin is computed appropriately, I don't see any artifect or stuttering. Playback goes smoothly. Here's what I tried to do in detail. After computing (approximately) the next segment boundary, I tried clearing from that point on when switching variant. This caused big problems already (because of a lack of accuracy probably), so I tried adding 0.2 seconds to such margin, and it was enough to solve the issue on most browsers except mainly for Chrome. Finally, I tried to increase the margin by adding the duration of a segment, and I stopped getting stuttering altogether. As long as the network state is good though. I mean, the only stutters that I noticed have happened because of buffer depletion, but that's a different issue. Bottom line is: I think two segments worth of buffer is a safe minimum to consider when computing the margin; and then the final value should be computed according to the bandwidth estimate I guess, to avoid depletion. I have found the commit where the support was dropped: 385a501 p.s. I've tested with Chrome, Firefox (52 and 57), Safari, Opera, Edge, IE 11. I also tried different segment sizes: 1, 4 and 6 seconds. Tested under good network conditions, mainly. Some tests under bandwidth cap as well. |
Thank you for doing such detailed testing. At this point, our plates are pretty full for the rest of the year to finish v2.3.0. We will look into this PR in January when we start work on v2.4.0. Please accept our apologies for the delay, and thank you for your contribution! |
Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you still interested in this PR? If so please rebase on the latest master and fix any style issues with our new style requirements (run build/all.py
).
lib/media/streaming_engine.js
Outdated
@@ -991,7 +1002,8 @@ shaka.media.StreamingEngine.prototype.onUpdate_ = function(mediaState) { | |||
if (mediaState.waitingToClearBuffer) { | |||
// Note: clearBuffer_() will schedule the next update. | |||
shaka.log.debug(logPrefix, 'skipping update and clearing the buffer'); | |||
this.clearBuffer_(mediaState, mediaState.waitingToFlushBuffer); | |||
this.clearBuffer_(mediaState, mediaState.waitingToFlushBuffer, | |||
mediaState.deferredClearBufferOffset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style is to keep all the arguments aligned. So please do either:
this.clearBuffer_(mediaState, mediaState.waitingToFlushBuffer,
mediaState.deferredClearBufferOffset);
Or
this.clearBuffer_(
mediaState, mediaState.waitingToFlushBuffer,
mediaState.deferredClearBufferOffset);
Yes, absolutely, still interested! I'll get to work asap, thanks. |
…ufferings. A new optional parameter called safeMargin is introduced to avoid clearing the buffer entirely. The value of such offset should be computed in a custom ABR manager and provided to the player and the streaming engine. Since the buffer could be busy when clearBuffer is called, another parameter had to be introduced to save the value of the safe margin when the call to clear the buffer is deferred to a later update. When a safe margin value is provided, the clearBuffer method uses ‘remove’ as method to clear the buffer, and not the method ‘clear’.
Okay, I rebased and fixed the style related issues as requested. No conflicts, and build went fine. |
lib/media/streaming_engine.js
Outdated
p.then(function() { | ||
if (!this.destroyed_ && flush) { | ||
if (!this.destroyed_ && flush && safeMargin == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is dependent on safeMargin == 0
, you could put this as part of line 2191.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What did you have in mind here? Not sure how to move this without duplicating things after.
p.then(function() {
if (!this.destroyed_ && flush && safeMargin == 0) {
return this.playerInterface_.mediaSourceEngine.flush(mediaState.type);
}
}.bind(this)).then(function() {
if (this.destroyed_) return;
shaka.log.debug(logPrefix, 'cleared buffer');
mediaState.lastStream = null;
mediaState.lastSegmentReference = null;
mediaState.clearingBuffer = false;
mediaState.endOfStream = false;
this.scheduleUpdate_(mediaState, 0);
}.bind(this));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (safeMargin) {
p = ...
} else {
p = this.playerInterface_.mediaSourceEngine.clear(mediaState.type).then(() => {
if (!this.destroyed_ && flush)
return this.playerInterface_.mediaSourceEngine.flush(mediaState.type);
});
}
p.then(() => {
if (this.destroyed_) return;
shaka.log.debug(logPrefix, 'cleared buffer');
...
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh okay, got it, thanks!
lib/media/streaming_engine.js
Outdated
@@ -237,6 +237,7 @@ shaka.media.StreamingEngine.PlayerInterface; | |||
* updateTimer: ?number, | |||
* waitingToClearBuffer: boolean, | |||
* waitingToFlushBuffer: boolean, | |||
* deferredClearBufferOffset: number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better name would be clearBufferSafeMargin
, or something including safeMargin, to be consistent.
lib/player.js
Outdated
@@ -133,6 +133,9 @@ shaka.Player = function(video, dependencyInjector) { | |||
/** @private {boolean} */ | |||
this.deferredVariantClearBuffer_ = false; | |||
|
|||
/** @private {number} */ | |||
this.deferredVariantClearBufferOffset_ = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Deferred" is fine here, but again, I think it should include safeMargin instead of offset.
lib/player.js
Outdated
@@ -1640,9 +1643,11 @@ shaka.Player.prototype.usingEmbeddedTextTrack = function() { | |||
* | |||
* @param {shaka.extern.Track} track | |||
* @param {boolean=} clearBuffer | |||
* @param {number=} safeMargin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This deserves a comment for what this will do. You should also include a part about browser compatibility for having this value be too small.
* @private | ||
*/ | ||
shaka.Player.prototype.switch_ = function(variant, clearBuffer = false) { | ||
shaka.Player.prototype.switch_ = function( | ||
variant, clearBuffer = false, safeMargin = 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the callback from AbrManager. If you want to change this, you'll need to change the definitions in externs/shaka/abr_manager.js
and in SimpleAbrManager to match the new function signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In SimpleAbrManager I see that the call to the switch function only passes the variant, without the optional parameters. I kept it this way. Is that okay?
I pushed the other modifications, including the updated abr manager extern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine.
@@ -33,6 +33,7 @@ Donato Borrello <[email protected]> | |||
Duc Pham <[email protected]> | |||
Esteban Dosztal <[email protected]> | |||
François Beaufort <[email protected]> | |||
Giuseppe Samela <[email protected]> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also add an entry to AUTHORS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Let me run it by the build bot.
* @private | ||
*/ | ||
shaka.Player.prototype.switch_ = function(variant, clearBuffer = false) { | ||
shaka.Player.prototype.switch_ = function( | ||
variant, clearBuffer = false, safeMargin = 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine.
All tests passed! |
The goal of this PR is to introduce an optional parameter that allows the buffer to be cleared but not entirely.
The idea is to keep a safe margin that avoids rebuffering events but allows for fast switches. The value of this parameter should be computed and provided by a custom ABR manager. Nothing changes if the value is not provided of course (e.g. SimpleAbrManager).
Since the buffer could be busy, another parameter had to be introduced to save the value of the safe margin when the call to clear the buffer is deferred to a later update.
Something similar was present in Shaka v1, but it was removed with the redesign.