Skip to content

chore: Simplify ABR management between player and preload manager #8890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/abr/simple_abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ shaka.abr.SimpleAbrManager = class {
this.resizeObserver_ = null;
}

this.resizeObserverTimer_.stop();
if (this.resizeObserverTimer_) {
this.resizeObserverTimer_.stop();
}

this.pictureInPictureWindow_ = null;

Expand All @@ -177,7 +179,7 @@ shaka.abr.SimpleAbrManager = class {
* @export
*/
release() {
// stop() should already have been called for unload
this.stop();
this.eventManager_.release();
this.resizeObserverTimer_ = null;
}
Expand Down
83 changes: 20 additions & 63 deletions lib/media/preload_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,9 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
/** @private {boolean} */
this.drmEngineEntrusted_ = false;

/** @private {?shaka.extern.AbrManager.Factory} */
this.abrManagerFactory_ = null;

/** @private {shaka.extern.AbrManager} */
this.abrManager_ = null;

/** @private {boolean} */
this.abrManagerEntrusted_ = false;

/** @private {!Map<number, shaka.media.SegmentPrefetch>} */
this.segmentPrefetchById_ = new Map();

Expand Down Expand Up @@ -150,9 +144,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
/** @private {?shaka.extern.Stream} */
this.prefetchedTextStream_ = null;

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

/** @private {boolean} */
this.hasBeenAttached_ = false;

Expand Down Expand Up @@ -257,28 +248,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
return this.currentAdaptationSetCriteria_;
}

/** @return {?shaka.extern.AbrManager.Factory} */
getAbrManagerFactory() {
return this.abrManagerFactory_;
}

/**
* Gets the abr manager, if it exists. Also marks that the abr manager should
* not be stopped if this manager is destroyed.
* @return {?shaka.extern.AbrManager}
*/
receiveAbrManager() {
this.abrManagerEntrusted_ = true;
return this.abrManager_;
}

/**
* @return {?shaka.extern.AbrManager}
*/
getAbrManager() {
return this.abrManager_;
}

/**
* Gets the parser, if it exists. Also marks that the parser should not be
* stopped if this manager is destroyed.
Expand Down Expand Up @@ -388,15 +357,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
return this.segmentPrefetchById_;
}

/**
* @param {?shaka.extern.AbrManager} abrManager
* @param {?shaka.extern.AbrManager.Factory} abrFactory
*/
attachAbrManager(abrManager, abrFactory) {
this.abrManager_ = abrManager;
this.abrManagerFactory_ = abrFactory;
}

/**
* @param {?shaka.media.AdaptationSetCriteria} adaptationSetCriteria
*/
Expand Down Expand Up @@ -701,25 +661,18 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
});
}

// Make the ABR manager.
if (this.allowMakeAbrManager_) {
const abrFactory = this.config_.abrFactory;
this.abrManagerFactory_ = abrFactory;
this.abrManager_ = abrFactory();
this.abrManager_.configure(this.config_.abr);
}

