diff --git a/src/compat/event_listeners.ts b/src/compat/event_listeners.ts index 24d1df96a4..cd5047bbd9 100644 --- a/src/compat/event_listeners.ts +++ b/src/compat/event_listeners.ts @@ -188,26 +188,26 @@ function getDocumentVisibilityRef( let prefix : string|undefined; const doc = document as ICompatDocument; - if (doc.hidden != null) { + if (!isNullOrUndefined(doc.hidden)) { prefix = ""; - } else if (doc.mozHidden != null) { + } else if (!isNullOrUndefined(doc.mozHidden)) { prefix = "moz"; - } else if (doc.msHidden != null) { + } else if (!isNullOrUndefined(doc.msHidden)) { prefix = "ms"; - } else if (doc.webkitHidden != null) { + } else if (!isNullOrUndefined(doc.webkitHidden)) { prefix = "webkit"; } - const hidden = isNonEmptyString(prefix) ? prefix + "Hidden" : + const hidden = isNonEmptyString(prefix) ? (prefix + "Hidden" as "hidden") : "hidden"; const visibilityChangeEvent = isNonEmptyString(prefix) ? prefix + "visibilitychange" : "visibilitychange"; - const isHidden = document[hidden as "hidden"]; + const isHidden = document[hidden]; const ref = new SharedReference(!isHidden, stopListening); addEventListener(document, visibilityChangeEvent, () => { - const isVisible = !(document[hidden as "hidden"]); + const isVisible = !(document[hidden]); ref.setValueIfChanged(isVisible); }, stopListening); diff --git a/src/core/adaptive/__tests__/buffer_based_chooser.test.ts b/src/core/adaptive/__tests__/buffer_based_chooser.test.ts index a1ffb8308a..4a11f09dff 100644 --- a/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +++ b/src/core/adaptive/__tests__/buffer_based_chooser.test.ts @@ -92,7 +92,7 @@ describe("BufferBasedChooser", () => { /* eslint-disable max-len */ it("should log an error and return the first bitrate if the given bitrate does not exist", () => { /* eslint-enable max-len */ - const logger = { debug: jest.fn(), error: jest.fn() }; + const logger = { debug: jest.fn(), info: jest.fn() }; jest.mock("../../../log", () => ({ __esModule: true as const, default: logger })); const BufferBasedChooser = jest.requireActual("../buffer_based_chooser").default; @@ -105,8 +105,8 @@ describe("BufferBasedChooser", () => { currentScore: undefined, }); expect(bbc.getLastEstimate()).toEqual(10); - expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error) + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info) .toHaveBeenCalledWith("ABR: Current Bitrate not found in the calculated levels"); }); diff --git a/src/core/adaptive/adaptive_representation_selector.ts b/src/core/adaptive/adaptive_representation_selector.ts index b1dc0d9c6b..2cff0bdd07 100644 --- a/src/core/adaptive/adaptive_representation_selector.ts +++ b/src/core/adaptive/adaptive_representation_selector.ts @@ -200,28 +200,46 @@ function getEstimateReference( return { estimates: estimateRef, callbacks }; + /** + * Emit the ABR estimates on various events through the returned + * `SharedReference`. + * @param {Array.} unsortedRepresentations - All `Representation` that + * the ABR logic can choose from. + * @param {Object} innerCancellationSignal - When this `CancellationSignal` + * emits, all events registered to to emit new estimates will be unregistered. + * Note that the returned reference may still be used to produce new estimates + * in the future if you want to even after this signal emits. + * @returns {Object} - `SharedReference` through which ABR estimates will be + * produced. + */ function createEstimateReference( - representations : Representation[], + unsortedRepresentations : Representation[], innerCancellationSignal : CancellationSignal ) : SharedReference { - if (representations.length <= 1) { + if (unsortedRepresentations.length <= 1) { // There's only a single Representation. Just choose it. - return new SharedReference({ bitrate: undefined, - representation: representations[0], - urgent: true, - knownStableBitrate: undefined }); + return new SharedReference({ + bitrate: undefined, + representation: unsortedRepresentations[0], + urgent: true, + knownStableBitrate: undefined, + }); } /** If true, Representation estimates based on the buffer health might be used. */ let allowBufferBasedEstimates = false; + /** Ensure `Representation` objects are sorted by bitrates and only rely on this. */ + const sortedRepresentations = unsortedRepresentations + .sort((ra, rb) => ra.bitrate - rb.bitrate); + /** * Module calculating the optimal Representation based on the current * buffer's health (i.e. whether enough data is buffered, history of * buffer size etc.). */ const bufferBasedChooser = new BufferBasedChooser( - representations.map(r => r.bitrate) + sortedRepresentations.map(r => r.bitrate) ); /** Store the previous estimate made here. */ @@ -282,7 +300,7 @@ function getEstimateReference( const bitrateThrottle = filters.throttleBitrate.getValue(); const currentRepresentationVal = currentRepresentation.getValue(); - const filteredReps = getFilteredRepresentations(representations, + const filteredReps = getFilteredRepresentations(sortedRepresentations, resolutionLimit, bitrateThrottle); const requests = requestsStore.getRequests(); @@ -372,7 +390,7 @@ function getEstimateReference( maximumPosition - position.last < 40) { chosenRepFromGuessMode = guessBasedChooser - .getGuess(representations, + .getGuess(sortedRepresentations, lastPlaybackObservation, currentRepresentationVal, currentBestBitrate, diff --git a/src/core/adaptive/buffer_based_chooser.ts b/src/core/adaptive/buffer_based_chooser.ts index b16f3c141a..bc85354d49 100644 --- a/src/core/adaptive/buffer_based_chooser.ts +++ b/src/core/adaptive/buffer_based_chooser.ts @@ -153,7 +153,7 @@ export default class BufferBasedChooser { } if (currentBitrateIndex < 0 || bitrates.length !== bufferLevels.length) { - log.error("ABR: Current Bitrate not found in the calculated levels"); + log.info("ABR: Current Bitrate not found in the calculated levels"); this._currentEstimate = bitrates[0]; return ; } diff --git a/src/core/adaptive/network_analyzer.ts b/src/core/adaptive/network_analyzer.ts index d0e61db7ef..4096b27037 100644 --- a/src/core/adaptive/network_analyzer.ts +++ b/src/core/adaptive/network_analyzer.ts @@ -18,6 +18,7 @@ import config from "../../config"; import log from "../../log"; import { Representation } from "../../manifest"; import arrayFind from "../../utils/array_find"; +import isNullOrUndefined from "../../utils/is_null_or_undefined"; import getMonotonicTimeStamp from "../../utils/monotonic_timestamp"; import { IRepresentationEstimatorPlaybackObservation, @@ -359,10 +360,10 @@ export default class NetworkAnalyzer { this._lowLatencyMode, lastEstimatedBitrate); - if (bandwidthEstimate != null) { + if (bandwidthEstimate !== undefined) { log.info("ABR: starvation mode emergency estimate:", bandwidthEstimate); bandwidthEstimator.reset(); - newBitrateCeil = currentRepresentation == null ? + newBitrateCeil = isNullOrUndefined(currentRepresentation) ? bandwidthEstimate : Math.min(bandwidthEstimate, currentRepresentation.bitrate); } @@ -372,11 +373,11 @@ export default class NetworkAnalyzer { if (newBitrateCeil == null) { bandwidthEstimate = bandwidthEstimator.getEstimate(); - if (bandwidthEstimate != null) { + if (bandwidthEstimate !== undefined) { newBitrateCeil = bandwidthEstimate * (this._inStarvationMode ? localConf.starvationBitrateFactor : localConf.regularBitrateFactor); - } else if (lastEstimatedBitrate != null) { + } else if (lastEstimatedBitrate !== undefined) { newBitrateCeil = lastEstimatedBitrate * (this._inStarvationMode ? localConf.starvationBitrateFactor : localConf.regularBitrateFactor); diff --git a/src/core/api/track_management/track_dispatcher.ts b/src/core/api/track_management/track_dispatcher.ts index e233bf0ba0..10cc537af2 100644 --- a/src/core/api/track_management/track_dispatcher.ts +++ b/src/core/api/track_management/track_dispatcher.ts @@ -209,7 +209,8 @@ export default class TrackDispatcher extends EventEmitter // Check if Locked Representations have changed const oldRef = reference.getValue(); - const sortedReps = playableRepresentations.slice().sort(); + const sortedReps = playableRepresentations.slice() + .sort((ra, rb) => ra.bitrate - rb.bitrate); if (sortedReps.length !== oldRef.representations.length) { reference.setValue({ representations: sortedReps, switchingMode }); return;