rx-player
Version:
Canal+ HTML5 Video Player
907 lines (906 loc) • 45.2 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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 });
exports.default = initializeWorkerMain;
var config_1 = require("../../../config");
var errors_1 = require("../../../errors");
var features_1 = require("../../../features");
var log_1 = require("../../../log");
var classes_1 = require("../../../manifest/classes");
var fast_js_parser_1 = require("../../../parsers/manifest/dash/fast-js-parser");
var wasm_parser_1 = require("../../../parsers/manifest/dash/wasm-parser");
var playback_observer_1 = require("../../../playback_observer");
var worker_playback_observer_1 = require("../../../playback_observer/worker_playback_observer");
var dash_1 = require("../../../transports/dash");
var array_find_1 = require("../../../utils/array_find");
var assert_1 = require("../../../utils/assert");
var global_scope_1 = require("../../../utils/global_scope");
var monotonic_timestamp_1 = require("../../../utils/monotonic_timestamp");
var object_assign_1 = require("../../../utils/object_assign");
var reference_1 = require("../../../utils/reference");
var task_canceller_1 = require("../../../utils/task_canceller");
var stream_1 = require("../../stream");
var create_content_time_boundaries_observer_1 = require("../common/create_content_time_boundaries_observer");
var get_buffered_data_per_media_buffer_1 = require("../common/get_buffered_data_per_media_buffer");
var get_thumbnail_data_1 = require("../common/get_thumbnail_data");
var synchronize_sinks_on_observation_1 = require("../common/synchronize_sinks_on_observation");
var content_preparer_1 = require("./content_preparer");
var globals_1 = require("./globals");
var send_message_1 = require("./send_message");
function initializeWorkerMain() {
/**
* `true` once the worker has been initialized.
* Allow to enforce the fact that it is only initialized once.
*/
var isInitialized = false;
/**
* Abstraction allowing to load contents (fetching its manifest as
* well as creating and reloading its MediaSource).
*
* Creating a default one which may change on initialization.
*/
var contentPreparer = new content_preparer_1.default({
hasMseInWorker: false,
hasVideo: true,
});
/**
* Abort all operations relative to the currently loaded content.
* `null` when there's no loaded content currently or when it is reloaidng.
*/
var currentLoadedContentTaskCanceller = null;
// Initialize Manually a `DashWasmParser` and add the feature.
// TODO allow worker-side feature-switching? Not sure how
var dashWasmParser = new wasm_parser_1.default();
features_1.default.dashParsers.wasm = dashWasmParser;
features_1.default.dashParsers.fastJs = fast_js_parser_1.default;
features_1.default.transports.dash = dash_1.default;
/**
* When set, emit playback observation made on the main thread.
*/
var playbackObservationRef = null;
global_scope_1.default.onmessageerror = function (_msg) {
log_1.default.error("MTCI: Error when receiving message from main thread.");
};
onmessage = function (e) {
var _a, _b;
log_1.default.debug("Worker: received message", e.data.type);
var msg = e.data;
switch (msg.type) {
case "init" /* MainThreadMessageType.Init */:
(0, assert_1.default)(!isInitialized);
isInitialized = true;
(0, monotonic_timestamp_1.scaleTimestamp)(msg.value);
updateLoggerLevel(msg.value.logLevel, msg.value.logFormat, msg.value.sendBackLogs);
if (msg.value.dashWasmUrl !== undefined && dashWasmParser.isCompatible()) {
dashWasmParser.initialize({ wasmUrl: msg.value.dashWasmUrl }).catch(function (err) {
var error = err instanceof Error ? err.toString() : "Unknown Error";
log_1.default.error("Worker: Could not initialize DASH_WASM parser", error);
});
}
if (!msg.value.hasVideo || msg.value.hasMseInWorker) {
contentPreparer.disposeCurrentContent();
contentPreparer = new content_preparer_1.default({
hasMseInWorker: msg.value.hasMseInWorker,
hasVideo: msg.value.hasVideo,
});
}
(0, send_message_1.default)({ type: "init-success" /* WorkerMessageType.InitSuccess */, value: null });
break;
case "log-level-update" /* MainThreadMessageType.LogLevelUpdate */:
updateLoggerLevel(msg.value.logLevel, msg.value.logFormat, msg.value.sendBackLogs);
break;
case "prepare" /* MainThreadMessageType.PrepareContent */:
prepareNewContent(contentPreparer, msg.value);
break;
case "start" /* MainThreadMessageType.StartPreparedContent */: {
var preparedContent = contentPreparer.getCurrentContent();
if (msg.contentId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.contentId)) {
return;
}
if (currentLoadedContentTaskCanceller !== null) {
currentLoadedContentTaskCanceller.cancel();
currentLoadedContentTaskCanceller = null;
}
var currentCanceller = new task_canceller_1.default();
var currentContentObservationRef_1 = new reference_1.default((0, object_assign_1.default)(msg.value.initialObservation, {
position: new (playback_observer_1.ObservationPosition.bind.apply(playback_observer_1.ObservationPosition, __spreadArray([void 0], __read(msg.value.initialObservation.position), false)))(),
}));
playbackObservationRef = currentContentObservationRef_1;
currentLoadedContentTaskCanceller = currentCanceller;
currentLoadedContentTaskCanceller.signal.register(function () {
currentContentObservationRef_1.finish();
});
log_1.default.debug("WP: Loading new pepared content.");
loadOrReloadPreparedContent(msg.value, contentPreparer, currentContentObservationRef_1, currentCanceller.signal);
break;
}
case "observation" /* MainThreadMessageType.PlaybackObservation */: {
var currentContent = contentPreparer.getCurrentContent();
if (msg.contentId !== (currentContent === null || currentContent === void 0 ? void 0 : currentContent.contentId)) {
return;
}
var observation = msg.value;
var buffered = observation.buffered;
var newBuffered = (0, get_buffered_data_per_media_buffer_1.default)(currentContent.mediaSource, null);
if (newBuffered.audio !== null) {
buffered.audio = newBuffered.audio;
}
if (newBuffered.video !== null) {
buffered.video = newBuffered.video;
}
playbackObservationRef === null || playbackObservationRef === void 0 ? void 0 : playbackObservationRef.setValue((0, object_assign_1.default)(observation, {
position: new (playback_observer_1.ObservationPosition.bind.apply(playback_observer_1.ObservationPosition, __spreadArray([void 0], __read(msg.value.position), false)))(),
}));
break;
}
case "ref-update" /* MainThreadMessageType.ReferenceUpdate */:
updateGlobalReference(msg);
break;
case "stop" /* MainThreadMessageType.StopContent */:
if (msg.contentId !== ((_a = contentPreparer.getCurrentContent()) === null || _a === void 0 ? void 0 : _a.contentId)) {
return;
}
contentPreparer.disposeCurrentContent();
if (currentLoadedContentTaskCanceller !== null) {
currentLoadedContentTaskCanceller.cancel();
currentLoadedContentTaskCanceller = null;
}
break;
case "sb-success" /* MainThreadMessageType.SourceBufferSuccess */: {
var preparedContent = contentPreparer.getCurrentContent();
if (msg.mediaSourceId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.mediaSource.id)) {
return;
}
var sourceBuffers = preparedContent.mediaSource.sourceBuffers;
var sourceBuffer = (0, array_find_1.default)(sourceBuffers, function (s) { return s.type === msg.sourceBufferType; });
if (sourceBuffer === undefined) {
log_1.default.info("WP: Success for an unknown SourceBuffer", msg.sourceBufferType);
return;
}
if (sourceBuffer.onOperationSuccess === undefined) {
log_1.default.warn("WP: A SourceBufferInterface with MSE performed a cross-thread operation", msg.sourceBufferType);
return;
}
sourceBuffer.onOperationSuccess(msg.operationId, msg.value.buffered);
break;
}
case "sb-error" /* MainThreadMessageType.SourceBufferError */: {
var preparedContent = contentPreparer.getCurrentContent();
if (msg.mediaSourceId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.mediaSource.id)) {
return;
}
var sourceBuffers = preparedContent.mediaSource.sourceBuffers;
var sourceBuffer = (0, array_find_1.default)(sourceBuffers, function (s) { return s.type === msg.sourceBufferType; });
if (sourceBuffer === undefined) {
log_1.default.info("WP: Error for an unknown SourceBuffer", msg.sourceBufferType);
return;
}
if (sourceBuffer.onOperationFailure === undefined) {
log_1.default.warn("WP: A SourceBufferInterface with MSE performed a cross-thread operation", msg.sourceBufferType);
return;
}
sourceBuffer.onOperationFailure(msg.operationId, msg.value);
break;
}
case "media-source-ready-state-change" /* MainThreadMessageType.MediaSourceReadyStateChange */: {
var preparedContent = contentPreparer.getCurrentContent();
if (msg.mediaSourceId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.mediaSource.id)) {
return;
}
if (preparedContent.mediaSource.onMediaSourceReadyStateChanged === undefined) {
log_1.default.warn("WP: A MediaSourceInterface with MSE performed a cross-thread operation");
return;
}
preparedContent.mediaSource.onMediaSourceReadyStateChanged(msg.value);
break;
}
case "decipherability-update" /* MainThreadMessageType.DecipherabilityStatusUpdate */: {
if (msg.contentId !== ((_b = contentPreparer.getCurrentContent()) === null || _b === void 0 ? void 0 : _b.contentId)) {
return;
}
var currentContent = contentPreparer.getCurrentContent();
if (currentContent === null || currentContent.manifest === null) {
return;
}
var updates_1 = msg.value;
currentContent.manifest.updateRepresentationsDeciperability(function (content) {
var e_1, _a;
try {
for (var updates_2 = __values(updates_1), updates_2_1 = updates_2.next(); !updates_2_1.done; updates_2_1 = updates_2.next()) {
var update = updates_2_1.value;
if (content.representation.uniqueId === update.representationUniqueId) {
return update.decipherable;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (updates_2_1 && !updates_2_1.done && (_a = updates_2.return)) _a.call(updates_2);
}
finally { if (e_1) throw e_1.error; }
}
return content.representation.decipherable;
});
break;
}
case "codec-support-update" /* MainThreadMessageType.CodecSupportUpdate */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.manifest === null) {
return;
}
var newEvaluatedCodecs = msg.value;
try {
var warning = preparedContent.manifest.updateCodecSupport(newEvaluatedCodecs);
if (warning !== null) {
(0, send_message_1.default)({
type: "warning" /* WorkerMessageType.Warning */,
contentId: preparedContent.contentId,
value: (0, send_message_1.formatErrorForSender)(warning),
});
}
}
catch (err) {
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: preparedContent.contentId,
value: (0, send_message_1.formatErrorForSender)(err),
});
}
break;
}
case "urls-update" /* MainThreadMessageType.ContentUrlsUpdate */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
preparedContent.manifestFetcher.updateContentUrls(msg.value.urls, msg.value.refreshNow);
break;
}
case "track-update" /* MainThreadMessageType.TrackUpdate */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
preparedContent.trackChoiceSetter.setTrack(msg.value.periodId, msg.value.bufferType, msg.value.choice);
break;
}
case "rep-update" /* MainThreadMessageType.RepresentationUpdate */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
preparedContent.trackChoiceSetter.updateRepresentations(msg.value.periodId, msg.value.adaptationId, msg.value.bufferType, msg.value.choice);
break;
}
case "add-text-success" /* MainThreadMessageType.PushTextDataSuccess */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
if (preparedContent.workerTextSender === null) {
log_1.default.error("WP: Added text track but text track aren't enabled");
return;
}
preparedContent.workerTextSender.onPushedTrackSuccess(msg.value.ranges);
break;
}
case "push-text-error" /* MainThreadMessageType.PushTextDataError */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
if (preparedContent.workerTextSender === null) {
log_1.default.error("WP: Added text track but text track aren't enabled");
return;
}
preparedContent.workerTextSender.onPushedTrackError(new Error(msg.value.message));
break;
}
case "remove-text-success" /* MainThreadMessageType.RemoveTextDataSuccess */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
if (preparedContent.workerTextSender === null) {
log_1.default.error("WP: Removed text track but text track aren't enabled");
return;
}
preparedContent.workerTextSender.onRemoveSuccess(msg.value.ranges);
break;
}
case "remove-text-error" /* MainThreadMessageType.RemoveTextDataError */: {
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.contentId !== msg.contentId) {
return;
}
if (preparedContent.workerTextSender === null) {
log_1.default.error("WP: Removed text track but text track aren't enabled");
return;
}
preparedContent.workerTextSender.onRemoveError(new Error(msg.value.message));
break;
}
case "pull-segment-sink-store-infos" /* MainThreadMessageType.PullSegmentSinkStoreInfos */: {
sendSegmentSinksStoreInfos(contentPreparer, msg.value.requestId);
break;
}
case "thumbnail-request" /* MainThreadMessageType.ThumbnailDataRequest */: {
sendThumbnailData(contentPreparer, msg);
break;
}
case "config-update" /* MainThreadMessageType.ConfigUpdate */: {
config_1.default.update(msg.value);
break;
}
default:
(0, assert_1.assertUnreachable)(msg);
}
};
}
function prepareNewContent(contentPreparer, contentInitData) {
contentPreparer.initializeNewContent(contentInitData).then(function (manifest) {
(0, send_message_1.default)({
type: "manifest-ready" /* WorkerMessageType.ManifestReady */,
contentId: contentInitData.contentId,
value: { manifest: manifest },
});
}, function (err) {
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: contentInitData.contentId,
value: (0, send_message_1.formatErrorForSender)(err),
});
});
}
function updateGlobalReference(msg) {
switch (msg.value.name) {
case "wantedBufferAhead":
globals_1.wantedBufferAhead.setValueIfChanged(msg.value.newVal);
break;
case "maxVideoBufferSize":
globals_1.maxVideoBufferSize.setValueIfChanged(msg.value.newVal);
break;
case "maxBufferBehind":
globals_1.maxBufferBehind.setValueIfChanged(msg.value.newVal);
break;
case "maxBufferAhead":
globals_1.maxBufferAhead.setValueIfChanged(msg.value.newVal);
break;
case "limitVideoResolution":
globals_1.limitVideoResolution.setValueIfChanged(msg.value.newVal);
break;
case "throttleVideoBitrate":
globals_1.throttleVideoBitrate.setValueIfChanged(msg.value.newVal);
break;
default:
(0, assert_1.assertUnreachable)(msg.value);
}
}
function loadOrReloadPreparedContent(val, contentPreparer, playbackObservationRef, parentCancelSignal) {
var _a;
log_1.default.debug("WP: Loading prepared content");
var currentLoadCanceller = new task_canceller_1.default();
currentLoadCanceller.linkToSignal(parentCancelSignal);
/**
* Stores last discontinuity update sent to the worker for each Period and type
* combinations, at least until the corresponding `PeriodStreamCleared`
* message.
*
* This is an optimization to avoid sending too much discontinuity messages to
* the main thread when it is not needed because nothing changed.
*/
var lastSentDiscontinuitiesStore = new Map();
var preparedContent = contentPreparer.getCurrentContent();
if (preparedContent === null || preparedContent.manifest === null) {
var error = new errors_1.OtherError("NONE", "Loading content when none is prepared");
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: undefined,
value: (0, send_message_1.formatErrorForSender)(error),
});
return;
}
var contentId = preparedContent.contentId, cmcdDataBuilder = preparedContent.cmcdDataBuilder, enableRepresentationAvoidance = preparedContent.enableRepresentationAvoidance, manifest = preparedContent.manifest, mediaSource = preparedContent.mediaSource, representationEstimator = preparedContent.representationEstimator, segmentSinksStore = preparedContent.segmentSinksStore, segmentQueueCreator = preparedContent.segmentQueueCreator;
var drmSystemId = val.drmSystemId, enableFastSwitching = val.enableFastSwitching, initialTime = val.initialTime, onCodecSwitch = val.onCodecSwitch;
playbackObservationRef.onUpdate(function (observation) {
(0, synchronize_sinks_on_observation_1.default)(observation, segmentSinksStore);
var freezeResolution = preparedContent.freezeResolver.onNewObservation(observation);
if (freezeResolution !== null) {
handleFreezeResolution(freezeResolution, {
contentId: contentId,
manifest: manifest,
handleMediaSourceReload: handleMediaSourceReload,
enableRepresentationAvoidance: enableRepresentationAvoidance,
});
}
}, { clearSignal: currentLoadCanceller.signal });
var initialPeriod = (_a = manifest.getPeriodForTime(initialTime)) !== null && _a !== void 0 ? _a : manifest.getNextPeriod(initialTime);
if (initialPeriod === undefined) {
var error = new errors_1.MediaError("MEDIA_STARTING_TIME_NOT_FOUND", "Wanted starting time not found in the Manifest.");
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: contentId,
value: (0, send_message_1.formatErrorForSender)(error),
});
return;
}
var playbackObserver = new worker_playback_observer_1.default(playbackObservationRef, contentId, send_message_1.default, currentLoadCanceller.signal);
cmcdDataBuilder === null || cmcdDataBuilder === void 0 ? void 0 : cmcdDataBuilder.startMonitoringPlayback(playbackObserver);
currentLoadCanceller.signal.register(function () {
cmcdDataBuilder === null || cmcdDataBuilder === void 0 ? void 0 : cmcdDataBuilder.stopMonitoringPlayback();
});
var contentTimeBoundariesObserver = (0, create_content_time_boundaries_observer_1.default)(manifest, mediaSource, playbackObserver, segmentSinksStore, {
onWarning: function (err) {
return (0, send_message_1.default)({
type: "warning" /* WorkerMessageType.Warning */,
contentId: contentId,
value: (0, send_message_1.formatErrorForSender)(err),
});
},
onPeriodChanged: function (period) {
(0, send_message_1.default)({
type: "active-period-changed" /* WorkerMessageType.ActivePeriodChanged */,
contentId: contentId,
value: { periodId: period.id },
});
},
}, currentLoadCanceller.signal);
(0, stream_1.default)({ initialPeriod: initialPeriod, manifest: manifest }, playbackObserver, representationEstimator, segmentSinksStore, segmentQueueCreator, {
wantedBufferAhead: globals_1.wantedBufferAhead,
maxVideoBufferSize: globals_1.maxVideoBufferSize,
maxBufferAhead: globals_1.maxBufferAhead,
maxBufferBehind: globals_1.maxBufferBehind,
drmSystemId: drmSystemId,
enableFastSwitching: enableFastSwitching,
onCodecSwitch: onCodecSwitch,
}, handleStreamOrchestratorCallbacks(), currentLoadCanceller.signal);
/**
* Returns Object handling the callbacks from a `StreamOrchestrator`, which
* are basically how it communicates about events.
* @returns {Object}
*/
function handleStreamOrchestratorCallbacks() {
return {
needsBufferFlush: function (payload) {
(0, send_message_1.default)({
type: "needs-buffer-flush" /* WorkerMessageType.NeedsBufferFlush */,
contentId: contentId,
value: payload,
});
},
streamStatusUpdate: function (value) {
sendDiscontinuityUpdateIfNeeded(value);
// If the status for the last Period indicates that segments are all loaded
// or on the contrary that the loading resumed, announce it to the
// ContentTimeBoundariesObserver.
if (manifest.isLastPeriodKnown &&
value.period.id === manifest.periods[manifest.periods.length - 1].id) {
var hasFinishedLoadingLastPeriod = value.hasFinishedLoading || value.isEmptyStream;
if (hasFinishedLoadingLastPeriod) {
contentTimeBoundariesObserver.onLastSegmentFinishedLoading(value.bufferType);
}
else {
contentTimeBoundariesObserver.onLastSegmentLoadingResume(value.bufferType);
}
}
},
needsManifestRefresh: function () {
contentPreparer.scheduleManifestRefresh({
enablePartialRefresh: true,
canUseUnsafeMode: true,
});
},
manifestMightBeOufOfSync: function () {
var OUT_OF_SYNC_MANIFEST_REFRESH_DELAY = config_1.default.getCurrent().OUT_OF_SYNC_MANIFEST_REFRESH_DELAY;
contentPreparer.scheduleManifestRefresh({
enablePartialRefresh: false,
canUseUnsafeMode: false,
delay: OUT_OF_SYNC_MANIFEST_REFRESH_DELAY,
});
},
lockedStream: function (payload) {
(0, send_message_1.default)({
type: "locked-stream" /* WorkerMessageType.LockedStream */,
contentId: contentId,
value: {
periodId: payload.period.id,
bufferType: payload.bufferType,
},
});
},
adaptationChange: function (value) {
var _a, _b;
contentTimeBoundariesObserver.onAdaptationChange(value.type, value.period, value.adaptation);
if (currentLoadCanceller.signal.isCancelled()) {
return;
}
(0, send_message_1.default)({
type: "adaptation-changed" /* WorkerMessageType.AdaptationChanged */,
contentId: contentId,
value: {
adaptationId: (_b = (_a = value.adaptation) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null,
periodId: value.period.id,
type: value.type,
},
});
},
representationChange: function (value) {
var _a, _b;
contentTimeBoundariesObserver.onRepresentationChange(value.type, value.period);
if (currentLoadCanceller.signal.isCancelled()) {
return;
}
(0, send_message_1.default)({
type: "representation-changed" /* WorkerMessageType.RepresentationChanged */,
contentId: contentId,
value: {
adaptationId: value.adaptation.id,
representationId: (_b = (_a = value.representation) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null,
periodId: value.period.id,
type: value.type,
},
});
},
inbandEvent: function (value) {
(0, send_message_1.default)({
type: "inband-event" /* WorkerMessageType.InbandEvent */,
contentId: contentId,
value: value,
});
},
warning: function (value) {
(0, send_message_1.default)({
type: "warning" /* WorkerMessageType.Warning */,
contentId: contentId,
value: (0, send_message_1.formatErrorForSender)(value),
});
},
periodStreamReady: function (value) {
if (preparedContent === null) {
return;
}
preparedContent.trackChoiceSetter.addTrackSetter(value.period.id, value.type, value.adaptationRef);
(0, send_message_1.default)({
type: "period-stream-ready" /* WorkerMessageType.PeriodStreamReady */,
contentId: contentId,
value: { periodId: value.period.id, bufferType: value.type },
});
},
periodStreamCleared: function (value) {
if (preparedContent === null) {
return;
}
var periodDiscontinuitiesStore = lastSentDiscontinuitiesStore.get(value.period);
if (periodDiscontinuitiesStore !== undefined) {
periodDiscontinuitiesStore.delete(value.type);
if (periodDiscontinuitiesStore.size === 0) {
lastSentDiscontinuitiesStore.delete(value.period);
}
}
contentTimeBoundariesObserver.onPeriodCleared(value.type, value.period);
preparedContent.trackChoiceSetter.removeTrackSetter(value.period.id, value.type);
(0, send_message_1.default)({
type: "period-stream-cleared" /* WorkerMessageType.PeriodStreamCleared */,
contentId: contentId,
value: { periodId: value.period.id, bufferType: value.type },
});
},
bitrateEstimateChange: function (payload) {
var _a;
if (preparedContent !== null) {
(_a = preparedContent.cmcdDataBuilder) === null || _a === void 0 ? void 0 : _a.updateThroughput(payload.type, payload.bitrate);
}
// TODO for low-latency contents it is __VERY__ frequent.
// Considering this is only for an unimportant undocumented API, we may
// throttle such messages. (e.g. max one per 2 seconds for each type?).
(0, send_message_1.default)({
type: "bitrate-estimate-change" /* WorkerMessageType.BitrateEstimateChange */,
contentId: contentId,
value: {
bitrate: payload.bitrate,
bufferType: payload.type,
},
});
},
needsMediaSourceReload: function (payload) {
handleMediaSourceReload(payload);
},
needsDecipherabilityFlush: function () {
(0, send_message_1.default)({
type: "needs-decipherability-flush" /* WorkerMessageType.NeedsDecipherabilityFlush */,
contentId: contentId,
value: null,
});
},
encryptionDataEncountered: function (values) {
var e_2, _a;
try {
for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) {
var value = values_1_1.value;
var originalContent = value.content;
var content = __assign({}, originalContent);
if (content.manifest instanceof classes_1.default) {
content.manifest = content.manifest.getMetadataSnapshot();
}
if (content.period instanceof classes_1.Period) {
content.period = content.period.getMetadataSnapshot();
}
if (content.adaptation instanceof classes_1.Adaptation) {
content.adaptation = content.adaptation.getMetadataSnapshot();
}
if (content.representation instanceof classes_1.Representation) {
content.representation = content.representation.getMetadataSnapshot();
}
(0, send_message_1.default)({
type: "encryption-data-encountered" /* WorkerMessageType.EncryptionDataEncountered */,
contentId: contentId,
value: {
keyIds: value.keyIds,
values: value.values,
content: content,
type: value.type,
},
});
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1);
}
finally { if (e_2) throw e_2.error; }
}
},
error: function (error) {
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: contentId,
value: (0, send_message_1.formatErrorForSender)(error),
});
},
};
}
function sendDiscontinuityUpdateIfNeeded(value) {
var imminentDiscontinuity = value.imminentDiscontinuity;
var periodMap = lastSentDiscontinuitiesStore.get(value.period);
var sentObjInfo = periodMap === null || periodMap === void 0 ? void 0 : periodMap.get(value.bufferType);
if (sentObjInfo !== undefined) {
if (sentObjInfo.discontinuity === null) {
if (imminentDiscontinuity === null) {
return;
}
}
else if (imminentDiscontinuity !== null &&
sentObjInfo.discontinuity.start === imminentDiscontinuity.start &&
sentObjInfo.discontinuity.end === imminentDiscontinuity.end) {
return;
}
}
if (periodMap === undefined) {
periodMap = new Map();
lastSentDiscontinuitiesStore.set(value.period, periodMap);
}
var msgObj = {
periodId: value.period.id,
bufferType: value.bufferType,
discontinuity: value.imminentDiscontinuity,
position: value.position,
};
periodMap.set(value.bufferType, msgObj);
(0, send_message_1.default)({
type: "discontinuity-update" /* WorkerMessageType.DiscontinuityUpdate */,
contentId: contentId,
value: msgObj,
});
}
function handleMediaSourceReload(payload) {
// TODO more precize one day?
var lastObservation = playbackObservationRef.getValue();
var newInitialTime = lastObservation.position.getWanted();
if (currentLoadCanceller !== null) {
currentLoadCanceller.cancel();
}
log_1.default.debug("WP: Reloading MediaSource", payload.timeOffset, payload.minimumPosition, payload.maximumPosition);
contentPreparer.reloadMediaSource(payload).then(function () {
log_1.default.info("WP: MediaSource Reloaded, loading content again");
loadOrReloadPreparedContent({
initialTime: newInitialTime,
drmSystemId: val.drmSystemId,
enableFastSwitching: val.enableFastSwitching,
onCodecSwitch: val.onCodecSwitch,
}, contentPreparer, playbackObservationRef, parentCancelSignal);
}, function (err) {
if (task_canceller_1.default.isCancellationError(err)) {
log_1.default.info("WP: A reloading operation was cancelled");
return;
}
(0, send_message_1.default)({
type: "error" /* WorkerMessageType.Error */,
contentId: contentId,
value: (0, send_message_1.formatErrorForSender)(err),
});
});
}
}
function updateLoggerLevel(logLevel, logFormat, sendBackLogs) {
if (!sendBackLogs) {
log_1.default.setLevel(logLevel, logFormat);
}
else {
// Here we force the log format to "standard" as the full formatting will be
// performed on main thread.
log_1.default.setLevel(logLevel, "standard", function (levelStr, logs) {
var sentLogs = logs.map(function (e) {
if (e instanceof Error) {
return (0, send_message_1.formatErrorForSender)(e);
}
return e;
});
// Not relying on `sendMessage` as it also logs
postMessage({
type: "log" /* WorkerMessageType.LogMessage */,
value: {
logLevel: levelStr,
logs: sentLogs,
},
});
});
}
}
/**
* Send a message `SegmentSinkStoreUpdate` to the main thread with
* a serialized object that represents the segmentSinksStore state.
* @param {ContentPreparer} contentPreparer
* @returns {void}
*/
function sendSegmentSinksStoreInfos(contentPreparer, requestId) {
var currentContent = contentPreparer.getCurrentContent();
if (currentContent === null) {
return;
}
var segmentSinksMetrics = currentContent.segmentSinksStore.getSegmentSinksMetrics();
(0, send_message_1.default)({
type: "segment-sink-store-update" /* WorkerMessageType.SegmentSinkStoreUpdate */,
contentId: currentContent.contentId,
value: { segmentSinkMetrics: segmentSinksMetrics, requestId: requestId },
});
}
/**
* Handle accordingly an `IFreezeResolution` object.
* @param {Object|null} freezeResolution - The `IFreezeResolution` suggested.
* @param {Object} param - Parameters that might be needed to implement the
* resolution.
* @param {string} param.contentId - `contentId` for the current content, used
* e.g. for message exchanges between threads.
* @param {Object} param.manifest - The current content's Manifest object.
* @param {Function} param.handleMediaSourceReload - Function to call if we need
* to ask for a "MediaSource reload".
* @param {Boolean} param.enableRepresentationAvoidance - If `true`, this
* function is authorized to mark `Representation` as "to avoid" if the
* `IFreezeResolution` object suggest it.
*/
function handleFreezeResolution(freezeResolution, _a) {
var contentId = _a.contentId, manifest = _a.manifest, handleMediaSourceReload = _a.handleMediaSourceReload, enableRepresentationAvoidance = _a.enableRepresentationAvoidance;
switch (freezeResolution.type) {
case "reload": {
log_1.default.info("WP: Planning reload due to freeze");
handleMediaSourceReload({
timeOffset: 0,
minimumPosition: 0,
maximumPosition: Infinity,
});
break;
}
case "flush": {
log_1.default.info("WP: Flushing buffer due to freeze");
(0, send_message_1.default)({
type: "needs-buffer-flush" /* WorkerMessageType.NeedsBufferFlush */,
contentId: contentId,
value: {
relativeResumingPosition: freezeResolution.value.relativeSeek,
relativePosHasBeenDefaulted: false,
},
});
break;
}
case "avoid-representations": {
log_1.default.info("WP: Planning Representation avoidance due to freeze");
var content = freezeResolution.value;
if (enableRepresentationAvoidance) {
manifest.addRepresentationsToAvoid(content);
}
handleMediaSourceReload({
timeOffset: 0,
minimumPosition: 0,
maximumPosition: Infinity,
});
break;
}
default:
(0, assert_1.assertUnreachable)(freezeResolution);
}
}
/**
* Handles thumbnail requests and send back the result to the main thread.
* @param {ContentPreparer} contentPreparer
* @returns {void}
*/
function sendThumbnailData(contentPreparer, msg) {
var preparedContent = contentPreparer.getCurrentContent();
var respondWithError = function (err) {
(0, send_message_1.default)({
type: "thumbnail-response" /* WorkerMessageType.ThumbnailDataResponse */,
contentId: msg.contentId,
value: {
status: "error",
requestId: msg.value.requestId,
error: (0, send_message_1.formatErrorForSender)(err),
},
});
};
if (preparedContent === null ||
preparedContent.manifest === null ||
preparedContent.contentId !== msg.contentId) {
return respondWithError(new Error("Content changed"));
}
(0, get_thumbnail_data_1.default)(preparedContent.fetchThumbnailData, preparedContent.manifest, msg.value.periodId, msg.value.thumbnailTrackId, msg.value.time).then(function (result) {
(0, send_message_1.default)({
type: "thumbnail-response" /* WorkerMessageType.ThumbnailDataResponse */,
contentId: msg.contentId,
value: {
status: "success",
requestId: msg.value.requestId,
data: result,
},
}, [result.data]);
}, function (err) {
return respondWithError(err);
});
}