const variant = this.configureAbrManagerAndChooseVariant_();
if (variant &&
this.shouldCreateSegmentIndexBeforeDrmEngineInitialization_()) {
const createSegmentIndexPromises = [];
for (const stream of [variant.video, variant.audio]) {
if (stream && !stream.segmentIndex) {
createSegmentIndexPromises.push(stream.createSegmentIndex());
if (this.shouldCreateSegmentIndexBeforeDrmEngineInitialization_()) {
const variant = this.configureAbrManagerAndChooseVariant_();
if (variant) {
const createSegmentIndexPromises = [];
for (const stream of [variant.video, variant.audio]) {
if (stream && !stream.segmentIndex) {
createSegmentIndexPromises.push(stream.createSegmentIndex());
}
}
if (createSegmentIndexPromises.length > 0) {
await Promise.all(createSegmentIndexPromises);
}
}
if (createSegmentIndexPromises.length > 0) {
await Promise.all(createSegmentIndexPromises);
}
}
}
Expand Down Expand Up @@ -784,12 +737,18 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
goog.asserts.assert(this.currentAdaptationSetCriteria_,
'Must have an AdaptationSetCriteria');

if (!this.abrManager_) {
// Make the ABR manager.
const abrFactory = this.config_.abrFactory;
this.abrManager_ = abrFactory();
this.abrManager_.configure(this.config_.abr);
}

const playableVariants = shaka.util.StreamUtils.getPlayableVariants(
this.manifest_.variants);
const adaptationSet = this.currentAdaptationSetCriteria_.create(
playableVariants);
// Guess what the first variant will be, based on a SimpleAbrManager.
this.abrManager_.configure(this.config_.abr);
this.abrManager_.setVariants(Array.from(adaptationSet.values()));

return this.abrManager_.chooseVariant(/* preferFastSwitching= */ true);
Expand Down Expand Up @@ -895,8 +854,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
if (this.parser_ && !this.parserEntrusted_) {
await this.parser_.stop();
}
if (this.abrManager_ && !this.abrManagerEntrusted_) {
await this.abrManager_.stop();
if (this.abrManager_) {
this.abrManager_.release();
}
if (this.regionTimeline_ && !this.regionTimelineEntrusted_) {
this.regionTimeline_.release();
Expand Down Expand Up @@ -965,7 +924,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
* networkingEngine: !shaka.net.NetworkingEngine,
* manifestFilterer: !shaka.media.ManifestFilterer,
* allowPrefetch: boolean,
* allowMakeAbrManager: boolean,
* }}
*
* @property {!shaka.extern.PlayerConfiguration} config
Expand All @@ -978,6 +936,5 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
* @property {!shaka.net.NetworkingEngine} networkingEngine
* @property {!shaka.media.ManifestFilterer} manifestFilterer
* @property {boolean} allowPrefetch
* @property {boolean} allowMakeAbrManager
*/
shaka.media.PreloadManager.PlayerInterface;
72 changes: 22 additions & 50 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
// components, we do not release the instance, we will reuse it in later
// loads.
if (this.abrManager_) {
await this.abrManager_.stop();
this.abrManager_.stop();
}

// Streaming engine will push new data to media source engine, so we need
Expand Down Expand Up @@ -1945,15 +1945,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
await this.drmEngine_.attach(this.video_);
}, 'drmEngine_.attach');

