UNPKG

rx-player

Version:
1,020 lines 61.2 kB
"use strict"; /** * Copyright 2015 CANAL+ Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); /** * This file is used to abstract the notion of text, audio and video tracks * switching for an easier API management. */ var config_1 = require("../../config"); var errors_1 = require("../../errors"); var log_1 = require("../../log"); var manifest_1 = require("../../manifest"); var array_find_1 = require("../../utils/array_find"); var assert_1 = require("../../utils/assert"); var event_emitter_1 = require("../../utils/event_emitter"); var is_null_or_undefined_1 = require("../../utils/is_null_or_undefined"); var object_assign_1 = require("../../utils/object_assign"); var reference_1 = require("../../utils/reference"); var track_dispatcher_1 = require("./track_dispatcher"); /** * Class helping with the management of the audio, video and text tracks and * qualities. * * The `TracksStore` allows to choose a track and qualities for different types * of media through a simpler API. * * @class TracksStore */ var TracksStore = /** @class */ (function (_super) { __extends(TracksStore, _super); function TracksStore(args) { var _a; var _this = _super.call(this) || this; _this._storedPeriodInfo = []; _this._isDisposed = false; _this._cachedPeriodInfo = new WeakMap(); _this._isTrickModeTrackEnabled = args.preferTrickModeTracks; _this._defaultAudioTrackSwitchingMode = (_a = args.defaultAudioTrackSwitchingMode) !== null && _a !== void 0 ? _a : config_1.default.getCurrent().DEFAULT_AUDIO_TRACK_SWITCHING_MODE; return _this; } /** * Return Array of Period information, to allow an outside application to * modify the track of any Period. * @returns {Array.<Object>} */ TracksStore.prototype.getAvailablePeriods = function () { return this._storedPeriodInfo.reduce(function (acc, p) { if (p.isPeriodAdvertised) { acc.push(toExposedPeriod(p.period)); } return acc; }, []); }; /** * Callack that needs to be called as codec support is either first known or * updated on the Manifest. */ TracksStore.prototype.onManifestCodecSupportUpdate = function () { this._selectInitialTrackIfNeeded(); }; /** * Update the list of Periods handled by the TracksStore and make a * track choice decision for each of them. * @param {Object} manifest - The new Manifest object */ TracksStore.prototype.onManifestUpdate = function (manifest) { var _a, e_1, _b; var _c, _d, _e, _f, _g, _h; var DEFAULT_VIDEO_TRACK_SWITCHING_MODE = config_1.default.getCurrent().DEFAULT_VIDEO_TRACK_SWITCHING_MODE; var periods = manifest.periods; // We assume that they are always sorted chronologically // In dev mode, perform a runtime check that this is the case if (0 /* __ENVIRONMENT__.CURRENT_ENV */ === 1 /* __ENVIRONMENT__.DEV */) { for (var i = 1; i < periods.length; i++) { (0, assert_1.default)(periods[i - 1].start <= periods[i].start); } } /** Periods which have just been added. */ var addedPeriods = []; var newPListIdx = 0; var _loop_1 = function (i) { var oldPeriod = this_1._storedPeriodInfo[i].period; var newPeriod = periods[newPListIdx]; if (newPeriod === undefined) { // We reached the end of the new Periods, remove remaining old Periods for (var j = this_1._storedPeriodInfo.length - 1; j >= i; j--) { this_1._storedPeriodInfo[j].inManifest = false; if (isPeriodItemRemovable(this_1._storedPeriodInfo[j])) { this_1._removePeriodObject(j); } } } else if (oldPeriod === newPeriod) { newPListIdx++; var curWantedTextTrack_1 = this_1._storedPeriodInfo[i].text.storedSettings; if (!(0, is_null_or_undefined_1.default)(curWantedTextTrack_1)) { var textAdaptations = (0, manifest_1.getSupportedAdaptations)(newPeriod, "text"); var stillHere = textAdaptations.some(function (a) { return a.id === curWantedTextTrack_1.adaptation.id; }); if (!stillHere) { log_1.default.warn("TS: Chosen text Adaptation not available anymore"); var periodInfo = this_1._storedPeriodInfo[i]; periodInfo.text.storedSettings = null; this_1.trigger("trackUpdate", { period: toExposedPeriod(newPeriod), trackType: "text", reason: "missing", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this_1._isDisposed) { return { value: void 0 }; } var periodItem = getPeriodItem(this_1._storedPeriodInfo, periodInfo.period.id); if (periodItem !== undefined && periodItem.isPeriodAdvertised && periodItem.text.storedSettings === null) { (_c = periodItem.text.dispatcher) === null || _c === void 0 ? void 0 : _c.updateTrack(null); } } } var curWantedVideoTrack_1 = this_1._storedPeriodInfo[i].video.storedSettings; if (!(0, is_null_or_undefined_1.default)(curWantedVideoTrack_1)) { var videoAdaptations = (0, manifest_1.getSupportedAdaptations)(newPeriod, "video"); var stillHere = videoAdaptations.some(function (a) { return a.id === curWantedVideoTrack_1.adaptation.id; }); if (!stillHere) { log_1.default.warn("TS: Chosen video Adaptation not available anymore"); var periodItem = this_1._storedPeriodInfo[i]; var storedSettings = void 0; if (videoAdaptations.length === 0) { storedSettings = null; } else { var adaptationBase = videoAdaptations[0]; var adaptation = getRightVideoTrack(adaptationBase, this_1._isTrickModeTrackEnabled); var lockedRepresentations = new reference_1.default(null); storedSettings = { adaptationBase: adaptationBase, adaptation: adaptation, switchingMode: DEFAULT_VIDEO_TRACK_SWITCHING_MODE, lockedRepresentations: lockedRepresentations, }; } periodItem.video.storedSettings = storedSettings; this_1.trigger("trackUpdate", { period: toExposedPeriod(newPeriod), trackType: "video", reason: "missing", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this_1._isDisposed) { return { value: void 0 }; } var newPeriodItem = getPeriodItem(this_1._storedPeriodInfo, periodItem.period.id); if (newPeriodItem !== undefined && newPeriodItem.isPeriodAdvertised && newPeriodItem.video.storedSettings === storedSettings) { (_d = newPeriodItem.video.dispatcher) === null || _d === void 0 ? void 0 : _d.updateTrack(storedSettings); } } } var curWantedAudioTrack_1 = this_1._storedPeriodInfo[i].audio.storedSettings; if (!(0, is_null_or_undefined_1.default)(curWantedAudioTrack_1)) { var audioAdaptations = (0, manifest_1.getSupportedAdaptations)(newPeriod, "audio"); var stillHere = audioAdaptations.some(function (a) { return a.id === curWantedAudioTrack_1.adaptation.id; }); if (!stillHere) { log_1.default.warn("TS: Chosen audio Adaptation not available anymore"); var periodItem = this_1._storedPeriodInfo[i]; var storedSettings = audioAdaptations.length === 0 ? null : { adaptation: audioAdaptations[0], switchingMode: this_1._defaultAudioTrackSwitchingMode, lockedRepresentations: new reference_1.default(null), }; periodItem.audio.storedSettings = storedSettings; this_1.trigger("trackUpdate", { period: toExposedPeriod(newPeriod), trackType: "audio", reason: "missing", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this_1._isDisposed) { return { value: void 0 }; } var newPeriodItem = getPeriodItem(this_1._storedPeriodInfo, periodItem.period.id); if (newPeriodItem !== undefined && newPeriodItem.isPeriodAdvertised && newPeriodItem.audio.storedSettings === storedSettings) { (_e = newPeriodItem.audio.dispatcher) === null || _e === void 0 ? void 0 : _e.updateTrack(storedSettings); } } } // (If not, what do?) } else if (oldPeriod.start <= newPeriod.start) { // This old Period does not exist anymore. this_1._storedPeriodInfo[i].inManifest = false; if (isPeriodItemRemovable(this_1._storedPeriodInfo[i])) { this_1._removePeriodObject(i); i--; } } else { var newPeriodInfo = generatePeriodInfo(newPeriod, true); // oldPeriod.start > newPeriod.start: insert newPeriod before this_1._storedPeriodInfo.splice(i, 0, newPeriodInfo); addedPeriods.push(newPeriodInfo); newPListIdx++; // Note: we don't increment `i` on purpose here, as we want to check the // same oldPeriod at the next loop iteration } out_i_1 = i; }; var this_1 = this, out_i_1; for (var i = 0; i < this._storedPeriodInfo.length; i++) { var state_1 = _loop_1(i); i = out_i_1; if (typeof state_1 === "object") return state_1.value; } if (newPListIdx < periods.length) { // Add further new Period var periodsToAdd = periods .slice(newPListIdx) .map(function (p) { return generatePeriodInfo(p, true); }); (_a = this._storedPeriodInfo).push.apply(_a, __spreadArray([], __read(periodsToAdd), false)); addedPeriods.push.apply(addedPeriods, __spreadArray([], __read(periodsToAdd), false)); } try { for (var _j = __values(this._storedPeriodInfo), _k = _j.next(); !_k.done; _k = _j.next()) { var storedPeriodInfo = _k.value; (_f = storedPeriodInfo.audio.dispatcher) === null || _f === void 0 ? void 0 : _f.refresh(); (_g = storedPeriodInfo.video.dispatcher) === null || _g === void 0 ? void 0 : _g.refresh(); (_h = storedPeriodInfo.text.dispatcher) === null || _h === void 0 ? void 0 : _h.refresh(); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_k && !_k.done && (_b = _j.return)) _b.call(_j); } finally { if (e_1) throw e_1.error; } } }; TracksStore.prototype.onDecipherabilityUpdates = function () { var e_2, _a; var _b, _c, _d; try { for (var _e = __values(this._storedPeriodInfo), _f = _e.next(); !_f.done; _f = _e.next()) { var storedPeriodInfo = _f.value; (_b = storedPeriodInfo.audio.dispatcher) === null || _b === void 0 ? void 0 : _b.refresh(); (_c = storedPeriodInfo.video.dispatcher) === null || _c === void 0 ? void 0 : _c.refresh(); (_d = storedPeriodInfo.text.dispatcher) === null || _d === void 0 ? void 0 : _d.refresh(); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_f && !_f.done && (_a = _e.return)) _a.call(_e); } finally { if (e_2) throw e_2.error; } } }; /** * Add shared reference to choose Adaptation for new "audio", "video" or * "text" Period. * * Note that such reference has to be removed through `removeTrackReference` * so ressources can be freed. * @param {string} bufferType - The concerned buffer type * @param {Period} period - The concerned Period. * @param {Object} adaptationRef - A reference through which * the choice will be given. */ TracksStore.prototype.addTrackReference = function (bufferType, period, adaptationRef) { var e_3, _a; var _this = this; log_1.default.debug("TS: Adding Track Reference", bufferType, period.id); var periodObj = getPeriodItem(this._storedPeriodInfo, period.id); if (periodObj === undefined) { // The Period has not yet been added. periodObj = generatePeriodInfo(period, false); var found = false; for (var i = 0; i < this._storedPeriodInfo.length; i++) { if (this._storedPeriodInfo[i].period.start > period.start) { this._storedPeriodInfo.splice(i, 0, periodObj); found = true; } } if (!found) { this._storedPeriodInfo.push(periodObj); } } if (periodObj[bufferType].dispatcher !== null) { log_1.default.error("TS: Subject already added for ".concat(bufferType, " ") + "and Period ".concat(period.start)); periodObj[bufferType].dispatcher.dispose(); } var dispatcher = new track_dispatcher_1.default(adaptationRef); periodObj[bufferType].dispatcher = dispatcher; dispatcher.addEventListener("noPlayableRepresentation", function () { var _a, _b, _c, _d; var nextAdaptation = (0, array_find_1.default)((_a = period.adaptations[bufferType]) !== null && _a !== void 0 ? _a : [], function (adaptation) { if (adaptation.supportStatus.hasSupportedCodec === false || adaptation.supportStatus.isDecipherable === false) { return false; } var playableRepresentations = adaptation.representations.filter(function (r) { return (0, manifest_1.isRepresentationPlayable)(r) === true; }); return playableRepresentations.length > 0; }); if (nextAdaptation === undefined) { var noRepErr = new errors_1.MediaError("NO_PLAYABLE_REPRESENTATION", "No ".concat(bufferType, " Representation can be played"), { tracks: undefined }); _this.trigger("error", noRepErr); _this.dispose(); return; } var typeInfo = (_b = getPeriodItem(_this._storedPeriodInfo, period.id)) === null || _b === void 0 ? void 0 : _b[bufferType]; if ((0, is_null_or_undefined_1.default)(typeInfo)) { return; } var switchingMode = bufferType === "audio" ? _this._defaultAudioTrackSwitchingMode : "reload"; var storedSettings = { adaptation: nextAdaptation, switchingMode: switchingMode, lockedRepresentations: new reference_1.default(null), }; typeInfo.storedSettings = storedSettings; _this.trigger("trackUpdate", { period: toExposedPeriod(period), trackType: bufferType, reason: "no-playable-representation", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (_this._isDisposed) { return; // Someone disposed the `TracksStore` on the previous side-effect } typeInfo = (_c = getPeriodItem(_this._storedPeriodInfo, period.id)) === null || _c === void 0 ? void 0 : _c[bufferType]; if ((0, is_null_or_undefined_1.default)(typeInfo) || typeInfo.storedSettings !== storedSettings) { return; } (_d = typeInfo.dispatcher) === null || _d === void 0 ? void 0 : _d.updateTrack(storedSettings); }); dispatcher.addEventListener("noPlayableLockedRepresentation", function () { // TODO check that it doesn't already lead to segment loading or MediaSource // reloading if (periodObj === undefined) { return; } _this.unlockVideoRepresentations(periodObj); _this.trigger("brokenRepresentationsLock", { period: { id: period.id, start: period.start, end: period.end }, trackType: bufferType, }); }); this._selectInitialTrackIfNeeded(); // Ensure `newAvailablePeriods` is sent if (this._shouldAdvertisePeriod(periodObj)) { periodObj.isPeriodAdvertised = true; this.trigger("newAvailablePeriods", [ { id: period.id, start: period.start, end: period.end, }, ]); if (this._isDisposed) { return; } } // Ensure the initial track is set for each type now) var trackTypes = ["audio", "video", "text"]; try { for (var trackTypes_1 = __values(trackTypes), trackTypes_1_1 = trackTypes_1.next(); !trackTypes_1_1.done; trackTypes_1_1 = trackTypes_1.next()) { var ttype = trackTypes_1_1.value; var trackObj = periodObj[ttype]; if (periodObj.isPeriodAdvertised && trackObj.dispatcher !== null && !trackObj.dispatcher.hasSetTrack() && trackObj.storedSettings !== undefined) { trackObj.dispatcher.updateTrack(trackObj.storedSettings); } if (this._isDisposed) { return; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (trackTypes_1_1 && !trackTypes_1_1.done && (_a = trackTypes_1.return)) _a.call(trackTypes_1); } finally { if (e_3) throw e_3.error; } } }; /** * Remove shared reference to choose an "audio", "video" or "text" Adaptation * for a Period. * @param {string} bufferType - The concerned buffer type * @param {string} periodId - The concerned Period's `id`. */ TracksStore.prototype.removeTrackReference = function (bufferType, periodId) { log_1.default.debug("TS: Removing Track Reference", bufferType, periodId); var periodIndex; for (var i = 0; i < this._storedPeriodInfo.length; i++) { var periodI = this._storedPeriodInfo[i]; if (periodI.period.id === periodId) { periodIndex = i; break; } } if (periodIndex === undefined) { log_1.default.warn("TS: ".concat(bufferType, " not found for period"), periodId); return; } var periodObj = this._storedPeriodInfo[periodIndex]; var choiceItem = periodObj[bufferType]; if ((choiceItem === null || choiceItem === void 0 ? void 0 : choiceItem.dispatcher) === null) { log_1.default.warn("TS: TrackDispatcher already removed for ".concat(bufferType, " ") + "and Period ".concat(periodId)); return; } choiceItem.dispatcher.dispose(); choiceItem.dispatcher = null; if (isPeriodItemRemovable(periodObj)) { this._removePeriodObject(periodIndex); } }; /** * Allows to recuperate a "Period Object" - used in get/set methods of the * `TracksStore` - by giving the Period itself. * * This method should be preferred when possible over `getPeriodObjectFromId` * because it is able to fallback on an internal cache in case the * corresponding Period is not stored anymore. * This for example could happen when a Period has been removed from the * Manifest yet may still be needed (e.g. because its linked segments might * still live in the buffers). * * Note however that this cache-retrieval logic is based on a Map whose key * is the Period's JavaScript reference. As such, the cache won't be used if * `Period` corresponds to a copy of the original `Period` object. * * @param {Object} period * @returns {Object} */ TracksStore.prototype.getPeriodObjectFromPeriod = function (period) { var periodObj = getPeriodItem(this._storedPeriodInfo, period.id); if (periodObj === undefined && period !== undefined) { return this._cachedPeriodInfo.get(period); } return periodObj; }; /** * Allows to recuperate a "Period Object" - used in get/set methods of the * `TracksStore` - by giving the Period's id. * * Note that unlike `getPeriodObjectFromPeriod` this method is only going to look * into currently stored Period and as such old Periods not in the Manifest * anymore might not be retrievable. * If you want to retrieve Period objects linked to such Period, you might * prefer to use `getPeriodObjectFromPeriod` (which necessitates the original * Period object). * * @param {string} periodId - The concerned Period's id * @returns {Object} */ TracksStore.prototype.getPeriodObjectFromId = function (periodId) { return getPeriodItem(this._storedPeriodInfo, periodId); }; TracksStore.prototype.disableVideoTrickModeTracks = function () { if (!this._isTrickModeTrackEnabled) { return; } this._isTrickModeTrackEnabled = false; this._resetVideoTrackChoices("trickmode-disabled"); }; TracksStore.prototype.enableVideoTrickModeTracks = function () { if (this._isTrickModeTrackEnabled) { return; } this._isTrickModeTrackEnabled = true; this._resetVideoTrackChoices("trickmode-enabled"); }; /** * Reset the TracksStore's Period objects: * - All Period which are not in the manifest currently will be removed. * - All References used to communicate the wanted track will be removed. * * You might want to call this API when restarting playback. */ TracksStore.prototype.resetPeriodObjects = function () { var _a, _b, _c; log_1.default.debug("TS: Resetting Period Objects"); for (var i = this._storedPeriodInfo.length - 1; i >= 0; i--) { var storedObj = this._storedPeriodInfo[i]; (_a = storedObj.audio.dispatcher) === null || _a === void 0 ? void 0 : _a.dispose(); storedObj.audio.dispatcher = null; (_b = storedObj.video.dispatcher) === null || _b === void 0 ? void 0 : _b.dispose(); storedObj.video.dispatcher = null; (_c = storedObj.text.dispatcher) === null || _c === void 0 ? void 0 : _c.dispose(); storedObj.text.dispatcher = null; if (!storedObj.inManifest) { this._removePeriodObject(i); } } }; /** * @returns {boolean} */ TracksStore.prototype.isTrickModeEnabled = function () { return this._isTrickModeTrackEnabled; }; /** * Set audio track based on the ID of its Adaptation for a given added Period. * @param {Object} params * @param {Object} params.periodRef - The concerned Period's object. * @param {string} params.trackId - adaptation id of the wanted track. * @param {string} params.switchingMode - Behavior when replacing the track by * another. * @param {Object|null} params.lockedRepresentations - Audio Representations * that should be locked after switching to that track. * `null` if no Audio Representation should be locked. * @param {number} params.relativeResumingPosition */ TracksStore.prototype.setAudioTrack = function (payload) { var periodRef = payload.periodRef, trackId = payload.trackId, switchingMode = payload.switchingMode, lockedRepresentations = payload.lockedRepresentations, relativeResumingPosition = payload.relativeResumingPosition; return this._setAudioOrTextTrack({ bufferType: "audio", periodRef: periodRef, trackId: trackId, switchingMode: switchingMode !== null && switchingMode !== void 0 ? switchingMode : this._defaultAudioTrackSwitchingMode, lockedRepresentations: lockedRepresentations, relativeResumingPosition: relativeResumingPosition, }); }; /** * Set text track based on the ID of its Adaptation for a given added Period. * @param {Object} periodObj - The concerned Period's object. * @param {string} wantedId - adaptation id of the wanted track. */ TracksStore.prototype.setTextTrack = function (periodObj, wantedId) { return this._setAudioOrTextTrack({ bufferType: "text", periodRef: periodObj, trackId: wantedId, switchingMode: "direct", lockedRepresentations: null, relativeResumingPosition: undefined, }); }; /** * Set audio track based on the ID of its Adaptation for a given added Period. * @param {Object} params * @param {string} params.bufferType * @param {Object} params.periodRef - The concerned Period's object. * @param {string} params.trackId - adaptation id of the wanted track. * @param {string} params.switchingMode - Behavior when replacing the track by * another. * @param {Array.<string>|null} params.lockedRepresentations - Audio * Representations that should be locked after switchingMode to that track. * `null` if no Audio Representation should be locked. * @param {number|undefined} params.relativeResumingPosition */ TracksStore.prototype._setAudioOrTextTrack = function (_a) { var _b, _c; var bufferType = _a.bufferType, periodRef = _a.periodRef, trackId = _a.trackId, switchingMode = _a.switchingMode, lockedRepresentations = _a.lockedRepresentations, relativeResumingPosition = _a.relativeResumingPosition; if (!periodRef.isPeriodAdvertised) { throw new Error("Wanted Period not yet advertised."); } var period = periodRef.period; var wantedAdaptation = (0, array_find_1.default)((_b = period.adaptations[bufferType]) !== null && _b !== void 0 ? _b : [], function (_a) { var id = _a.id, supportStatus = _a.supportStatus; return supportStatus.hasSupportedCodec !== false && supportStatus.isDecipherable !== false && id === trackId; }); if (wantedAdaptation === undefined) { throw new Error("Wanted ".concat(bufferType, " track not found.")); } var typeInfo = periodRef[bufferType]; var lockedRepresentationsRef; if (lockedRepresentations === null) { lockedRepresentationsRef = new reference_1.default(null); } else { var representationsToLock = this._getRepresentationsToLock(wantedAdaptation, lockedRepresentations); var repSwitchingMode = bufferType === "audio" ? this._defaultAudioTrackSwitchingMode : "direct"; lockedRepresentationsRef = new reference_1.default({ representationIds: representationsToLock, switchingMode: repSwitchingMode, }); } var storedSettings = { adaptation: wantedAdaptation, switchingMode: switchingMode, lockedRepresentations: lockedRepresentationsRef, relativeResumingPosition: relativeResumingPosition, }; typeInfo.storedSettings = storedSettings; this.trigger("trackUpdate", { period: toExposedPeriod(period), trackType: bufferType, reason: "manual", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this._isDisposed) { return; // Someone disposed the `TracksStore` on the previous side-effect } var newPeriodItem = getPeriodItem(this._storedPeriodInfo, period.id); if (newPeriodItem !== undefined && newPeriodItem[bufferType].storedSettings === storedSettings) { (_c = newPeriodItem[bufferType].dispatcher) === null || _c === void 0 ? void 0 : _c.updateTrack(storedSettings); } }; /** * Set video track based on the ID of its Adaptation for a given added Period. * @param {Object} params * @param {Object} params.periodRef - The concerned Period's object. * @param {string} params.trackId - adaptation id of the wanted track. * @param {string} params.switchingMode - Behavior when replacing the track by * another. * @param {Array.<string>|null} params.lockedRepresentations - Video * Representations that should be locked after switching to that track. * `null` if no Video Representation should be locked. * @param {number|undefined} params.relativeResumingPosition */ TracksStore.prototype.setVideoTrack = function (payload) { var _a, _b; var periodRef = payload.periodRef, trackId = payload.trackId, switchingMode = payload.switchingMode, lockedRepresentations = payload.lockedRepresentations, relativeResumingPosition = payload.relativeResumingPosition; if (!periodRef.isPeriodAdvertised) { throw new Error("Wanted Period not yet advertised."); } var period = periodRef.period; var wantedAdaptation = (0, array_find_1.default)((_a = period.adaptations.video) !== null && _a !== void 0 ? _a : [], function (_a) { var id = _a.id, supportStatus = _a.supportStatus; return supportStatus.isDecipherable !== false && supportStatus.hasSupportedCodec !== false && id === trackId; }); if (wantedAdaptation === undefined) { throw new Error("Wanted video track not found."); } var DEFAULT_VIDEO_TRACK_SWITCHING_MODE = config_1.default.getCurrent().DEFAULT_VIDEO_TRACK_SWITCHING_MODE; var typeInfo = periodRef.video; var newAdaptation = getRightVideoTrack(wantedAdaptation, this._isTrickModeTrackEnabled); var lockedRepresentationsRef; if (lockedRepresentations === null) { lockedRepresentationsRef = new reference_1.default(null); } else { var representationsToLock = this._getRepresentationsToLock(wantedAdaptation, lockedRepresentations); var repSwitchingMode = DEFAULT_VIDEO_TRACK_SWITCHING_MODE; lockedRepresentationsRef = new reference_1.default({ representationIds: representationsToLock, switchingMode: repSwitchingMode, }); } var storedSettings = { adaptationBase: wantedAdaptation, switchingMode: switchingMode !== null && switchingMode !== void 0 ? switchingMode : DEFAULT_VIDEO_TRACK_SWITCHING_MODE, adaptation: newAdaptation, relativeResumingPosition: relativeResumingPosition, lockedRepresentations: lockedRepresentationsRef, }; typeInfo.storedSettings = storedSettings; this.trigger("trackUpdate", { period: toExposedPeriod(period), trackType: "video", reason: "manual", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this._isDisposed) { return; // Someone disposed the `TracksStore` on the previous side-effect } var newPeriodItem = getPeriodItem(this._storedPeriodInfo, period.id); if (newPeriodItem !== undefined && newPeriodItem.video.storedSettings === storedSettings) { (_b = newPeriodItem.video.dispatcher) === null || _b === void 0 ? void 0 : _b.updateTrack(storedSettings); } }; /** * Disable the current text track for a given period. * * @param {Object} periodObj - The concerned Period's object * @param {string} bufferType - The type of track to disable. * @throws Error - Throws if the period given has not been added */ TracksStore.prototype.disableTrack = function (periodObj, bufferType) { var _a, _b; if (!periodObj.isPeriodAdvertised) { throw new Error("Wanted Period not yet advertised."); } var trackInfo = periodObj[bufferType]; if (trackInfo.storedSettings === null) { return; } if (bufferType !== "text") { // Potentially unneeded, but let's be clean (_a = periodObj[bufferType].storedSettings) === null || _a === void 0 ? void 0 : _a.lockedRepresentations.finish(); } trackInfo.storedSettings = null; this.trigger("trackUpdate", { period: toExposedPeriod(periodObj.period), trackType: bufferType, reason: "manual", }); // The previous event trigger could have had side-effects, so we // re-check if we're still mostly in the same state if (this._isDisposed) { return; // Someone disposed the `TracksStore` on the previous side-effect } var newPeriodItem = getPeriodItem(this._storedPeriodInfo, periodObj.period.id); if (newPeriodItem !== undefined && newPeriodItem[bufferType].storedSettings === null) { (_b = newPeriodItem[bufferType].dispatcher) === null || _b === void 0 ? void 0 : _b.updateTrack(null); } }; /** * Returns an object describing the chosen audio track for the given audio * Period. * * Returns `null` is the the current audio track is disabled or not * set yet.a pas bcp de marge de manoeuvre j'ai l'impression * * Returns `undefined` if the given Period's id is not currently found in the * `TracksStore`. The cause being most probably that the corresponding * Period is not available anymore. * If you're in that case and if still have the corresponding JavaScript * reference to the wanted Period, you can call `getOldAudioTrack` with it. It * will try retrieving the choice it made from its cache. * @param {Object} periodObj - The concerned Period's object * @returns {Object|null|undefined} - The audio track chosen for this Period. * `null` if audio tracks were disabled and `undefined` if the Period is not * known. */ TracksStore.prototype.getChosenAudioTrack = function (periodObj, filterPlayableRepresentations) { return (0, is_null_or_undefined_1.default)(periodObj.audio.storedSettings) ? null : (0, manifest_1.toAudioTrack)(periodObj.audio.storedSettings.adaptation, filterPlayableRepresentations); }; /** * Returns an object describing the chosen text track for the given text * Period. * * Returns null is the the current text track is disabled or not * set yet. * * @param {Object} periodObj - The concerned Period's object * @returns {Object|null} - The text track chosen for this Period */ TracksStore.prototype.getChosenTextTrack = function (periodObj) { return (0, is_null_or_undefined_1.default)(periodObj.text.storedSettings) ? null : (0, manifest_1.toTextTrack)(periodObj.text.storedSettings.adaptation); }; /** * Returns an object describing the chosen video track for the given video * Period. * * Returns null is the the current video track is disabled or not * set yet. * * @param {Object} periodObj - The concerned Period's object * @returns {Object|null} - The video track chosen for this Period */ TracksStore.prototype.getChosenVideoTrack = function (periodObj, filterPlayableRepresentations) { if ((0, is_null_or_undefined_1.default)(periodObj.video.storedSettings)) { return null; } return (0, manifest_1.toVideoTrack)(periodObj.video.storedSettings.adaptation, filterPlayableRepresentations); }; /** * Returns all available audio tracks for a given Period, as an array of * objects. * * Returns `undefined` if the given Period's id is not known. * * @param {Object} periodObj - The concerned Period's object * @param {boolean} filterPlayableRepresentations - If `true`, only * representations considered to be "playable" will be included in the * returned response. * If `false`, the response should contain all linked representations. * @returns {Array.<Object>} */ TracksStore.prototype.getAvailableAudioTracks = function (periodObj, filterPlayableRepresentations) { var storedSettings = periodObj.audio.storedSettings; var currentId = !(0, is_null_or_undefined_1.default)(storedSettings) ? storedSettings.adaptation.id : null; var adaptations = (0, manifest_1.getSupportedAdaptations)(periodObj.period, "audio"); return adaptations.map(function (adaptation) { var active = currentId === null ? false : currentId === adaptation.id; return (0, object_assign_1.default)((0, manifest_1.toAudioTrack)(adaptation, filterPlayableRepresentations), { active: active, }); }); }; /** * Returns all available text tracks for a given Period, as an array of * objects. * * Returns `undefined` if the given Period's id is not known. * * @param {Object} periodObj - The concerned Period's object * @returns {Array.<Object>} */ TracksStore.prototype.getAvailableTextTracks = function (periodObj) { var storedSettings = periodObj.text.storedSettings; var currentId = !(0, is_null_or_undefined_1.default)(storedSettings) ? storedSettings.adaptation.id : null; var adaptations = (0, manifest_1.getSupportedAdaptations)(periodObj.period, "text"); return adaptations.map(function (adaptation) { var active = currentId === null ? false : currentId === adaptation.id; return (0, object_assign_1.default)((0, manifest_1.toTextTrack)(adaptation), { active: active }); }); }; /** * Returns all available video tracks for a given Period, as an array of * objects. * * Returns `undefined` if the given Period's id is not known. * * @param {Object} periodObj - The concerned Period's object * @param {boolean} filterPlayableRepresentations - If `true`, only * representations considered to be "playable" will be included in the * returned response. * If `false`, the response should contain all linked representations. * @returns {Array.<Object>} */ TracksStore.prototype.getAvailableVideoTracks = function (periodObj, filterPlayableRepresentations) { var storedSettings = periodObj.video.storedSettings; var currentId = (0, is_null_or_undefined_1.default)(storedSettings) ? undefined : storedSettings.adaptation.id; var adaptations = (0, manifest_1.getSupportedAdaptations)(periodObj.period, "video"); return adaptations.map(function (adaptation) { var active = currentId === null ? false : currentId === adaptation.id; var track = (0, manifest_1.toVideoTrack)(adaptation, filterPlayableRepresentations); var trickModeTracks = track.trickModeTracks !== undefined ? track.trickModeTracks.map(function (trickModeAdaptation) { var isActive = currentId === null ? false : currentId === trickModeAdaptation.id; return (0, object_assign_1.default)(trickModeAdaptation, { active: isActive }); }) : []; var availableTrack = (0, object_assign_1.default)(track, { active: active }); if (trickModeTracks !== undefined) { availableTrack.trickModeTracks = trickModeTracks; } return availableTrack; }); }; TracksStore.prototype.getLockedAudioRepresentations = function (periodObj) { var storedSettings = periodObj.audio.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings)) { return null; } var lastLockedSettings = storedSettings.lockedRepresentations.getValue(); return lastLockedSettings === null ? null : lastLockedSettings.representationIds; }; TracksStore.prototype.getLockedVideoRepresentations = function (periodObj) { var storedSettings = periodObj.video.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings)) { return null; } var lastLockedSettings = storedSettings.lockedRepresentations.getValue(); return lastLockedSettings === null ? null : lastLockedSettings.representationIds; }; TracksStore.prototype.lockAudioRepresentations = function (periodObj, lockSettings) { var _a; var storedSettings = periodObj.audio.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings)) { return; } var DEFAULT_AUDIO_REPRESENTATIONS_SWITCHING_MODE = config_1.default.getCurrent().DEFAULT_AUDIO_REPRESENTATIONS_SWITCHING_MODE; var filtered = this._getRepresentationsToLock(storedSettings.adaptation, lockSettings.representations); var switchingMode = (_a = lockSettings.switchingMode) !== null && _a !== void 0 ? _a : DEFAULT_AUDIO_REPRESENTATIONS_SWITCHING_MODE; storedSettings.lockedRepresentations.setValue({ representationIds: filtered, switchingMode: switchingMode, }); }; TracksStore.prototype.lockVideoRepresentations = function (periodObj, lockSettings) { var _a; var storedSettings = periodObj.video.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings)) { return; } var DEFAULT_VIDEO_REPRESENTATIONS_SWITCHING_MODE = config_1.default.getCurrent().DEFAULT_VIDEO_REPRESENTATIONS_SWITCHING_MODE; var filtered = this._getRepresentationsToLock(storedSettings.adaptation, lockSettings.representations); var switchingMode = (_a = lockSettings.switchingMode) !== null && _a !== void 0 ? _a : DEFAULT_VIDEO_REPRESENTATIONS_SWITCHING_MODE; storedSettings.lockedRepresentations.setValue({ representationIds: filtered, switchingMode: switchingMode, }); }; TracksStore.prototype.unlockAudioRepresentations = function (periodObj) { var storedSettings = periodObj.audio.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings) || storedSettings.lockedRepresentations.getValue() === null) { return; } storedSettings.lockedRepresentations.setValue(null); }; TracksStore.prototype.unlockVideoRepresentations = function (periodObj) { var storedSettings = periodObj.video.storedSettings; if ((0, is_null_or_undefined_1.default)(storedSettings) || storedSettings.lockedRepresentations.getValue() === null) { return; } storedSettings.lockedRepresentations.setValue(null); }; TracksStore.prototype.dispose = function () { this._isDisposed = true; while (true) { var lastPeriod = this._storedPeriodInfo.pop(); if (lastPeriod === undefined) { return; } lastPeriod.isRemoved = true; } }; TracksStore.prototype._resetVideoTrackChoices = function (reason) { var _a; for (var i = 0; i < this._storedPeriodInfo.length; i++) { var periodObj = this._storedPeriodInfo[i]; if (!(0, is_null_or_undefined_1.default)(periodObj.video.storedSettings)) { var chosenBaseTrack = periodObj.video.storedSettings.adaptationBase; if (chosenBaseTrack !== null) { var chosenTrack = getRightVideoTrack(chosenBaseTrack, this._isTrickModeTrackEnabled); periodObj.video.storedSettings.adaptationBase = chosenBaseTrack; periodObj.video.storedSettings.adaptation = chosenTrack; } } } // Clone the current Period list to not be influenced if Periods are removed // or added while the loop is running. var sliced = this._storedPeriodInfo.slice(); for (var i = 0; i < sliced.length; i++) { var period = sliced[i].period; var videoItem = sliced[i].video; var storedSettings = videoItem.storedSettings; if (storedSettings !== undefined) { this.trigger("trackUpdate", { period: toExposedPeriod(period), trackType: "video", reason: reason, }); // The previous event trigger c