Skip to content

Commit

Permalink
feat: Allow preload text tracks and expose the preloaded variant and …
Browse files Browse the repository at this point in the history
…text tracks (#8187)
  • Loading branch information
avelad authored Feb 28, 2025
1 parent bcf159d commit 4e05551
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 67 deletions.
49 changes: 49 additions & 0 deletions lib/media/preload_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
/** @private {?shaka.extern.Variant} */
this.prefetchedVariant_ = null;

/** @private {?shaka.extern.Stream} */
this.prefetchedTextStream_ = null;

/** @private {boolean} */
this.allowMakeAbrManager_ = typedPlayerInterface.allowMakeAbrManager;

Expand Down Expand Up @@ -345,6 +348,32 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
return this.prefetchedVariant_;
}

/**
* Gets the preloaded variant track if it exists.
*
* @return {?shaka.extern.Track}
* @export
*/
getPrefetchedVariantTrack() {
if (!this.prefetchedVariant_) {
return null;
}
return shaka.util.StreamUtils.variantToTrack(this.prefetchedVariant_);
}

/**
* Gets the preloaded text track if it exists.
*
* @return {?shaka.extern.Track}
* @export
*/
getPrefetchedTextTrack() {
if (!this.prefetchedTextStream_) {
return null;
}
return shaka.util.StreamUtils.textStreamToTrack(this.prefetchedTextStream_);
}

/**
* Gets the SegmentPrefetch objects for the initial stream ids. Also marks
* that those objects should not be aborted if this manager is destroyed.
Expand Down Expand Up @@ -709,11 +738,31 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
if (variant.audio) {
promises.push(this.prefetchStream_(variant.audio, isLive));
}
const textStream = this.chooseTextStream_();
if (textStream && shaka.util.StreamUtils.shouldInitiallyShowText(
variant.audio, textStream, this.config_)) {
promises.push(this.prefetchStream_(textStream, isLive));
this.prefetchedTextStream_ = textStream;
}

await Promise.all(promises);
}
}
}

/**
* @return {?shaka.extern.Stream}
* @private
*/
chooseTextStream_() {
const subset = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
this.manifest_.textStreams,
this.config_.preferredTextLanguage,
this.config_.preferredTextRole,
this.config_.preferForcedSubs);
return subset[0] || null;
}

/**
* @param {!shaka.extern.Stream} stream
* @param {boolean} isLive
Expand Down
70 changes: 3 additions & 67 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
goog.provide('shaka.Player');

goog.require('goog.asserts');
goog.require('shaka.config.AutoShowText');
goog.require('shaka.Deprecate');
goog.require('shaka.drm.DrmEngine');
goog.require('shaka.drm.DrmUtils');
Expand Down Expand Up @@ -7829,8 +7828,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
// Check if we should show text (based on difference between audio and text
// languages).
if (initialTextStream) {
if (this.shouldInitiallyShowText_(
initialVariant.audio, initialTextStream)) {
goog.asserts.assert(this.config_, 'Must not be destroyed');
if (shaka.util.StreamUtils.shouldInitiallyShowText(
initialVariant.audio, initialTextStream, this.config_)) {
this.isTextVisible_ = true;
}
if (this.isTextVisible_) {
Expand All @@ -7846,70 +7846,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}
}

/**
* Check if we should show text on screen automatically.
*
* @param {?shaka.extern.Stream} audioStream
* @param {shaka.extern.Stream} textStream
* @return {boolean}
* @private
*/
shouldInitiallyShowText_(audioStream, textStream) {
const AutoShowText = shaka.config.AutoShowText;

if (this.config_.autoShowText == AutoShowText.NEVER) {
return false;
}
if (this.config_.autoShowText == AutoShowText.ALWAYS) {
return true;
}

const LanguageUtils = shaka.util.LanguageUtils;

/** @type {string} */
const preferredTextLocale =
LanguageUtils.normalize(this.config_.preferredTextLanguage);
/** @type {string} */
const textLocale = LanguageUtils.normalize(textStream.language);

if (this.config_.autoShowText == AutoShowText.IF_PREFERRED_TEXT_LANGUAGE) {
// Only the text language match matters.
return LanguageUtils.areLanguageCompatible(
textLocale,
preferredTextLocale);
}

if (this.config_.autoShowText == AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED) {
if (!audioStream) {
return false;
}
/* The text should automatically be shown if the text is
* language-compatible with the user's text language preference, but not
* compatible with the audio. These are cases where we deduce that
* subtitles may be needed.
*
* For example:
* preferred | chosen | chosen |
* text | text | audio | show
* -----------------------------------
* en-CA | en | jp | true
* en | en-US | fr | true
* fr-CA | en-US | jp | false
* en-CA | en-US | en-US | false
*
*/
/** @type {string} */
const audioLocale = LanguageUtils.normalize(audioStream.language);

return (
LanguageUtils.areLanguageCompatible(textLocale, preferredTextLocale) &&
!LanguageUtils.areLanguageCompatible(audioLocale, textLocale));
}

shaka.log.alwaysWarn('Invalid autoShowText setting!');
return false;
}

/**
* Callback from StreamingEngine.
*
Expand Down
66 changes: 66 additions & 0 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
goog.provide('shaka.util.StreamUtils');

goog.require('goog.asserts');
goog.require('shaka.config.AutoShowText');
goog.require('shaka.lcevc.Dec');
goog.require('shaka.log');
goog.require('shaka.media.Capabilities');
Expand Down Expand Up @@ -2003,6 +2004,71 @@ shaka.util.StreamUtils = class {
static clearDecodingConfigCache() {
shaka.util.StreamUtils.decodingConfigCache_.clear();
}


/**
* Check if we should show text on screen automatically.
*
* @param {?shaka.extern.Stream} audioStream
* @param {shaka.extern.Stream} textStream
* @param {!shaka.extern.PlayerConfiguration} config
* @return {boolean}
*/
static shouldInitiallyShowText(audioStream, textStream, config) {
const AutoShowText = shaka.config.AutoShowText;

if (config.autoShowText == AutoShowText.NEVER) {
return false;
}
if (config.autoShowText == AutoShowText.ALWAYS) {
return true;
}

const LanguageUtils = shaka.util.LanguageUtils;

/** @type {string} */
const preferredTextLocale =
LanguageUtils.normalize(config.preferredTextLanguage);
/** @type {string} */
const textLocale = LanguageUtils.normalize(textStream.language);

if (config.autoShowText == AutoShowText.IF_PREFERRED_TEXT_LANGUAGE) {
// Only the text language match matters.
return LanguageUtils.areLanguageCompatible(
textLocale,
preferredTextLocale);
}

if (config.autoShowText == AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED) {
if (!audioStream) {
return false;
}
/* The text should automatically be shown if the text is
* language-compatible with the user's text language preference, but not
* compatible with the audio. These are cases where we deduce that
* subtitles may be needed.
*
* For example:
* preferred | chosen | chosen |
* text | text | audio | show
* -----------------------------------
* en-CA | en | jp | true
* en | en-US | fr | true
* fr-CA | en-US | jp | false
* en-CA | en-US | en-US | false
*
*/
/** @type {string} */
const audioLocale = LanguageUtils.normalize(audioStream.language);

return (
LanguageUtils.areLanguageCompatible(textLocale, preferredTextLocale) &&
!LanguageUtils.areLanguageCompatible(audioLocale, textLocale));
}

shaka.log.alwaysWarn('Invalid autoShowText setting!');
return false;
}
};


Expand Down

0 comments on commit 4e05551

Please sign in to comment.