UNPKG

rx-player

Version:
1,002 lines 50.9 kB
"use strict";
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 = initializeCoreEntry;
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 playback_observer_1 = require("../../playback_observer");
var core_playback_observer_1 = require("../../playback_observer/core_playback_observer");
var array_find_1 = require("../../utils/array_find");
var assert_1 = require("../../utils/assert");
var is_null_or_undefined_1 = require("../../utils/is_null_or_undefined");
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 content_preparer_1 = require("./content_preparer");
var create_content_time_boundaries_observer_1 = require("./create_content_time_boundaries_observer");
var get_buffered_data_per_media_buffer_1 = require("./get_buffered_data_per_media_buffer");
var get_thumbnail_data_1 = require("./get_thumbnail_data");
var utils_1 = require("./utils");
/**
 * Initialize a `CoreEntry`, which is the part of the RxPlayer acting as an
 * entry point to all its "core" code.
 *
 * Its role is to receive and react to messages coming from "main thead", which
 * may include loading and playing a content, and to send back messages for the main
 * thread.
 * @param {Function} setMessageReceiver - Declares the function that will
 * receive messages coming from the "main thread" part of the RxPlayer logic.
 * @param {Function} sendMessage - Function allowing to send messages to the
 * "main thread" part of the RxPlayer logic.
 * @param {Object} corePlugins - Callbacks that may have been registered by
 * the application if it loaded the core independantly as a worker.
 */