// Also get the ABR manager, which has special logic related to being
// received.
const abrManagerFactory = preloadManager.getAbrManagerFactory();
if (abrManagerFactory) {
const abrManager = preloadManager.receiveAbrManager();
if (this.abrManager_ && this.abrManager_ !== abrManager) {

const abrFactory = this.config_.abrFactory;
if (!this.abrManager_ || this.abrManagerFactory_ != abrFactory) {
this.abrManagerFactory_ = abrFactory;
if (this.abrManager_) {
this.abrManager_.release();
}
this.abrManager_ = abrManager;
this.abrManager_ = abrFactory();
if (typeof this.abrManager_.setMediaElement != 'function') {
shaka.Deprecate.deprecateFeature(5,
'AbrManager w/o setMediaElement',
Expand All @@ -1972,7 +1971,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
'Please use an AbrManager with trySuggestStreams function.');
this.abrManager_.trySuggestStreams = () => {};
}
this.abrManagerFactory_ = abrManagerFactory;
this.abrManager_.configure(this.config_.abr);
}

// Load the asset.
Expand Down Expand Up @@ -2087,8 +2086,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.mimeType_,
this.config_,
/* allowPrefetch= */ true,
/* disableVideo= */ false,
/* allowMakeAbrManager= */ false);
/* disableVideo= */ false);
this.createdPreloadManagers_.push(preloadManager);
if (this.parser_ && this.parser_.setMediaElement) {
this.parser_.setMediaElement(/* mediaElement= */ null);
Expand Down Expand Up @@ -2192,25 +2190,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}
const preloadConfig = config || this.config_;
let disableVideo = false;
let allowMakeAbrManager = true;
if (standardLoad) {
if (this.abrManager_ &&
this.abrManagerFactory_ == preloadConfig.abrFactory) {
// If there's already an abr manager, don't make a new abr manager at
// all.
// In standardLoad mode, the abr manager isn't used for anything anyway,
// so it should only be created to create an abr manager for the player
// to use... which is unnecessary if we already have one of the right
// type.
allowMakeAbrManager = false;
}
if (this.video_ && this.video_.nodeName === 'AUDIO') {
disableVideo = true;
}
if (standardLoad && this.video_ && this.video_.nodeName === 'AUDIO') {
disableVideo = true;
}
let preloadManagerPromise = this.makePreloadManager_(
assetUri, startTime, mimeType || null, preloadConfig,
/* allowPrefetch= */ !standardLoad, disableVideo, allowMakeAbrManager);
/* allowPrefetch= */ !standardLoad, disableVideo);
if (!standardLoad) {
// We only need to track the PreloadManager if it is not part of a
// standard load. If it is, the load() method will handle destroying it.
Expand All @@ -2237,12 +2222,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
* @param {shaka.extern.PlayerConfiguration} preloadConfig
* @param {boolean=} allowPrefetch
* @param {boolean=} disableVideo
* @param {boolean=} allowMakeAbrManager
* @return {!Promise<!shaka.media.PreloadManager>}
* @private
*/
async makePreloadManager_(assetUri, startTime, mimeType, preloadConfig,
allowPrefetch = true, disableVideo = false, allowMakeAbrManager = true) {
allowPrefetch = true, disableVideo = false) {
goog.asserts.assert(this.networkingEngine_, 'Must have net engine');
/** @type {?shaka.media.PreloadManager} */
let preloadManager = null;
Expand Down Expand Up @@ -2466,14 +2450,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
manifestFilterer,
networkingEngine,
allowPrefetch,
allowMakeAbrManager,
};
preloadManager = new shaka.media.PreloadManager(
assetUri, mimeType, startTime, playerInterface);
if (!allowMakeAbrManager) {
preloadManager.attachAbrManager(
this.abrManager_, this.abrManagerFactory_);
}
return preloadManager;
}

Expand Down Expand Up @@ -3746,13 +3725,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
getPreloadManager = () => null;
}

const getAbrManager = () => {
if (getPreloadManager()) {
return getPreloadManager().getAbrManager();
} else {
return this.abrManager_;
}
};
const getParser = () => {
if (getPreloadManager()) {
return getPreloadManager().getParser();
Expand Down Expand Up @@ -3784,14 +3756,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
/** @type {shaka.net.NetworkingEngine.onProgressUpdated} */
const onProgressUpdated_ = (deltaTimeMs,
bytesDownloaded, allowSwitch, request, context) => {
// In some situations, such as during offline storage, the abr manager
// might not yet exist. Therefore, we need to check if abr manager has
// been initialized before using it.
const abrManager = getAbrManager();
if (abrManager) {
abrManager.segmentDownloaded(deltaTimeMs, bytesDownloaded,
allowSwitch, request, context);
}
lateQueue(() => {
if (this.abrManager_) {
this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded,
allowSwitch, request, context);
}
});
};
/** @type {shaka.net.NetworkingEngine.OnHeadersReceived} */
const onHeadersReceived_ = (headers, request, requestType) => {
Expand Down Expand Up @@ -3831,7 +3801,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
/** @type {shaka.net.NetworkingEngine.OnRequest} */
const onRequest_ = (type, request, context) => {
lateQueue(() => {
this.cmcdManager_.applyRequestData(type, request, context);
if (this.cmcdManager_) {
this.cmcdManager_.applyRequestData(type, request, context);
}
});
};

Expand Down
Loading