From 08002f3795ebc1d0d809b61fa156291f9f86452f Mon Sep 17 00:00:00 2001 From: Alvaro Velad Galvan Date: Thu, 29 Sep 2022 19:22:34 +0200 Subject: [PATCH 1/2] feat: Allow add extra features to MediaSource.addSourceBuffer --- demo/common/message_ids.js | 2 ++ demo/config.js | 10 ++++++++++ demo/locales/en.json | 2 ++ demo/locales/source.json | 8 ++++++++ externs/shaka/player.js | 19 +++++++++++++++++++ lib/media/media_source_engine.js | 18 ++++++++++++++++-- lib/player.js | 2 ++ lib/util/player_configuration.js | 5 +++++ test/cast/cast_utils_unit.js | 3 +++ test/media/drm_engine_integration.js | 3 +++ test/media/media_source_engine_integration.js | 2 ++ test/media/media_source_engine_unit.js | 2 ++ test/media/streaming_engine_integration.js | 3 +++ test/player_unit.js | 1 + test/test/util/fake_media_source_engine.js | 3 +++ 15 files changed, 81 insertions(+), 2 deletions(-) diff --git a/demo/common/message_ids.js b/demo/common/message_ids.js index 0b53985fd5..84a4cb7ea9 100644 --- a/demo/common/message_ids.js +++ b/demo/common/message_ids.js @@ -222,6 +222,7 @@ shakaDemo.MessageIds = { MAX_HEIGHT: 'DEMO_MAX_HEIGHT', MAX_PIXELS: 'DEMO_MAX_PIXELS', MAX_WIDTH: 'DEMO_MAX_WIDTH', + MEDIA_SOURCE_SECTION_HEADER: 'DEMO_MEDIA_SOURCE_SECTION_HEADER', MIN_BANDWIDTH: 'DEMO_MIN_BANDWIDTH', MIN_BYTES: 'DEMO_MIN_BYTES', MIN_FRAMERATE: 'DEMO_MIN_FRAMERATE', @@ -248,6 +249,7 @@ shakaDemo.MessageIds = { SESSION_ID: 'DEMO_SESSION_ID', SHAKA_CONTROLS: 'DEMO_SHAKA_CONTROLS', SLOW_HALF_LIFE: 'DEMO_SLOW_HALF_LIFE', + SOURCE_BUFFER_EXTRA_FEATURES: 'DEMO_SOURCE_BUFFER_EXTRA_FEATURES', STALL_DETECTOR_ENABLED: 'DEMO_STALL_DETECTOR_ENABLED', STALL_THRESHOLD: 'DEMO_STALL_THRESHOLD', STALL_TIMEOUT: 'DEMO_STALL_TIMEOUT', diff --git a/demo/config.js b/demo/config.js index 58524f68f7..e9d41ed6b3 100644 --- a/demo/config.js +++ b/demo/config.js @@ -92,6 +92,7 @@ shakaDemo.Config = class { this.addOfflineSection_(); this.addDrmSection_(); this.addStreamingSection_(); + this.addMediaSourceSection_(); this.addManifestSection_(); this.addRetrictionsSection_('', shakaDemo.MessageIds.RESTRICTIONS_SECTION_HEADER); @@ -428,6 +429,15 @@ shakaDemo.Config = class { MessageIds.STREAMING_RETRY_SECTION_HEADER); } + /** @private */ + addMediaSourceSection_() { + const MessageIds = shakaDemo.MessageIds; + const docLink = this.resolveExternLink_('.MediaSourceConfiguration'); + this.addSection_(MessageIds.MEDIA_SOURCE_SECTION_HEADER, docLink) + .addTextInput_(MessageIds.SOURCE_BUFFER_EXTRA_FEATURES, + 'mediaSource.sourceBufferExtraFeatures'); + } + /** @private */ addLanguageSection_() { const MessageIds = shakaDemo.MessageIds; diff --git a/demo/locales/en.json b/demo/locales/en.json index d7684fc40d..976c822c8a 100644 --- a/demo/locales/en.json +++ b/demo/locales/en.json @@ -144,6 +144,7 @@ "DEMO_MAX_HEIGHT": "Max Height", "DEMO_MAX_PIXELS": "Max Pixels", "DEMO_MAX_WIDTH": "Max Width", + "DEMO_MEDIA_SOURCE_SECTION_HEADER": "Media source", "DEMO_METACDN": "MetaCDN", "DEMO_MICROSOFT": "Microsoft", "DEMO_MIME_TYPE": "MIME Type", @@ -195,6 +196,7 @@ "DEMO_SHAKA_CONTROLS": "Shaka Controls", "DEMO_SLOW_HALF_LIFE": "Slow half life", "DEMO_SOURCE": "Source on GitHub", + "DEMO_SOURCE_BUFFER_EXTRA_FEATURES": "Source buffer extra features", "DEMO_SOURCE_SEARCH": "Source", "DEMO_STALL_DETECTOR_ENABLED": "Stall Detector Enabled", "DEMO_STALL_THRESHOLD": "Stall Threshold", diff --git a/demo/locales/source.json b/demo/locales/source.json index 37c9af9a12..fede82ad59 100644 --- a/demo/locales/source.json +++ b/demo/locales/source.json @@ -579,6 +579,10 @@ "description": "The name of a configuration value.", "message": "Max Width" }, + "DEMO_MEDIA_SOURCE_SECTION_HEADER": { + "description": "The header for a section of configuration values.", + "message": "Media source" + }, "DEMO_METACDN": { "description": "Text that describes an asset that comes from the MetaCDN asset library.", "message": "[PROPER_NAME:MetaCDN]" @@ -783,6 +787,10 @@ "description": "A link in the footer, to the Shaka Player source on GitHub.", "message": "Source on [PROPER_NAME:GitHub]" }, + "DEMO_SOURCE_BUFFER_EXTRA_FEATURES": { + "description": "The name of a configuration value.", + "message": "Source buffer extra features" + }, "DEMO_SOURCE_SEARCH": { "description": "A header on a search field that filters by the source of the asset.", "message": "Source" diff --git a/externs/shaka/player.js b/externs/shaka/player.js index fa85935b4f..f6fa85f87f 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -1017,6 +1017,22 @@ shaka.extern.ManifestConfiguration; shaka.extern.StreamingConfiguration; +/** + * @typedef {{ + * sourceBufferExtraFeatures: string + * }} + * + * @description + * Media source configuration. + * + * @property {string} sourceBufferExtraFeatures + * Some platforms may need to pass features when initializing the + * sourceBuffer. + * @exportDoc + */ +shaka.extern.MediaSourceConfiguration; + + /** * @typedef {{ * enabled: boolean, @@ -1185,6 +1201,7 @@ shaka.extern.OfflineConfiguration; * drm: shaka.extern.DrmConfiguration, * manifest: shaka.extern.ManifestConfiguration, * streaming: shaka.extern.StreamingConfiguration, + * mediaSource: shaka.extern.MediaSourceConfiguration, * abrFactory: shaka.extern.AbrManager.Factory, * abr: shaka.extern.AbrConfiguration, * cmcd: shaka.extern.CmcdConfiguration, @@ -1212,6 +1229,8 @@ shaka.extern.OfflineConfiguration; * Manifest configuration and settings. * @property {shaka.extern.StreamingConfiguration} streaming * Streaming configuration and settings. + * @property {shaka.extern.MediaSourceConfiguration} mediaSource + * Media source configuration and settings. * @property {shaka.extern.AbrManager.Factory} abrFactory * A factory to construct an abr manager. * @property {shaka.extern.AbrConfiguration} abr diff --git a/lib/media/media_source_engine.js b/lib/media/media_source_engine.js index c303adce02..a8df88bd68 100644 --- a/lib/media/media_source_engine.js +++ b/lib/media/media_source_engine.js @@ -53,6 +53,9 @@ shaka.media.MediaSourceEngine = class { /** @private {HTMLMediaElement} */ this.video_ = video; + /** @private {?shaka.extern.MediaSourceConfiguration} */ + this.config_ = null; + /** @private {shaka.extern.TextDisplayer} */ this.textDisplayer_ = textDisplayer; @@ -291,6 +294,7 @@ shaka.media.MediaSourceEngine = class { this.video_ = null; } + this.config_ = null; this.mediaSource_ = null; this.textEngine_ = null; this.textDisplayer_ = null; @@ -357,8 +361,8 @@ shaka.media.MediaSourceEngine = class { mimeType = shaka.media.Transmuxer.convertTsCodecs(contentType, mimeType); } - - const sourceBuffer = this.mediaSource_.addSourceBuffer(mimeType); + const type = mimeType + this.config_.sourceBufferExtraFeatures; + const sourceBuffer = this.mediaSource_.addSourceBuffer(type); this.eventManager_.listen( sourceBuffer, 'error', @@ -374,6 +378,16 @@ shaka.media.MediaSourceEngine = class { } } + /** + * Called by the Player to provide an updated configuration any time it + * changes. Must be called at least once before init(). + * + * @param {shaka.extern.MediaSourceConfiguration} config + */ + configure(config) { + this.config_ = config; + } + /** * Reinitialize the TextEngine for a new text type. * @param {string} mimeType diff --git a/lib/player.js b/lib/player.js index d1d4147fbb..98282426a3 100644 --- a/lib/player.js +++ b/lib/player.js @@ -1611,6 +1611,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { (metadata, offset, endTime) => { this.processTimedMetadataMediaSrc_(metadata, offset, endTime); }); + mediaSourceEngine.configure(this.config_.mediaSource); const {segmentRelativeVttTiming} = this.config_.manifest; mediaSourceEngine.setSegmentRelativeVttTiming(segmentRelativeVttTiming); @@ -3084,6 +3085,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { } if (this.mediaSourceEngine_) { + this.mediaSourceEngine_.configure(this.config_.mediaSource); const {segmentRelativeVttTiming} = this.config_.manifest; this.mediaSourceEngine_.setSegmentRelativeVttTiming( segmentRelativeVttTiming); diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 6b03e49259..fcffb12d59 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -258,6 +258,10 @@ shaka.util.PlayerConfiguration = class { useHeaders: false, }; + const mediaSource = { + sourceBufferExtraFeatures: '', + }; + const AutoShowText = shaka.config.AutoShowText; /** @type {shaka.extern.PlayerConfiguration} */ @@ -265,6 +269,7 @@ shaka.util.PlayerConfiguration = class { drm: drm, manifest: manifest, streaming: streaming, + mediaSource: mediaSource, offline: offline, abrFactory: () => new shaka.abr.SimpleAbrManager(), abr: abr, diff --git a/test/cast/cast_utils_unit.js b/test/cast/cast_utils_unit.js index 93e08c7654..fa78068622 100644 --- a/test/cast/cast_utils_unit.js +++ b/test/cast/cast_utils_unit.js @@ -207,6 +207,9 @@ describe('CastUtils', () => { video, new shaka.test.FakeClosedCaptionParser(), new shaka.test.FakeTextDisplayer()); + const config = + shaka.util.PlayerConfiguration.createDefault().mediaSource; + mediaSourceEngine.configure(config); const ContentType = shaka.util.ManifestParserUtils.ContentType; const initObject = new Map(); diff --git a/test/media/drm_engine_integration.js b/test/media/drm_engine_integration.js index 8b3f587af9..25ea1524ae 100644 --- a/test/media/drm_engine_integration.js +++ b/test/media/drm_engine_integration.js @@ -128,6 +128,9 @@ describe('DrmEngine', () => { video, new shaka.test.FakeClosedCaptionParser(), new shaka.test.FakeTextDisplayer()); + const mediaSourceConfig = + shaka.util.PlayerConfiguration.createDefault().mediaSource; + mediaSourceEngine.configure(mediaSourceConfig); const expectedObject = new Map(); expectedObject.set(ContentType.AUDIO, audioStream); diff --git a/test/media/media_source_engine_integration.js b/test/media/media_source_engine_integration.js index ebbd49a370..b950129967 100644 --- a/test/media/media_source_engine_integration.js +++ b/test/media/media_source_engine_integration.js @@ -41,6 +41,8 @@ describe('MediaSourceEngine', () => { video, new shaka.media.ClosedCaptionParser(), textDisplayer); + const config = shaka.util.PlayerConfiguration.createDefault().mediaSource; + mediaSourceEngine.configure(config); mediaSource = /** @type {?} */(mediaSourceEngine)['mediaSource_']; expect(video.src).toBeTruthy(); diff --git a/test/media/media_source_engine_unit.js b/test/media/media_source_engine_unit.js index 2ef9e190db..8a805d5e7e 100644 --- a/test/media/media_source_engine_unit.js +++ b/test/media/media_source_engine_unit.js @@ -150,6 +150,8 @@ describe('MediaSourceEngine', () => { video, mockClosedCaptionParser, mockTextDisplayer); + const config = shaka.util.PlayerConfiguration.createDefault().mediaSource; + mediaSourceEngine.configure(config); }); afterEach(() => { diff --git a/test/media/streaming_engine_integration.js b/test/media/streaming_engine_integration.js index 5546d10d6c..517b68ecba 100644 --- a/test/media/streaming_engine_integration.js +++ b/test/media/streaming_engine_integration.js @@ -68,6 +68,9 @@ describe('StreamingEngine', () => { video, new shaka.test.FakeClosedCaptionParser(), new shaka.test.FakeTextDisplayer()); + const mediaSourceConfig = + shaka.util.PlayerConfiguration.createDefault().mediaSource; + mediaSourceEngine.configure(mediaSourceConfig); waiter.setMediaSourceEngine(mediaSourceEngine); }); diff --git a/test/player_unit.js b/test/player_unit.js index 0b1e02cffd..5007a645c3 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -123,6 +123,7 @@ describe('Player', () => { streamingEngine = new shaka.test.FakeStreamingEngine(); mediaSourceEngine = { init: jasmine.createSpy('init').and.returnValue(Promise.resolve()), + configure: jasmine.createSpy('configure'), open: jasmine.createSpy('open').and.returnValue(Promise.resolve()), destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()), diff --git a/test/test/util/fake_media_source_engine.js b/test/test/util/fake_media_source_engine.js index f7e784bc32..360d398308 100644 --- a/test/test/util/fake_media_source_engine.js +++ b/test/test/util/fake_media_source_engine.js @@ -54,6 +54,9 @@ shaka.test.FakeMediaSourceEngine = class { /** @type {!jasmine.Spy} */ this.open = jasmine.createSpy('open').and.returnValue(Promise.resolve()); + /** @type {!jasmine.Spy} */ + this.configure = jasmine.createSpy('configure').and.stub(); + /** @type {!jasmine.Spy} */ this.reinitText = jasmine.createSpy('reinitText').and.stub(); From d5d732f659d83ef3c24bdfc75cec295729978444 Mon Sep 17 00:00:00 2001 From: Alvaro Velad Galvan Date: Fri, 30 Sep 2022 08:11:20 +0200 Subject: [PATCH 2/2] Improve documentation --- externs/shaka/player.js | 1 + 1 file changed, 1 insertion(+) diff --git a/externs/shaka/player.js b/externs/shaka/player.js index f6fa85f87f..d3cdae9275 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -1028,6 +1028,7 @@ shaka.extern.StreamingConfiguration; * @property {string} sourceBufferExtraFeatures * Some platforms may need to pass features when initializing the * sourceBuffer. + * This string is ultimately appended to MIME types in addSourceBuffer(). * @exportDoc */ shaka.extern.MediaSourceConfiguration;