function initializeCoreEntry(setMessageReceiver, sendMessage, corePlugins) {
    var _a = config_1.default.getCurrent(), DEFAULT_WANTED_BUFFER_AHEAD = _a.DEFAULT_WANTED_BUFFER_AHEAD, DEFAULT_MAX_VIDEO_BUFFER_SIZE = _a.DEFAULT_MAX_VIDEO_BUFFER_SIZE, DEFAULT_MAX_BUFFER_AHEAD = _a.DEFAULT_MAX_BUFFER_AHEAD, DEFAULT_MAX_BUFFER_BEHIND = _a.DEFAULT_MAX_BUFFER_BEHIND;
    var refs = {
        wantedBufferAhead: new reference_1.default(DEFAULT_WANTED_BUFFER_AHEAD),
        maxVideoBufferSize: new reference_1.default(DEFAULT_MAX_VIDEO_BUFFER_SIZE),
        maxBufferAhead: new reference_1.default(DEFAULT_MAX_BUFFER_AHEAD),
        maxBufferBehind: new reference_1.default(DEFAULT_MAX_BUFFER_BEHIND),
        limitVideoResolution: new reference_1.default({
            height: undefined,
            width: undefined,
            pixelRatio: 1,
        }),
        throttleVideoBitrate: new reference_1.default(Infinity),
    };
    /**
     * `true` once the CoreEntry has been initialized.
     * Allow to enforce the fact that it is only initialized once.
     */
    var isInitialized = false;
    /**
     * Abstraction allowing to prepare contents (fetching its manifest as
     * well as creating and reloading its MediaSource) for playback.
     *
     * Creating a default one which may change on initialization.
     */
    var contentPreparer = new content_preparer_1.default({ hasVideo: true });
    /**
     * Object allowing to control the lifecycle of the current content (stop/reload etc.).
     * `null` if there's no content loaded currently.
     */
    var currentContentHandle = null;
    /**
     * When set, emit playback observation made on the main thread.
     */
    var playbackObservationRef = null;
    setMessageReceiver(function (e) {
        var _a, _b;
        log_1.default.debug("Core", "received message", { name: 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 (!(0, is_null_or_undefined_1.default)(msg.value.dashWasmUrl)) {
                        var dashWasmParser = features_1.default.dashParsers.wasm;
                        if (dashWasmParser === null) {
                            log_1.default.error("Core", "Could not initialize DASH_WASM parser: DASH_WASM feature not added");
                        }
                        else if (!dashWasmParser.isCompatible()) {
                            log_1.default.warn("Core", "Could not initialize DASH_WASM parser: Browser not compatible");
                        }
                        else {
                            dashWasmParser
                                .initialize({ wasmUrl: msg.value.dashWasmUrl })
                                .catch(function (err) {
                                var error = err instanceof Error ? err.toString() : "Unknown Error";
                                log_1.default.error("Core", "Could not initialize DASH_WASM parser", error);
                            });
                        }
                    }
                    if (!msg.value.hasVideo) {
                        contentPreparer.disposeCurrentContent("Received Init msg");
                        contentPreparer = new content_preparer_1.default({ hasVideo: msg.value.hasVideo });
                    }
                    sendMessage({ type: "init-success" /* CoreMessageType.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(sendMessage, contentPreparer, msg.value, refs, corePlugins);
                break;
            case "start" /* MainThreadMessageType.StartPreparedContent */: {
                var preparedContent = contentPreparer.getCurrentContent();
                if (msg.contentId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.contentId)) {
                    return;
                }
                currentContentHandle === null || currentContentHandle === void 0 ? void 0 : currentContentHandle.stop();
                playbackObservationRef === null || playbackObservationRef === void 0 ? void 0 : playbackObservationRef.finish();
                var currentContentObservationRef = 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;
                currentContentHandle = loadPreparedContent(sendMessage, msg.value, contentPreparer, currentContentObservationRef, refs);
                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 */:
                updateCoreReference(msg, refs);
                break;
            case "stop" /* MainThreadMessageType.StopContent */:
                if (msg.contentId !== ((_a = contentPreparer.getCurrentContent()) === null || _a === void 0 ? void 0 : _a.contentId)) {
                    return;
                }
                contentPreparer.disposeCurrentContent("StopContent message");
                currentContentHandle === null || currentContentHandle === void 0 ? void 0 : currentContentHandle.stop();
                currentContentHandle = null;
                playbackObservationRef === null || playbackObservationRef === void 0 ? void 0 : playbackObservationRef.finish();
                playbackObservationRef = null;
                break;
            case "ms-reload" /* MainThreadMessageType.MediaSourceReload */:
                {
                    var preparedContent = contentPreparer.getCurrentContent();
                    if (msg.mediaSourceId !== (preparedContent === null || preparedContent === void 0 ? void 0 : preparedContent.mediaSource.id)) {
                        return;
                    }
                    currentContentHandle === null || currentContentHandle === void 0 ? void 0 : currentContentHandle.signalMediaSourceReload();
                }
                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("Core", "Success for an unknown SourceBuffer", {
                        sourceBufferType: msg.sourceBufferType,
                    });
                    return;
                }
                if (sourceBuffer.onOperationSuccess === undefined) {
                    log_1.default.warn("Core", "A SourceBufferInterface with MSE performed a cross-thread operation", { sourceBufferType: 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("Core", "Error for an unknown SourceBuffer", {
                        sourceBufferType: msg.sourceBufferType,
                    });
                    return;
                }
                if (sourceBuffer.onOperationFailure === undefined) {
                    log_1.default.warn("Core", "A SourceBufferInterface with MSE performed a cross-thread operation", {
                        sourceBufferType: 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("Core", "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) {
                        sendMessage({
                            type: "warning" /* CoreMessageType.Warning */,
                            contentId: preparedContent.contentId,
                            value: (0, utils_1.formatErrorForSender)(warning),
                        });
                    }
                }
                catch (err) {
                    sendMessage({
                        type: "error" /* CoreMessageType.Error */,
                        contentId: preparedContent.contentId,
                        value: (0, utils_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.coreTextSender === null) {
                    log_1.default.error("Core", "Added text track but text track aren't enabled");
                    return;
                }
                preparedContent.coreTextSender.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.coreTextSender === null) {
                    log_1.default.error("Core", "Added text track but text track aren't enabled");
                    return;
                }
                preparedContent.coreTextSender.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.coreTextSender === null) {
                    log_1.default.error("Core", "Removed text track but text track aren't enabled");
                    return;
                }
                preparedContent.coreTextSender.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.coreTextSender === null) {
                    log_1.default.error("Core", "Removed text track but text track aren't enabled");
                    return;
                }
                preparedContent.coreTextSender.onRemoveError(new Error(msg.value.message));
                break;
            }
            case "pull-segment-sink-store-infos" /* MainThreadMessageType.PullSegmentSinkStoreInfos */: {
                sendSegmentSinksStoreInfos(sendMessage, contentPreparer, msg.value.requestId);
                break;
            }
            case "thumbnail-request" /* MainThreadMessageType.ThumbnailDataRequest */: {
                sendThumbnailData(sendMessage, contentPreparer, msg);
                break;
            }
            case "config-update" /* MainThreadMessageType.ConfigUpdate */: {
                config_1.default.update(msg.value);
                break;
            }
            case "app-defined" /* MainThreadMessageType.AppDefined */: {
                break;
            }
            default:
                (0, assert_1.assertUnreachable)(msg);
        }
    });
}
/**
 * Performs steps needed to prepare a future content to be played:
 *   - Load its Manifest file
 *   - Create MSE `MediaSource` for that content.
 *   - Initialize all modules that will follow that content
 *   - etc.
 * @param {Function} sendMessage - Function allowing to send messages to the
 * "main thread" part of the RxPlayer logic.
 * @param {ContentPreparer} contentPreparer - Interface allowing to setup and
 * prepare a content.
 * @param {Object} contentInitData - Configuration wanted for the content to
 * load.
 * @param {Object} refs - Collection of so-called "references": values
 * configuring playback that may be updated at any time and that the
 * CoreEntry should react on.
 * @param {Object} corePlugins - Callbacks that may have been registered by
 * the application if it loaded the core independantly as a worker.
 */
function prepareNewContent(sendMessage, contentPreparer, contentInitData, refs, corePlugins) {
    contentPreparer
        .initializeNewContent(sendMessage, contentInitData, {
        limitResolution: { video: refs.limitVideoResolution },
        throttleBitrate: { video: refs.throttleVideoBitrate },
    }, corePlugins)
        .then(function (manifest) {
        sendMessage({
            type: "manifest-ready" /* CoreMessageType.ManifestReady */,
            contentId: contentInitData.contentId,
            value: { manifest: manifest },
        });
    }, function (err) {
        sendMessage({
            type: "error" /* CoreMessageType.Error */,
            contentId: contentInitData.contentId,
            value: (0, utils_1.formatErrorForSender)(err),
        });
    });
}
function updateCoreReference(msg, refs) {
    switch (msg.value.name) {
        case "wantedBufferAhead":
            refs.wantedBufferAhead.setValueIfChanged(msg.value.newVal);
            break;
        case "maxVideoBufferSize":
            refs.maxVideoBufferSize.setValueIfChanged(msg.value.newVal);
            break;
        case "maxBufferBehind":
            refs.maxBufferBehind.setValueIfChanged(msg.value.newVal);
            break;
        case "maxBufferAhead":
            refs.maxBufferAhead.setValueIfChanged(msg.value.newVal);
            break;
        case "limitVideoResolution":
            refs.limitVideoResolution.setValueIfChanged(msg.value.newVal);
            break;
        case "throttleVideoBitrate":
            refs.throttleVideoBitrate.setValueIfChanged(msg.value.newVal);
            break;
        default:
            (0, assert_1.assertUnreachable)(msg.value);
    }
}
function loadPreparedContent(sendMessage, val, contentPreparer, playbackObservationRef, refs) {
    log_1.default.debug("Core", "Loading pepared content.");
    var contentCanceller = new task_canceller_1.default("Start Content Worker");
    var currentLoadCanceller = null;
    startLoadingAt(val.initialTime);
    return {
        signalMediaSourceReload: function () {
            return onMediaSourceReload();
        },
        stop: function () {
            contentCanceller.cancel("ContentHandle stop");
        },
    };
    function startLoadingAt(startTime) {
        var _a;
        currentLoadCanceller === null || currentLoadCanceller === void 0 ? void 0 : currentLoadCanceller.cancel("Reloading content worker");
        currentLoadCanceller = new task_canceller_1.default("(Re)Loading Content Worker");
        currentLoadCanceller.linkToSignal(contentCanceller.signal);
        /**
         * Stores last discontinuity update sent to the Core 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");
            sendMessage({
                type: "error" /* CoreMessageType.Error */,
                contentId: undefined,
                value: (0, utils_1.formatErrorForSender)(error),
            });
            throw error;
        }
        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, onCodecSwitch = val.onCodecSwitch;
        playbackObservationRef.onUpdate(function (observation) {
            (0, utils_1.synchronizeSegmentSinksOnObservation)(observation, segmentSinksStore);
            var freezeResolution = preparedContent.freezeResolver.onNewObservation(observation);
            if (freezeResolution !== null) {
                handleFreezeResolution(sendMessage, freezeResolution, {
                    contentId: contentId,
                    manifest: manifest,
                    handleMediaSourceReload: performMediaSourceReload,
                    enableRepresentationAvoidance: enableRepresentationAvoidance,
                });
            }
        }, { clearSignal: currentLoadCanceller.signal });
        var initialPeriod = (_a = manifest.getPeriodForTime(startTime)) !== null && _a !== void 0 ? _a : manifest.getNextPeriod(startTime);
        if (initialPeriod === undefined) {
            var error = new errors_1.MediaError("MEDIA_STARTING_TIME_NOT_FOUND", "Wanted starting time not found in the Manifest.");
            sendMessage({
                type: "error" /* CoreMessageType.Error */,
                contentId: contentId,
                value: (0, utils_1.formatErrorForSender)(error),
            });
            throw error;
        }
        var playbackObserver = new core_playback_observer_1.default(playbackObservationRef, contentId, sendMessage, 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 sendMessage({
                    type: "warning" /* CoreMessageType.Warning */,
                    contentId: contentId,
                    value: (0, utils_1.formatErrorForSender)(err),
                });
            },
            onPeriodChanged: function (period) {
                sendMessage({
                    type: "active-period-changed" /* CoreMessageType.ActivePeriodChanged */,
                    contentId: contentId,
                    value: { periodId: period.id },
                });
            },
        }, currentLoadCanceller.signal);
        (0, stream_1.default)({ initialPeriod: initialPeriod, manifest: manifest }, playbackObserver, representationEstimator, segmentSinksStore, segmentQueueCreator, {
            wantedBufferAhead: refs.wantedBufferAhead,
            maxVideoBufferSize: refs.maxVideoBufferSize,
            maxBufferAhead: refs.maxBufferAhead,
            maxBufferBehind: refs.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) {
                    sendMessage({
                        type: "needs-buffer-flush" /* CoreMessageType.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) {
                    sendMessage({
                        type: "locked-stream" /* CoreMessageType.LockedStream */,
                        contentId: contentId,
                        value: {
                            periodId: payload.period.id,
                            bufferType: payload.bufferType,
                        },
                    });
                },
                adaptationChange: function (value) {
                    var _a, _b;
                    sendMessage({
                        type: "adaptation-changed" /* CoreMessageType.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,
                        },
                    });
                    contentTimeBoundariesObserver.onAdaptationChange(value.type, value.period, value.adaptation);
                },
                representationChange: function (value) {
                    var _a, _b;
                    contentTimeBoundariesObserver.onRepresentationChange(value.type, value.period);
                    if (currentLoadCanceller === null ||
                        currentLoadCanceller.signal.isCancelled()) {
                        return;
                    }
                    sendMessage({
                        type: "representation-changed" /* CoreMessageType.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) {
                    sendMessage({
                        type: "inband-event" /* CoreMessageType.InbandEvent */,
                        contentId: contentId,
                        value: value,
                    });
                },
                warning: function (value) {
                    sendMessage({
                        type: "warning" /* CoreMessageType.Warning */,
                        contentId: contentId,
                        value: (0, utils_1.formatErrorForSender)(value),
                    });
                },
                periodStreamReady: function (value) {
                    if (preparedContent === null) {
                        return;
                    }
                    preparedContent.trackChoiceSetter.addTrackSetter(value.period.id, value.type, value.adaptationRef);
                    sendMessage({
                        type: "period-stream-ready" /* CoreMessageType.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);
                    sendMessage({
                        type: "period-stream-cleared" /* CoreMessageType.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?).
                    sendMessage({
                        type: "bitrate-estimate-change" /* CoreMessageType.BitrateEstimateChange */,
                        contentId: contentId,
                        value: {
                            bitrate: payload.bitrate,
                            bufferType: payload.type,
                        },
                    });
                },
                needsMediaSourceReload: function (payload) {
                    performMediaSourceReload(payload);
                },
                needsDecipherabilityFlush: function () {
                    sendMessage({
                        type: "needs-decipherability-flush" /* CoreMessageType.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();
                            }
                            sendMessage({
                                type: "encryption-data-encountered" /* CoreMessageType.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) {
                    sendMessage({
                        type: "error" /* CoreMessageType.Error */,
                        contentId: contentId,
                        value: (0, utils_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);
            sendMessage({
                type: "discontinuity-update" /* CoreMessageType.DiscontinuityUpdate */,
                contentId: contentId,
                value: msgObj,
            });
        }
    }
    function performMediaSourceReload(payload) {
        var _a;
        if (currentLoadCanceller !== null) {
            currentLoadCanceller.cancel("WorkerMain MediaSource reload");
            currentLoadCanceller = null;
        }
        var mediaSourceId = (_a = contentPreparer.getCurrentContent()) === null || _a === void 0 ? void 0 : _a.mediaSource.id;
        if (mediaSourceId === undefined) {
            log_1.default.warn("Core", "Cannot reload MediaSource: no MediaSource currently.");
            return;
        }
        log_1.default.debug("Core", "Reloading MediaSource", {
            timeOffset: payload.timeOffset,
            minimumPosition: payload.minimumPosition,
            maximumPosition: payload.maximumPosition,
        });
        sendMessage({
            type: "reloading-media-source" /* CoreMessageType.ReloadingMediaSource */,
            mediaSourceId: mediaSourceId,
            value: payload,
        }, []);
        onMediaSourceReload();
    }
    function onMediaSourceReload() {
        var _a;
        // TODO more precize one day?
        var lastObservation = playbackObservationRef.getValue();
        var newInitialTime = lastObservation.position.getWanted();
        if (currentLoadCanceller !== null) {
            currentLoadCanceller.cancel("MediaSource reload");
            currentLoadCanceller = null;
        }
        var contentId = (_a = contentPreparer.getCurrentContent()) === null || _a === void 0 ? void 0 : _a.contentId;
        contentPreparer.reloadMediaSource(sendMessage).then(function () {
            log_1.default.info("Core", "MediaSource Reloaded, loading content again", {
                newInitialTime: newInitialTime,
            });
            startLoadingAt(newInitialTime);
        }, function (err) {
            if (task_canceller_1.default.isCancellationError(err)) {
                log_1.default.info("Core", "A reloading operation was cancelled");
                return;
            }
            sendMessage({
                type: "error" /* CoreMessageType.Error */,
                contentId: contentId,
                value: (0, utils_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, namespace, logs) {
            var sentLogs = logs.map(function (e) {
                if (e instanceof Error) {
                    return (0, utils_1.formatErrorForSender)(e);
                }
                return e;
            });
            // Not relying on `sendMessage` as it also logs
            postMessage({
                type: "log" /* CoreMessageType.LogMessage */,
                value: {
                    namespace: namespace,
                    logLevel: levelStr,
                    logs: sentLogs,
                },
            });
        });
    }
}
/**
 * Send a message `SegmentSinkStoreUpdate` to the main thread with
 * a serialized object that represents the segmentSinksStore state.
 * @param {Function} sendMessage - Function allowing to send messages to the
 * "main thread" part of the RxPlayer logic.
 * @param {ContentPreparer} contentPreparer
 */
function sendSegmentSinksStoreInfos(sendMessage, contentPreparer, requestId) {
    var currentContent = contentPreparer.getCurrentContent();
    if (currentContent === null) {
        return;
    }
    var segmentSinksMetrics = currentContent.segmentSinksStore.getSegmentSinksMetrics();
    sendMessage({
        type: "segment-sink-store-update" /* CoreMessageType.SegmentSinkStoreUpdate */,
        contentId: currentContent.contentId,
        value: { segmentSinkMetrics: segmentSinksMetrics, requestId: requestId },
    });
}
/**
 * Handle accordingly an `IFreezeResolution` object.
 * @param {Function} sendMessage - Function allowing to send messages to the
 * "main thread" part of the RxPlayer logic.
 * @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(sendMessage, freezeResolution, _a) {
    var contentId = _a.contentId, manifest = _a.manifest, handleMediaSourceReload = _a.handleMediaSourceReload, enableRepresentationAvoidance = _a.enableRepresentationAvoidance;
    switch (freezeResolution.type) {
        case "reload": {
            log_1.default.info("Core", "Planning reload due to freeze");
            handleMediaSourceReload({
                timeOffset: 0,
                minimumPosition: 0,
                maximumPosition: Infinity,
            });
            break;
        }
        case "flush": {
            log_1.default.info("Core", "Flushing buffer due to freeze");
            sendMessage({
                type: "needs-buffer-flush" /* CoreMessageType.NeedsBufferFlush */,
                contentId: contentId,
                value: {
                    relativeResumingPosition: freezeResolution.value.relativeSeek,
                    relativePosHasBeenDefaulted: false,
                },
            });
            break;
        }
        case "avoid-representations": {
            log_1.default.info("Core", "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(sendMessage, contentPreparer, msg) {
    var preparedContent = contentPreparer.getCurrentContent();
    var respondWithError = function (err) {
        sendMessage({
            type: "thumbnail-response" /* CoreMessageType.ThumbnailDataResponse */,
            contentId: msg.contentId,
            value: {
                status: "error",
                requestId: msg.value.requestId,
                error: (0, utils_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) {
        // Multiple thumbnail requests can share the same fetched payload.
        // Transfer a copy here so replying to one request does not detach the
        // ArrayBuffer that still has to be sent to another requester.
        var data = result.data.slice(0);
        sendMessage({
            type: "thumbnail-response" /* CoreMessageType.ThumbnailDataResponse */,
            contentId: msg.contentId,
            value: {
                status: "success",
                requestId: msg.value.requestId,
                data: __assign(__assign({}, result), { data: data }),
            },
        }, [data]);
    }, function (err) {
        return respondWithError(err);
    });
}