From de2a2d885f50ecdbebef9f5886bc90b232a20533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Wed, 24 Apr 2024 18:15:44 +0200 Subject: [PATCH] feat: Add TextDisplayer config (#6477) --- demo/config.js | 10 ++++++++++ docs/tutorials/upgrade.md | 2 ++ externs/shaka/player.js | 20 ++++++++++++++++++++ externs/shaka/text.js | 7 +++++++ lib/player.js | 22 +++++++++++++++++++++- lib/text/simple_text_displayer.js | 9 +++++++++ lib/text/stub_text_displayer.js | 7 +++++++ lib/text/text_engine.js | 8 +++++--- lib/text/ui_text_displayer.js | 22 ++++++++++++++-------- lib/util/player_configuration.js | 5 +++++ test/test/util/fake_text_displayer.js | 3 +++ test/test/util/layout_tests.js | 3 ++- test/text/ui_text_displayer_unit.js | 3 ++- 13 files changed, 107 insertions(+), 14 deletions(-) diff --git a/demo/config.js b/demo/config.js index e253907104..15e14540bb 100644 --- a/demo/config.js +++ b/demo/config.js @@ -93,6 +93,7 @@ shakaDemo.Config = class { this.addMssManifestSection_(); this.addRetrySection_('manifest', 'Manifest Retry Parameters'); this.addRetrictionsSection_('', 'Restrictions'); + this.addTextDisplayerSection_(); this.addCmcdSection_(); this.addCmsdSection_(); this.addLcevcSection_(); @@ -306,6 +307,15 @@ shakaDemo.Config = class { this.addRetrictionsSection_('abr', 'Adaptation Restrictions'); } + /** @private */ + addTextDisplayerSection_() { + const docLink = this.resolveExternLink_('.TextDisplayerConfiguration'); + this.addSection_('Text displayer', docLink) + .addNumberInput_('Captions update period', + 'textDisplayer.captionsUpdatePeriod', + /* canBeDecimal= */ true); + } + /** @private */ addCmcdSection_() { const docLink = this.resolveExternLink_('.CmcdConfiguration'); diff --git a/docs/tutorials/upgrade.md b/docs/tutorials/upgrade.md index d050b9aab3..1ea7d560b2 100644 --- a/docs/tutorials/upgrade.md +++ b/docs/tutorials/upgrade.md @@ -106,6 +106,8 @@ application: - `mediaSource.sourceBufferExtraFeatures` has been replaced with `mediaSource.addExtraFeaturesToSourceBuffer` callback. - Plugin changes: + - `TextDisplayer` plugins must implement the `configure()` method. + - `TextParser` plugins must implement the `setManifestType()` and `setSequenceMode()` methods. - `Transmuxer` plugins now has three new parameters in `transmux()` method. - Player API Changes: diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 8bfa363771..420b2015a9 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -1753,6 +1753,23 @@ shaka.extern.LcevcConfiguration; shaka.extern.OfflineConfiguration; +/** + * @typedef {{ + * captionsUpdatePeriod: number + * }} + * + * @description + * Text displayer configuration. + * + * @property {number} captionsUpdatePeriod + * The number of seconds to see if the captions should be updated. + * Defaults to 0.25. + * + * @exportDoc + */ +shaka.extern.TextDisplayerConfiguration; + + /** * @typedef {{ * ads: shaka.extern.AdsConfiguration, @@ -1784,6 +1801,7 @@ shaka.extern.OfflineConfiguration; * restrictions: shaka.extern.Restrictions, * playRangeStart: number, * playRangeEnd: number, + * textDisplayer: shaka.extern.TextDisplayerConfiguration, * textDisplayFactory: shaka.extern.TextDisplayer.Factory * }} * @@ -1870,6 +1888,8 @@ shaka.extern.OfflineConfiguration; * @property {number} playRangeEnd * Optional playback and seek end time in seconds. Defaults to the end of * the presentation if not provided. + * @property {shaka.extern.TextDisplayerConfiguration} textDisplayer + * Text displayer configuration and settings. * @property {shaka.extern.TextDisplayer.Factory} textDisplayFactory * A factory to construct a text displayer. Note that, if this is changed * during playback, it will cause the text tracks to be reloaded. diff --git a/externs/shaka/text.js b/externs/shaka/text.js index 61ec1ee941..0ef74dd04b 100644 --- a/externs/shaka/text.js +++ b/externs/shaka/text.js @@ -132,6 +132,13 @@ shaka.extern.TextDisplayer = class { */ destroy() {} + /** + * Sets the TextDisplayer configuration. + * + * @param {shaka.extern.TextDisplayerConfiguration} config + */ + configure(config) {} + /** * Append given text cues to the list of cues to be displayed. * diff --git a/lib/player.js b/lib/player.js index 5259fbf1c2..f7d7ae911d 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2088,6 +2088,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget { // until they are both initialized before setting the initial value. const textDisplayerFactory = this.config_.textDisplayFactory; const textDisplayer = textDisplayerFactory(); + if (textDisplayer.configure) { + textDisplayer.configure(this.config_.textDisplayer); + } else { + shaka.Deprecate.deprecateFeature(5, + 'Text displayer w/ configure', + 'Text displayer should have a "configure" method!'); + } this.lastTextFactory_ = textDisplayerFactory; const mediaSourceEngine = this.createMediaSourceEngine( @@ -3436,6 +3443,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget { const textDisplayerFactory = this.config_.textDisplayFactory; if (this.lastTextFactory_ != textDisplayerFactory) { const displayer = textDisplayerFactory(); + if (displayer.configure) { + displayer.configure(this.config_.textDisplayer); + } else { + shaka.Deprecate.deprecateFeature(5, + 'Text displayer w/ configure', + 'Text displayer should have a "configure" method!'); + } this.mediaSourceEngine_.setTextDisplayer(displayer); this.lastTextFactory_ = textDisplayerFactory; @@ -3443,6 +3457,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget { // Reload the text stream, so the cues will load again. this.streamingEngine_.reloadTextStream(); } + } else { + const displayer = this.mediaSourceEngine_.getTextDisplayer(); + if (displayer.configure) { + displayer.configure(this.config_.textDisplayer); + } } } if (this.abrManager_) { @@ -5590,8 +5609,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget { // TextDisplay factory must capture a reference to "this". config.textDisplayFactory = () => { if (this.videoContainer_) { + const latestConfig = this.getConfiguration(); return new shaka.text.UITextDisplayer( - this.video_, this.videoContainer_); + this.video_, this.videoContainer_, latestConfig.textDisplayer); } else { // eslint-disable-next-line no-restricted-syntax if (HTMLMediaElement.prototype.addTextTrack) { diff --git a/lib/text/simple_text_displayer.js b/lib/text/simple_text_displayer.js index 4a2aeaaf21..80fb2736d5 100644 --- a/lib/text/simple_text_displayer.js +++ b/lib/text/simple_text_displayer.js @@ -56,6 +56,15 @@ shaka.text.SimpleTextDisplayer = class { this.textTrack_.mode = 'hidden'; } + + /** + * @override + * @export + */ + configure(config) { + // Unused. + } + /** * @override * @export diff --git a/lib/text/stub_text_displayer.js b/lib/text/stub_text_displayer.js index 785e06a2a3..daa96904d9 100644 --- a/lib/text/stub_text_displayer.js +++ b/lib/text/stub_text_displayer.js @@ -13,6 +13,13 @@ goog.provide('shaka.text.StubTextDisplayer'); * @export */ shaka.text.StubTextDisplayer = class { + /** + * @override + * @export + */ + configure(config) { + } + /** * @override * @export diff --git a/lib/text/text_engine.js b/lib/text/text_engine.js index f6d257306b..d2b86f2058 100644 --- a/lib/text/text_engine.js +++ b/lib/text/text_engine.js @@ -7,7 +7,7 @@ goog.provide('shaka.text.TextEngine'); goog.require('goog.asserts'); -goog.require('shaka.log'); +goog.require('shaka.Deprecate'); goog.require('shaka.media.ClosedCaptionParser'); goog.require('shaka.text.Cue'); goog.require('shaka.util.BufferUtils'); @@ -153,13 +153,15 @@ shaka.text.TextEngine = class { if (this.parser_.setSequenceMode) { this.parser_.setSequenceMode(sequenceMode); } else { - shaka.log.alwaysWarn( + shaka.Deprecate.deprecateFeature(5, + 'Text parsers w/ setSequenceMode', 'Text parsers should have a "setSequenceMode" method!'); } if (this.parser_.setManifestType) { this.parser_.setManifestType(manifestType); } else { - shaka.log.alwaysWarn( + shaka.Deprecate.deprecateFeature(5, + 'Text parsers w/ setManifestType', 'Text parsers should have a "setManifestType" method!'); } this.segmentRelativeVttTiming_ = segmentRelativeVttTiming; diff --git a/lib/text/ui_text_displayer.js b/lib/text/ui_text_displayer.js index 4271971cb7..95d5f6c4fd 100644 --- a/lib/text/ui_text_displayer.js +++ b/lib/text/ui_text_displayer.js @@ -28,8 +28,9 @@ shaka.text.UITextDisplayer = class { * Constructor. * @param {HTMLMediaElement} video * @param {HTMLElement} videoContainer + * @param {shaka.extern.TextDisplayerConfiguration} config */ - constructor(video, videoContainer) { + constructor(video, videoContainer, config) { goog.asserts.assert(videoContainer, 'videoContainer should be valid.'); /** @private {boolean} */ @@ -64,18 +65,12 @@ shaka.text.UITextDisplayer = class { this.videoContainer_.appendChild(this.textContainer_); - /** - * The captions' update period in seconds. - * @private {number} - */ - const updatePeriod = 0.25; - /** @private {shaka.util.Timer} */ this.captionsTimer_ = new shaka.util.Timer(() => { if (!this.video_.paused) { this.updateCaptions_(); } - }).tickEvery(updatePeriod); + }).tickEvery(config.captionsUpdatePeriod); /** * Maps cues to cue elements. Specifically points out the wrapper element of @@ -130,6 +125,17 @@ shaka.text.UITextDisplayer = class { } + /** + * @override + * @export + */ + configure(config) { + if (this.captionsTimer_) { + this.captionsTimer_.tickEvery(config.captionsUpdatePeriod); + } + } + + /** * @override * @export diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 5145917c00..c78e6789ac 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -396,6 +396,10 @@ shaka.util.PlayerConfiguration = class { skipPlayDetection, }; + const textDisplayer = { + captionsUpdatePeriod: 0.25, + }; + const AutoShowText = shaka.config.AutoShowText; /** @type {shaka.extern.PlayerConfiguration} */ @@ -436,6 +440,7 @@ shaka.util.PlayerConfiguration = class { }, playRangeStart: 0, playRangeEnd: Infinity, + textDisplayer: textDisplayer, textDisplayFactory: () => null, cmcd: cmcd, cmsd: cmsd, diff --git a/test/test/util/fake_text_displayer.js b/test/test/util/fake_text_displayer.js index 784907ae93..05f312f566 100644 --- a/test/test/util/fake_text_displayer.js +++ b/test/test/util/fake_text_displayer.js @@ -34,6 +34,9 @@ shaka.test.FakeTextDisplayer = class { return func(); } + /** @override */ + configure(config) {} + /** @override */ append(cues) { const func = shaka.test.Util.spyFunc(this.appendSpy); diff --git a/test/test/util/layout_tests.js b/test/test/util/layout_tests.js index ebe44525a4..50864700d0 100644 --- a/test/test/util/layout_tests.js +++ b/test/test/util/layout_tests.js @@ -291,7 +291,8 @@ shaka.test.DomTextLayoutTests = class extends shaka.test.TextLayoutTests { recreateTextDisplayer() { this.textDisplayer = new shaka.text.UITextDisplayer( /** @type {!HTMLMediaElement} */(this.mockVideo), - this.videoContainer); + this.videoContainer, + {captionsUpdatePeriod: 0.25}); this.textDisplayer.setTextVisibility(true); } diff --git a/test/text/ui_text_displayer_unit.js b/test/text/ui_text_displayer_unit.js index 7fca26ef46..9464ad3e2c 100644 --- a/test/text/ui_text_displayer_unit.js +++ b/test/text/ui_text_displayer_unit.js @@ -51,7 +51,8 @@ describe('UITextDisplayer', () => { beforeEach(() => { video.currentTime = 0; - textDisplayer = new shaka.text.UITextDisplayer(video, videoContainer); + textDisplayer = new shaka.text.UITextDisplayer( + video, videoContainer, {captionsUpdatePeriod: 0.25}); }); afterEach(async () => {