From 9df1e9f6a0770b6879f519d5557219c954092524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=CC=81lvaro=20Velad=20Galva=CC=81n?= Date: Mon, 18 Nov 2024 10:31:01 +0100 Subject: [PATCH] feat: Update usage of minBufferTime according to the DASH spec Related to https://github.com/shaka-project/shaka-player/issues/7602#issuecomment-2479518970 --- docs/tutorials/faq.md | 13 ----- docs/tutorials/manifest-parser.md | 1 - .../tutorials/network-and-buffering-config.md | 7 --- externs/shaka/manifest.js | 6 --- externs/shaka/player.js | 3 +- lib/dash/dash_parser.js | 4 -- lib/hls/hls_parser.js | 1 - lib/media/playhead.js | 8 +-- lib/media/streaming_engine.js | 8 +-- lib/mss/mss_parser.js | 1 - lib/offline/manifest_converter.js | 1 - lib/player.js | 16 ++---- test/dash/dash_parser_manifest_unit.js | 54 +------------------ test/media/playhead_unit.js | 1 - test/media/streaming_engine_integration.js | 1 - test/media/streaming_engine_unit.js | 6 --- test/offline/storage_compatibility_unit.js | 1 - test/test/util/manifest_generator.js | 2 - test/test/util/streaming_engine_util.js | 1 - 19 files changed, 8 insertions(+), 127 deletions(-) diff --git a/docs/tutorials/faq.md b/docs/tutorials/faq.md index 9faddee816..eb9824f6cf 100644 --- a/docs/tutorials/faq.md +++ b/docs/tutorials/faq.md @@ -191,19 +191,6 @@ the ad blocker in compiled mode as well.
-**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); -``` - -
- **Q:** My HLS stream is failing on Chrome, with a chunk demuxer append failed error. diff --git a/docs/tutorials/manifest-parser.md b/docs/tutorials/manifest-parser.md index a596d0b477..8d8f709749 100644 --- a/docs/tutorials/manifest-parser.md +++ b/docs/tutorials/manifest-parser.md @@ -256,7 +256,6 @@ MyManifestParser.prototype.loadManifest_ = function(data) { return { presentationTimeline: timeline, - minBufferTime: 5, // seconds offlineSessionIds: [], variants: [ this.loadVariant_(true, true), diff --git a/docs/tutorials/network-and-buffering-config.md b/docs/tutorials/network-and-buffering-config.md index 14b690fd33..51b7173aef 100644 --- a/docs/tutorials/network-and-buffering-config.md +++ b/docs/tutorials/network-and-buffering-config.md @@ -70,13 +70,6 @@ content will be removed from the start of the buffer to save memory. This is a minimum; if the stream's max segment size is longer than the 'bufferBehind', then that will be used instead. - -*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. diff --git a/externs/shaka/manifest.js b/externs/shaka/manifest.js index e725cb5677..6f41f93c32 100644 --- a/externs/shaka/manifest.js +++ b/externs/shaka/manifest.js @@ -17,7 +17,6 @@ * textStreams: !Array., * imageStreams: !Array., * offlineSessionIds: !Array., - * minBufferTime: number, * sequenceMode: boolean, * ignoreManifestTimestampsInSegmentsMode: boolean, * type: string, @@ -76,11 +75,6 @@ * @property {!Array.} offlineSessionIds * Defaults to [].
* An array of EME sessions to load for offline playback. - * @property {number} minBufferTime - * Defaults to 0.
- * 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. diff --git a/externs/shaka/player.js b/externs/shaka/player.js index bdaf20b6f9..fd87ecca1c 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -1104,8 +1104,7 @@ shaka.extern.xml.Node; * Defaults to false. * @property {boolean} ignoreMinBufferTime * If true will cause DASH parser to ignore minBufferTime from - * manifest. It allows player config to take precedence over manifest for - * rebufferingGoal. + * manifest. *
* Defaults to false. * @property {boolean} autoCorrectDrift diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index a816901781..aa468d46b8 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -799,10 +799,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); @@ -818,7 +815,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, diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 3834cc560f..14a06556d8 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -1002,7 +1002,6 @@ shaka.hls.HlsParser = class { textStreams, imageStreams, offlineSessionIds: [], - minBufferTime: 0, sequenceMode: this.config_.hls.sequenceMode, ignoreManifestTimestampsInSegmentsMode: this.config_.hls.ignoreManifestTimestampsInSegmentsMode, diff --git a/lib/media/playhead.js b/lib/media/playhead.js index 35b0a90108..0d56428480 100644 --- a/lib/media/playhead.js +++ b/lib/media/playhead.js @@ -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; @@ -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(); diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index eca072ed05..43a788b0ba 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -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; @@ -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_); diff --git a/lib/mss/mss_parser.js b/lib/mss/mss_parser.js index f8be5fc390..a2cea2585d 100644 --- a/lib/mss/mss_parser.js +++ b/lib/mss/mss_parser.js @@ -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, diff --git a/lib/offline/manifest_converter.js b/lib/offline/manifest_converter.js index 6d22d3310b..b07c6e9cc7 100644 --- a/lib/offline/manifest_converter.js +++ b/lib/offline/manifest_converter.js @@ -84,7 +84,6 @@ shaka.offline.ManifestConverter = class { return { presentationTimeline: timeline, - minBufferTime: 2, offlineSessionIds: manifestDB.sessionIds, variants: Array.from(variants.values()), textStreams: textStreams, diff --git a/lib/player.js b/lib/player.js index b68c4f0800..660c00e4ee 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2638,13 +2638,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) { @@ -4187,12 +4182,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_) { diff --git a/test/dash/dash_parser_manifest_unit.js b/test/dash/dash_parser_manifest_unit.js index 74bfb04732..d84df6138a 100644 --- a/test/dash/dash_parser_manifest_unit.js +++ b/test/dash/dash_parser_manifest_unit.js @@ -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; @@ -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; @@ -1759,56 +1757,6 @@ describe('DashParser Manifest', () => { expect(stream).toBeUndefined(); }); - it('override manifest value if ignoreMinBufferTime is true', async () => { - const manifestText = [ - '', - ' ', - ' ', - ' ', - ' v-sd.mp4', - ' ', - ' ', - ' ', - ' ', - '', - ].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 = [ - '', - ' ', - ' ', - ' ', - ' v-sd.mp4', - ' ', - ' ', - ' ', - ' ', - '', - ].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 = [ '', @@ -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 () => { diff --git a/test/media/playhead_unit.js b/test/media/playhead_unit.js index 9472a25c3d..abacc9678a 100644 --- a/test/media/playhead_unit.js +++ b/test/media/playhead_unit.js @@ -131,7 +131,6 @@ describe('Playhead', () => { textStreams: [], imageStreams: [], presentationTimeline: timeline, - minBufferTime: 10, offlineSessionIds: [], sequenceMode: false, ignoreManifestTimestampsInSegmentsMode: false, diff --git a/test/media/streaming_engine_integration.js b/test/media/streaming_engine_integration.js index 9f47541b27..dbaefe2dc2 100644 --- a/test/media/streaming_engine_integration.js +++ b/test/media/streaming_engine_integration.js @@ -598,7 +598,6 @@ describe('StreamingEngine', () => { return { presentationTimeline: timeline, offlineSessionIds: [], - minBufferTime: 2, textStreams: [], imageStreams: [], sequenceMode: false, diff --git a/test/media/streaming_engine_unit.js b/test/media/streaming_engine_unit.js index b129ad92e0..0d342df942 100644 --- a/test/media/streaming_engine_unit.js +++ b/test/media/streaming_engine_unit.js @@ -2641,8 +2641,6 @@ describe('StreamingEngine', () => { beforeEach(() => { setupVod(); - manifest.minBufferTime = 1; - config = shaka.util.PlayerConfiguration.createDefault().streaming; config.rebufferingGoal = 1; config.bufferingGoal = 1; @@ -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; @@ -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; diff --git a/test/offline/storage_compatibility_unit.js b/test/offline/storage_compatibility_unit.js index 32bdb426f0..8f1f338cf8 100644 --- a/test/offline/storage_compatibility_unit.js +++ b/test/offline/storage_compatibility_unit.js @@ -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) => { diff --git a/test/test/util/manifest_generator.js b/test/test/util/manifest_generator.js index bf335e62f7..0016af3115 100644 --- a/test/test/util/manifest_generator.js +++ b/test/test/util/manifest_generator.js @@ -93,8 +93,6 @@ shaka.test.ManifestGenerator.Manifest = class { this.presentationTimeline = timeline; /** @type {!Array.} */ this.offlineSessionIds = []; - /** @type {number} */ - this.minBufferTime = 0; /** @type {boolean} */ this.sequenceMode = false; /** @type {boolean} */ diff --git a/test/test/util/streaming_engine_util.js b/test/test/util/streaming_engine_util.js index 11a69c9fe3..2b40f9bf9b 100644 --- a/test/test/util/streaming_engine_util.js +++ b/test/test/util/streaming_engine_util.js @@ -329,7 +329,6 @@ shaka.test.StreamingEngineUtil = class { /** @type {shaka.extern.Manifest} */ const manifest = { presentationTimeline, - minBufferTime: 2, offlineSessionIds: [], variants: [], textStreams: [],