Skip to content

Commit

Permalink
fix: prevent memory leak in SimpleAbrManager while destroying (#5149)
Browse files Browse the repository at this point in the history
SimpleAbrManager was not properly destroyed after a call to
player.destroy, leading to SimpleAbrManager instances being kept in
memory.

This PR aims to resolve it :)
  • Loading branch information
valotvince authored and joeyparrish committed Apr 26, 2023
1 parent d55479c commit f32b11f
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 5 deletions.
9 changes: 7 additions & 2 deletions externs/network_information.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ NetworkInformation.prototype.saveData;
* @param {string} type
* @param {Function} listener
*/
NetworkInformation.prototype.addEventListener =
function(type, listener) {};
NetworkInformation.prototype.addEventListener = function(type, listener) {};

/**
* @param {string} type
* @param {Function} listener
*/
NetworkInformation.prototype.removeEventListener = function(type, listener) {};
6 changes: 6 additions & 0 deletions externs/shaka/abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ shaka.extern.AbrManager = class {
*/
stop() {}

/**
* Request that this object release all internal references.
* @exportDoc
*/
release() {}

/**
* Updates manager's variants collection.
*
Expand Down
28 changes: 26 additions & 2 deletions lib/abr/simple_abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

goog.provide('shaka.abr.SimpleAbrManager');

goog.require('shaka.util.IReleasable');
goog.require('goog.asserts');
goog.require('shaka.abr.EwmaBandwidthEstimator');
goog.require('shaka.log');
Expand All @@ -32,6 +33,7 @@ goog.require('shaka.util.Timer');
* </p>
*
* @implements {shaka.extern.AbrManager}
* @implements {shaka.util.IReleasable}
* @export
*/
shaka.abr.SimpleAbrManager = class {
Expand All @@ -51,7 +53,7 @@ shaka.abr.SimpleAbrManager = class {
// to the change event to be able to make quick changes in case the type
// of connectivity changes.
if (navigator.connection) {
navigator.connection.addEventListener('change', () => {
this.onNetworkInformationChange_ = () => {
if (this.config_.useNetworkInformation && this.enabled_) {
this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
if (this.config_) {
Expand All @@ -62,7 +64,10 @@ shaka.abr.SimpleAbrManager = class {
this.switch_(chosenVariant);
}
}
});
};

navigator.connection.addEventListener(
'change', this.onNetworkInformationChange_);
}

/**
Expand Down Expand Up @@ -93,6 +98,9 @@ shaka.abr.SimpleAbrManager = class {
/** @private {ResizeObserver} */
this.resizeObserver_ = null;

/** @private {?function():void} */
this.onNetworkInformationChange_ = null;

/** @private {shaka.util.Timer} */
this.resizeObserverTimer_ = new shaka.util.Timer(() => {
if (this.config_.restrictToElementSize) {
Expand Down Expand Up @@ -128,6 +136,22 @@ shaka.abr.SimpleAbrManager = class {
// can start using bandwidth estimates right away after init() is called.
}

/**
* @override
* @export
*/
release() {
// stop() should already have been called for unload

if (navigator.connection) {
navigator.connection.removeEventListener(
'change', this.onNetworkInformationChange_);
this.onNetworkInformationChange_ = null;
}

this.resizeObserverTimer_ = null;
}


/**
* @override
Expand Down
6 changes: 5 additions & 1 deletion lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}

this.abrManagerFactory_ = null;
this.abrManager_ = null;
this.config_ = null;
this.stats_ = null;
this.videoContainer_ = null;
Expand All @@ -908,6 +907,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.networkingEngine_ = null;
}

if (this.abrManager_) {
this.abrManager_.release();
this.abrManager_ = null;
}

// FakeEventTarget implements IReleasable
super.release();
}
Expand Down
3 changes: 3 additions & 0 deletions test/player_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ describe('Player', () => {
await player.destroy();

expect(abrManager.stop).toHaveBeenCalled();
expect(abrManager.release).toHaveBeenCalled();
expect(networkingEngine.destroy).toHaveBeenCalled();
expect(drmEngine.destroy).toHaveBeenCalled();
expect(playhead.release).toHaveBeenCalled();
Expand Down Expand Up @@ -246,6 +247,7 @@ describe('Player', () => {
parser.start.and.returnValue(p);
parser.stop.and.callFake(() => {
expect(abrManager.stop).not.toHaveBeenCalled();
expect(abrManager.release).not.toHaveBeenCalled();
expect(networkingEngine.destroy).not.toHaveBeenCalled();
});
shaka.media.ManifestParser.registerParserByMime(
Expand All @@ -255,6 +257,7 @@ describe('Player', () => {
await shaka.test.Util.shortDelay();
await player.destroy();
expect(abrManager.stop).toHaveBeenCalled();
expect(abrManager.release).toHaveBeenCalled();
expect(networkingEngine.destroy).toHaveBeenCalled();
expect(parser.stop).toHaveBeenCalled();
await expectAsync(load).toBeRejected();
Expand Down
3 changes: 3 additions & 0 deletions test/test/util/simple_fakes.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ shaka.test.FakeAbrManager = class {
/** @type {!jasmine.Spy} */
this.stop = jasmine.createSpy('stop');

/** @type {!jasmine.Spy} */
this.release = jasmine.createSpy('release');

/** @type {!jasmine.Spy} */
this.enable = jasmine.createSpy('enable');

Expand Down

0 comments on commit f32b11f

Please sign in to comment.