Skip to content

Commit

Permalink
feat: Update usage of minBufferTime according to the DASH spec (#7616)
Browse files Browse the repository at this point in the history
Related to
#7602 (comment)

From 23009-1:

The value of the minimum buffer time does not provide any instructions
to the client on how long
to buffer the media. The value however describes how much buffer a
client should have under
ideal network conditions. As such, MBT is not describing the burstiness
or jitter in the network,
it is describing the burstiness or jitter in the content encoding.
Together with the BW value, it is
a property of the content. Using the "leaky bucket" model, it is the
size of the bucket that makes
    BW true, given the way the content is encoded
  • Loading branch information
avelad authored Nov 19, 2024
1 parent f277f2c commit 2260aa9
Show file tree
Hide file tree
Showing 19 changed files with 16 additions and 130 deletions.
13 changes: 0 additions & 13 deletions docs/tutorials/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,6 @@ the ad blocker in compiled mode as well.

<hr>

**Q:** Why does some DASH content take a long time to start playback?

**A:** Shaka Player honors the `minBufferTime` field in DASH. If this field is
set to a large value, Shaka Player will buffer that much content before
beginning playback. To override this behavior and ignore the `minBufferTime`
field, we offer the following configuration:

```js
player.configure('manifest.dash.ignoreMinBufferTime', true);
```

<hr>

**Q:** My HLS stream is failing on Chrome, with a chunk demuxer append failed
error.

Expand Down
1 change: 0 additions & 1 deletion docs/tutorials/manifest-parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ MyManifestParser.prototype.loadManifest_ = function(data) {

return {
presentationTimeline: timeline,
minBufferTime: 5, // seconds
offlineSessionIds: [],
variants: [
this.loadVariant_(true, true),
Expand Down
3 changes: 0 additions & 3 deletions docs/tutorials/network-and-buffering-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ This is a minimum; if the stream's max segment size is longer than the

*NOTES:*
- *`rebufferingGoal` should always be less than `bufferingGoal`.*
- *A DASH manifest's `minBufferTime`, if greater, overrides `rebufferingGoal`.*
- *You can ignore `minBufferTime` by setting the
`manifest.dash.ignoreMinBufferTime` configuration to true.*

All of these settings should be customized for your application. The default
values are very conservative.
Expand Down
6 changes: 0 additions & 6 deletions externs/shaka/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* textStreams: !Array.<shaka.extern.Stream>,
* imageStreams: !Array.<shaka.extern.Stream>,
* offlineSessionIds: !Array.<string>,
* minBufferTime: number,
* sequenceMode: boolean,
* ignoreManifestTimestampsInSegmentsMode: boolean,
* type: string,
Expand Down Expand Up @@ -76,11 +75,6 @@
* @property {!Array.<string>} offlineSessionIds
* <i>Defaults to [].</i> <br>
* An array of EME sessions to load for offline playback.
* @property {number} minBufferTime
* <i>Defaults to 0.</i> <br>
* The minimum number of seconds of content that must be buffered before
* playback can begin. Can be overridden by a higher value from the Player
* configuration.
* @property {boolean} sequenceMode
* If true, we will append the media segments using sequence mode; that is to
* say, ignoring any timestamps inside the media files.
Expand Down
3 changes: 1 addition & 2 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1104,8 +1104,7 @@ shaka.extern.xml.Node;
* Defaults to <code>false</code>.
* @property {boolean} ignoreMinBufferTime
* If true will cause DASH parser to ignore <code>minBufferTime</code> from
* manifest. It allows player config to take precedence over manifest for
* <code>rebufferingGoal</code>.
* manifest.
* <br>
* Defaults to <code>false</code>.
* @property {boolean} autoCorrectDrift
Expand Down
17 changes: 6 additions & 11 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -643,13 +643,6 @@ shaka.dash.DashParser = class {
}
this.manifestPatchContext_.availabilityTimeOffset = availabilityTimeOffset;

const ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
let minBufferTime = 0;
if (!ignoreMinBufferTime) {
minBufferTime =
TXml.parseAttr(mpd, 'minBufferTime', TXml.parseDuration) || 0;
}

this.updatePeriod_ = /** @type {number} */ (TXml.parseAttr(
mpd, 'minimumUpdatePeriod', TXml.parseDuration, -1));

Expand Down Expand Up @@ -702,6 +695,12 @@ shaka.dash.DashParser = class {
}
}
} else {
const ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
let minBufferTime = 0;
if (!ignoreMinBufferTime) {
minBufferTime =
TXml.parseAttr(mpd, 'minBufferTime', TXml.parseDuration) || 0;
}
// DASH IOP v3.0 suggests using a default delay between minBufferTime
// and timeShiftBufferDepth. This is literally the range of all
// feasible choices for the value. Nothing older than
Expand Down Expand Up @@ -799,10 +798,7 @@ shaka.dash.DashParser = class {

await contentSteeringPromise;

// Set minBufferTime to 0 for low-latency DASH live stream to achieve the
// best latency
if (this.lowLatencyMode_) {
minBufferTime = 0;
const presentationDelay = suggestedPresentationDelay != null ?
suggestedPresentationDelay : this.config_.defaultPresentationDelay;
presentationTimeline.setDelay(presentationDelay);
Expand All @@ -818,7 +814,6 @@ shaka.dash.DashParser = class {
textStreams: this.periodCombiner_.getTextStreams(),
imageStreams: this.periodCombiner_.getImageStreams(),
offlineSessionIds: [],
minBufferTime: minBufferTime || 0,
sequenceMode: this.config_.dash.sequenceMode,
ignoreManifestTimestampsInSegmentsMode: false,
type: shaka.media.ManifestParser.DASH,
Expand Down
1 change: 0 additions & 1 deletion lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,6 @@ shaka.hls.HlsParser = class {
textStreams,
imageStreams,
offlineSessionIds: [],
minBufferTime: 0,
sequenceMode: this.config_.hls.sequenceMode,
ignoreManifestTimestampsInSegmentsMode:
this.config_.hls.ignoreManifestTimestampsInSegmentsMode,
Expand Down
8 changes: 1 addition & 7 deletions lib/media/playhead.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,6 @@ shaka.media.MediaSourcePlayhead = class {
/** @private {shaka.media.PresentationTimeline} */
this.timeline_ = manifest.presentationTimeline;

/** @private {number} */
this.minBufferTime_ = manifest.minBufferTime || 0;

/** @private {?shaka.extern.StreamingConfiguration} */
this.config_ = config;

Expand Down Expand Up @@ -482,10 +479,7 @@ shaka.media.MediaSourcePlayhead = class {
const isBuffered = (playheadTime) => shaka.media.TimeRangesUtils.isBuffered(
this.mediaElement_.buffered, playheadTime);

const rebufferingGoal = Math.max(
this.minBufferTime_,
this.config_.rebufferingGoal);

const rebufferingGoal = this.config_.rebufferingGoal;
const safeSeekOffset = this.config_.safeSeekOffset;

let start = this.timeline_.getSeekRangeStart();
Expand Down
8 changes: 2 additions & 6 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,9 +779,7 @@ shaka.media.StreamingEngine = class {
// If the new segment can be finished in time without risking a buffer
// underflow, we should abort the old one and switch.
const bufferedAhead = (bufferEnd || 0) - presentationTime;
const safetyBuffer = Math.max(
this.manifest_.minBufferTime || 0,
this.config_.rebufferingGoal);
const safetyBuffer = this.config_.rebufferingGoal;
const safeBufferedAhead = bufferedAhead - safetyBuffer;
if (timeToFetchNewSegment < safeBufferedAhead) {
return true;
Expand Down Expand Up @@ -1366,9 +1364,7 @@ shaka.media.StreamingEngine = class {
'bufferedAhead=' + bufferedAhead);

const unscaledBufferingGoal = Math.max(
this.manifest_.minBufferTime || 0,
this.config_.rebufferingGoal,
this.config_.bufferingGoal);
this.config_.rebufferingGoal, this.config_.bufferingGoal);

const scaledBufferingGoal = Math.max(1,
unscaledBufferingGoal * this.bufferingScale_);
Expand Down
1 change: 0 additions & 1 deletion lib/mss/mss_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,6 @@ shaka.mss.MssParser = class {
textStreams: context.textStreams,
imageStreams: [],
offlineSessionIds: [],
minBufferTime: 0,
sequenceMode: this.config_.mss.sequenceMode,
ignoreManifestTimestampsInSegmentsMode: false,
type: shaka.media.ManifestParser.MSS,
Expand Down
1 change: 0 additions & 1 deletion lib/offline/manifest_converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ shaka.offline.ManifestConverter = class {

return {
presentationTimeline: timeline,
minBufferTime: 2,
offlineSessionIds: manifestDB.sessionIds,
variants: Array.from(variants.values()),
textStreams: textStreams,
Expand Down
16 changes: 3 additions & 13 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2649,13 +2649,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.playheadObservers_ =
this.createPlayheadObserversForMSE_(startTime);

// We need to start the buffer management code near the end because it
// will set the initial buffering state and that depends on other
// components being initialized.
const rebufferThreshold = Math.max(
this.manifest_.minBufferTime,
this.config_.streaming.rebufferingGoal);
this.startBufferManagement_(mediaElement, rebufferThreshold);
this.startBufferManagement_(
mediaElement, this.config_.streaming.rebufferingGoal);
};

if (!this.config_.streaming.startAtSegmentBoundary) {
Expand Down Expand Up @@ -4207,12 +4202,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.onAbrStatusChanged_();
}
if (this.bufferObserver_) {
let rebufferThreshold = this.config_.streaming.rebufferingGoal;
if (this.manifest_) {
rebufferThreshold =
Math.max(rebufferThreshold, this.manifest_.minBufferTime);
}
this.updateBufferingSettings_(rebufferThreshold);
this.updateBufferingSettings_(this.config_.streaming.rebufferingGoal);
}

if (this.manifest_) {
Expand Down
54 changes: 1 addition & 53 deletions test/dash/dash_parser_manifest_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ describe('DashParser Manifest', () => {
manifest.sequenceMode = true;
manifest.type = shaka.media.ManifestParser.DASH;
manifest.anyTimeline();
manifest.minBufferTime = 75;
manifest.addPartialVariant((variant) => {
variant.language = 'en';
variant.bandwidth = 200;
Expand Down Expand Up @@ -256,7 +255,6 @@ describe('DashParser Manifest', () => {
manifest.sequenceMode = false;
manifest.type = shaka.media.ManifestParser.DASH;
manifest.anyTimeline();
manifest.minBufferTime = 75;
manifest.addPartialVariant((variant) => {
variant.language = 'en';
variant.bandwidth = 200;
Expand Down Expand Up @@ -1759,56 +1757,6 @@ describe('DashParser Manifest', () => {
expect(stream).toBeUndefined();
});

it('override manifest value if ignoreMinBufferTime is true', async () => {
const manifestText = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet id="1" mimeType="video/mp4">',
' <Representation id="video-sd" width="640" height="480">',
' <BaseURL>v-sd.mp4</BaseURL>',
' <SegmentBase indexRange="100-200" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', manifestText);
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
config.dash.ignoreMinBufferTime = true;
parser.configure(config);

/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);
const minBufferTime = manifest.minBufferTime;
expect(minBufferTime).toBe(0);
});

it('get manifest value if ignoreMinBufferTime is false', async () => {
const manifestText = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet id="1" mimeType="video/mp4">',
' <Representation id="video-sd" width="640" height="480">',
' <BaseURL>v-sd.mp4</BaseURL>',
' <SegmentBase indexRange="100-200" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', manifestText);
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
config.dash.ignoreMinBufferTime = false;
parser.configure(config);

/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);
const minBufferTime = manifest.minBufferTime;
expect(minBufferTime).toBe(75);
});

it('honors the ignoreMaxSegmentDuration config', async () => {
const manifestText = [
'<MPD maxSegmentDuration="PT5S">',
Expand Down Expand Up @@ -1944,7 +1892,7 @@ describe('DashParser Manifest', () => {
const manifest = await parser.start('dummy://foo', playerInterface);
const presentationTimeline = manifest.presentationTimeline;
const presentationDelay = presentationTimeline.getDelay();
expect(presentationDelay).toBe(1.5*manifest.minBufferTime);
expect(presentationDelay).toBe(3);
});

it('Honors the ignoreEmptyAdaptationSet config', async () => {
Expand Down
3 changes: 2 additions & 1 deletion test/media/playhead_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ describe('Playhead', () => {
textStreams: [],
imageStreams: [],
presentationTimeline: timeline,
minBufferTime: 10,
offlineSessionIds: [],
sequenceMode: false,
ignoreManifestTimestampsInSegmentsMode: false,
Expand Down Expand Up @@ -405,6 +404,8 @@ describe('Playhead', () => {
timeline.getSeekRangeStart.and.returnValue(5);
timeline.getSeekRangeEnd.and.returnValue(60);

config.rebufferingGoal = 10;

playhead = new shaka.media.MediaSourcePlayhead(
video,
manifest,
Expand Down
1 change: 0 additions & 1 deletion test/media/streaming_engine_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,6 @@ describe('StreamingEngine', () => {
return {
presentationTimeline: timeline,
offlineSessionIds: [],
minBufferTime: 2,
textStreams: [],
imageStreams: [],
sequenceMode: false,
Expand Down
6 changes: 0 additions & 6 deletions test/media/streaming_engine_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2641,8 +2641,6 @@ describe('StreamingEngine', () => {
beforeEach(() => {
setupVod();

manifest.minBufferTime = 1;

config = shaka.util.PlayerConfiguration.createDefault().streaming;
config.rebufferingGoal = 1;
config.bufferingGoal = 1;
Expand Down Expand Up @@ -2752,8 +2750,6 @@ describe('StreamingEngine', () => {
it('does not fail immediately', async () => {
setupVod();

manifest.minBufferTime = 1;

// Create StreamingEngine.
const config = shaka.util.PlayerConfiguration.createDefault().streaming;
config.rebufferingGoal = 1;
Expand Down Expand Up @@ -2816,8 +2812,6 @@ describe('StreamingEngine', () => {
it('fails after multiple QuotaExceededError', async () => {
setupVod();

manifest.minBufferTime = 1;

// Create StreamingEngine.
const config = shaka.util.PlayerConfiguration.createDefault().streaming;
config.rebufferingGoal = 1;
Expand Down
1 change: 0 additions & 1 deletion test/offline/storage_compatibility_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ filterDescribe('Storage Compatibility', offlineSupported, () => {

const expected = shaka.test.ManifestGenerator.generate((manifest) => {
manifest.anyTimeline();
manifest.minBufferTime = 2;

manifest.addPartialVariant((variant) => {
variant.addPartialStream(ContentType.VIDEO, (stream) => {
Expand Down
2 changes: 0 additions & 2 deletions test/test/util/manifest_generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ shaka.test.ManifestGenerator.Manifest = class {
this.presentationTimeline = timeline;
/** @type {!Array.<string>} */
this.offlineSessionIds = [];
/** @type {number} */
this.minBufferTime = 0;
/** @type {boolean} */
this.sequenceMode = false;
/** @type {boolean} */
Expand Down
1 change: 0 additions & 1 deletion test/test/util/streaming_engine_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ shaka.test.StreamingEngineUtil = class {
/** @type {shaka.extern.Manifest} */
const manifest = {
presentationTimeline,
minBufferTime: 2,
offlineSessionIds: [],
variants: [],
textStreams: [],
Expand Down

0 comments on commit 2260aa9

Please sign in to comment.