{"version":3,"file":"vendors-7b4f5c40.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/versioning/Versioning.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/versioning/version.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videocaptureandencodeparameter/DefaultVideoCaptureAndEncodeParameter.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/AllHighestVideoBandwidthPolicy.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/NoVideoDownlinkBandwidthPolicy.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/TargetDisplaySize.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoAdaptiveProbePolicy.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoPreference.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoPreferences.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoPriorityBasedPolicy.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoPriorityBasedPolicyConfig.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videodownlinkbandwidthpolicy/VideoQualityAdaptationPreference.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoelementfactory/NoOpVideoElementFactory.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoframeprocessor/CanvasVideoFrameBuffer.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoframeprocessor/DefaultVideoFrameProcessorPipeline.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoframeprocessor/DefaultVideoFrameProcessorTimer.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoframeprocessor/DefaultVideoTransformDevice.js","webpack://lernplattform-admin-frontend/./node_modules/amazon-chime-sdk-js/build/videoframeprocessor/NoOpVideoFrameProcessor.js"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst DefaultBrowserBehavior_1 = __importDefault(require(\"../browserbehavior/DefaultBrowserBehavior\"));\nconst version_1 = __importDefault(require(\"./version\"));\nclass Versioning {\n    /**\n     * Return string representation of SDK name\n     */\n    static get sdkName() {\n        return 'amazon-chime-sdk-js';\n    }\n    /**\n     * Return string representation of SDK version\n     */\n    static get sdkVersion() {\n        return version_1.default.semverString;\n    }\n    /**\n     * Returns the parts of the semver, so major/minor/patch can be extracted individually.\n     */\n    static get sdkVersionSemVer() {\n        var _a, _b, _c, _d;\n        const v = version_1.default.semverString.match(/^(?<major>[0-9]+)\\.(?<minor>[0-9]+)((?:\\.(?<patch>[0-9]+))(?:-(?<preRelease>[a-zA-Z]+(\\.[0-9])*))?)?/);\n        return {\n            major: (_a = v === null || v === void 0 ? void 0 : v.groups) === null || _a === void 0 ? void 0 : _a.major,\n            minor: (_b = v === null || v === void 0 ? void 0 : v.groups) === null || _b === void 0 ? void 0 : _b.minor,\n            patch: (_c = v === null || v === void 0 ? void 0 : v.groups) === null || _c === void 0 ? void 0 : _c.patch,\n            preRelease: (_d = v === null || v === void 0 ? void 0 : v.groups) === null || _d === void 0 ? void 0 : _d.preRelease,\n        };\n    }\n    /**\n     * Return the SHA-1 of the Git commit from which this build was created.\n     */\n    static get buildSHA() {\n        // Skip the leading 'g'.\n        return version_1.default.hash.substring(1);\n    }\n    /**\n     * Return low-resolution string representation of SDK user agent (e.g. `chrome-78`)\n     */\n    static get sdkUserAgentLowResolution() {\n        const browserBehavior = new DefaultBrowserBehavior_1.default();\n        return `${browserBehavior.name()}-${browserBehavior.majorVersion()}`;\n    }\n    /**\n     * Return URL with versioning information appended\n     */\n    static urlWithVersion(url) {\n        const urlWithVersion = new URL(url);\n        urlWithVersion.searchParams.append(Versioning.X_AMZN_VERSION, Versioning.sdkVersion);\n        urlWithVersion.searchParams.append(Versioning.X_AMZN_USER_AGENT, Versioning.sdkUserAgentLowResolution);\n        return urlWithVersion.toString();\n    }\n}\nexports.default = Versioning;\nVersioning.X_AMZN_VERSION = 'X-Amzn-Version';\nVersioning.X_AMZN_USER_AGENT = 'X-Amzn-User-Agent';\n//# sourceMappingURL=Versioning.js.map","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = {\n    \"hash\": \"0b5a81b\",\n    \"raw\": \"v3.28.0\",\n    \"semverString\": \"3.28.0\"\n};\n//# sourceMappingURL=version.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass DefaultVideoCaptureAndEncodeParameter {\n    constructor(cameraWidth, cameraHeight, cameraFrameRate, maxEncodeBitrateKbps, isSimulcast, scaleResolutionDownBy = 1, isSVC = false) {\n        this.cameraWidth = cameraWidth;\n        this.cameraHeight = cameraHeight;\n        this.cameraFrameRate = cameraFrameRate;\n        this.maxEncodeBitrateKbps = maxEncodeBitrateKbps;\n        this.isSimulcast = isSimulcast;\n        this.scaleResolutionDownBy = scaleResolutionDownBy;\n        this.isSVC = isSVC;\n    }\n    equal(other) {\n        let checkForEqual = other.captureWidth() === this.cameraWidth &&\n            other.captureHeight() === this.cameraHeight &&\n            other.captureFrameRate() === this.cameraFrameRate &&\n            other.encodeBitrates().length === this.encodeBitrates().length &&\n            other.encodeScaleResolutionDownBy().length === this.encodeScaleResolutionDownBy().length &&\n            other.encodeWidths().length === this.encodeWidths().length &&\n            other.encodeHeights().length === this.encodeHeights().length &&\n            other.isSVCEncoding() === this.isSVC;\n        if (checkForEqual) {\n            for (let i = 0; i < other.encodeWidths().length; i++) {\n                if (other.encodeWidths()[i] !== this.encodeWidths()[i] ||\n                    other.encodeHeights()[i] !== this.encodeHeights()[i] ||\n                    other.encodeBitrates()[i] !== this.encodeBitrates()[i] ||\n                    other.encodeScaleResolutionDownBy()[i] !== this.encodeScaleResolutionDownBy()[i]) {\n                    checkForEqual = false;\n                    return checkForEqual;\n                }\n            }\n        }\n        return checkForEqual;\n    }\n    clone() {\n        return new DefaultVideoCaptureAndEncodeParameter(this.cameraWidth, this.cameraHeight, this.cameraFrameRate, this.maxEncodeBitrateKbps, this.isSimulcast, this.scaleResolutionDownBy, this.isSVC);\n    }\n    captureWidth() {\n        return this.cameraWidth;\n    }\n    captureHeight() {\n        return this.cameraHeight;\n    }\n    captureFrameRate() {\n        return this.cameraFrameRate;\n    }\n    encodeBitrates() {\n        // TODO: add simulcast layer\n        return [this.maxEncodeBitrateKbps];\n    }\n    encodeScaleResolutionDownBy() {\n        return [this.scaleResolutionDownBy];\n    }\n    encodeWidths() {\n        return [this.cameraWidth];\n    }\n    encodeHeights() {\n        return [this.cameraHeight];\n    }\n    isSVCEncoding() {\n        return this.isSVC;\n    }\n}\nexports.default = DefaultVideoCaptureAndEncodeParameter;\n//# sourceMappingURL=DefaultVideoCaptureAndEncodeParameter.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst DefaultVideoStreamIdSet_1 = __importDefault(require(\"../videostreamidset/DefaultVideoStreamIdSet\"));\n/**\n * [[AllHighestVideoBandwidthPolicy]] implements is a rudimentary policy that simply\n * always subscribes to the highest quality video stream available\n * for all non-self participants.\n */\nclass AllHighestVideoBandwidthPolicy {\n    constructor(selfAttendeeId) {\n        this.selfAttendeeId = selfAttendeeId;\n        this.reset();\n    }\n    reset() {\n        this.optimalReceiveSet = new DefaultVideoStreamIdSet_1.default();\n        this.subscribedReceiveSet = new DefaultVideoStreamIdSet_1.default();\n        this.videoSources = undefined;\n    }\n    updateIndex(videoIndex) {\n        this.videoIndex = videoIndex;\n        this.optimalReceiveSet = this.calculateOptimalReceiveSet(videoIndex);\n    }\n    updateMetrics(_clientMetricReport) { }\n    wantsResubscribe() {\n        return !this.subscribedReceiveSet.equal(this.optimalReceiveSet);\n    }\n    chooseSubscriptions() {\n        this.subscribedReceiveSet = this.optimalReceiveSet.clone();\n        return this.subscribedReceiveSet.clone();\n    }\n    chooseRemoteVideoSources(videoSources) {\n        this.videoSources = videoSources;\n        this.optimalReceiveSet = this.calculateOptimalReceiveSet(this.videoIndex).clone();\n    }\n    calculateOptimalReceiveSet(videoIndex) {\n        const streamSelectionSet = new DefaultVideoStreamIdSet_1.default();\n        if (!this.videoIndex || this.videoIndex.allStreams().empty()) {\n            return streamSelectionSet;\n        }\n        const receiveSet = videoIndex.highestQualityStreamFromEachGroupExcludingSelf(this.selfAttendeeId);\n        // If video sources are not chosen, then use all of them.\n        const videoSources = !!this.videoSources\n            ? this.videoSources\n            : this.videoIndex.allVideoSendingSourcesExcludingSelf(this.selfAttendeeId);\n        // Get the list of all the remote stream information\n        const remoteInfos = this.videoIndex.remoteStreamDescriptions();\n        const mapOfAttendeeIdToRemoteDescriptions = new Map();\n        for (const info of remoteInfos) {\n            if (receiveSet.contain(info.streamId)) {\n                mapOfAttendeeIdToRemoteDescriptions.set(info.attendeeId, info);\n            }\n        }\n        let totalBitrateKbps = 0;\n        for (const videoSource of videoSources) {\n            const attendeeId = videoSource.attendee.attendeeId;\n            if (mapOfAttendeeIdToRemoteDescriptions.has(attendeeId)) {\n                const info = mapOfAttendeeIdToRemoteDescriptions.get(attendeeId);\n                if (totalBitrateKbps + info.maxBitrateKbps <=\n                    AllHighestVideoBandwidthPolicy.maxReceiveBitrateKbps) {\n                    streamSelectionSet.add(info.streamId);\n                    totalBitrateKbps += info.maxBitrateKbps;\n                }\n                else {\n                    console.warn('total bitrate ' +\n                        (totalBitrateKbps + info.maxBitrateKbps) +\n                        ' exceeds maximum limit (15000). Use chooseRemoteVideoSources to select a subset of participants to avoid this.');\n                    // We could continue to check more sources (some of them might still fall under the limit).\n                    // But we stop here to limit resubscribes if we're hovering near the cap.\n                    break;\n                }\n            }\n        }\n        return streamSelectionSet;\n    }\n}\nexports.default = AllHighestVideoBandwidthPolicy;\n// Cap total receive bitrate at 15000 kbps to avoid hitting per client connection limits\nAllHighestVideoBandwidthPolicy.maxReceiveBitrateKbps = 15000;\n//# sourceMappingURL=AllHighestVideoBandwidthPolicy.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst DefaultVideoStreamIdSet_1 = __importDefault(require(\"../videostreamidset/DefaultVideoStreamIdSet\"));\nclass NoVideoDownlinkBandwidthPolicy {\n    reset() { }\n    updateIndex(_videoIndex) { }\n    updateMetrics(_clientMetricReport) { }\n    wantsResubscribe() {\n        return false;\n    }\n    chooseSubscriptions() {\n        return new DefaultVideoStreamIdSet_1.default();\n    }\n}\nexports.default = NoVideoDownlinkBandwidthPolicy;\n//# sourceMappingURL=NoVideoDownlinkBandwidthPolicy.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TargetDisplaySize = void 0;\n/**\n * [[TargetDisplaySize]] represents the max resolution that a video stream can have when simulcast is enabled in priority based downlink policy.\n * If there is only one stream being sent, then this field will get ignored.  Its values currently parallel [[SimulcastLayers]].\n */\nvar TargetDisplaySize;\n(function (TargetDisplaySize) {\n    /**\n     * Low resolution video stream.\n     */\n    TargetDisplaySize[TargetDisplaySize[\"Low\"] = 0] = \"Low\";\n    /**\n     * Medium resolution video stream.\n     */\n    TargetDisplaySize[TargetDisplaySize[\"Medium\"] = 1] = \"Medium\";\n    /**\n     * High resolution video stream.\n     */\n    TargetDisplaySize[TargetDisplaySize[\"High\"] = 2] = \"High\";\n    /**\n     * Maximum resolution video stream.\n     */\n    TargetDisplaySize[TargetDisplaySize[\"Maximum\"] = 3] = \"Maximum\";\n})(TargetDisplaySize = exports.TargetDisplaySize || (exports.TargetDisplaySize = {}));\nexports.default = TargetDisplaySize;\n//# sourceMappingURL=TargetDisplaySize.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst ContentShareConstants_1 = __importDefault(require(\"../contentsharecontroller/ContentShareConstants\"));\nconst ServerSideNetworkAdaption_1 = __importDefault(require(\"../signalingclient/ServerSideNetworkAdaption\"));\nconst VideoPreference_1 = __importDefault(require(\"./VideoPreference\"));\nconst VideoPreferences_1 = require(\"./VideoPreferences\");\nconst VideoPriorityBasedPolicy_1 = __importDefault(require(\"./VideoPriorityBasedPolicy\"));\nconst VideoPriorityBasedPolicyConfig_1 = __importDefault(require(\"./VideoPriorityBasedPolicyConfig\"));\n/**\n * [[VideoAdaptiveProbePolicy]] wraps [[VideoPriorityBasedPolicy]] with customized behavior to automatically\n * assign a high preference to content share. This is a legacy policy used by default when simulcast is enabled, but\n * all customers should be using `VideoPriorityBasedPolicy` instead, since this policy does not work with\n * server side network adaptation.\n *\n * You can imitate this classes behavior using the `VideoPriorityBasedPolicy` with the following snippet\n * ```\n *   remoteVideoSourcesDidChange(videoSources: VideoSource[]) {\n *     const videoPreferences = VideoPreferences.prepare();\n *     for(const source of videoSources) {\n *        const isContent = source.attendee.attendeeId.endsWith(ContentShareConstants.Modality);\n *        videoPreferences.add(new VideoPreference(source.attendee.attendeeId, isContent ? 2: 1, TargetDisplaySize.High));\n *     }\n *     this.priorityBasedDownlinkPolicy.chooseRemoteVideoSources(videoPreferences.build());\n *   }\n * ```\n */\nclass VideoAdaptiveProbePolicy extends VideoPriorityBasedPolicy_1.default {\n    constructor(logger) {\n        // We use a static function to create config because Typescript requires\n        // super(...) calls to be the first line in constructors\n        super(logger, VideoAdaptiveProbePolicy.createConfig());\n        this.logger = logger;\n        super.shouldPauseTiles = false;\n        this.videoPreferences = undefined;\n    }\n    static createConfig() {\n        const config = new VideoPriorityBasedPolicyConfig_1.default();\n        config.serverSideNetworkAdaption = ServerSideNetworkAdaption_1.default.None;\n        return config;\n    }\n    reset() {\n        super.reset();\n        super.shouldPauseTiles = false;\n        this.videoPreferences = undefined;\n    }\n    updateIndex(videoIndex) {\n        super.updateIndex(videoIndex);\n        const newPreferences = VideoPreferences_1.VideoPreferences.prepare();\n        let containsContent = false;\n        const remoteInfos = videoIndex.remoteStreamDescriptions();\n        // If there is active content then set that as high priority, and the rest at lower\n        for (const info of remoteInfos) {\n            // I don't know why we need to do this duplicate check.\n            if (!newPreferences.some(preference => preference.attendeeId === info.attendeeId)) {\n                // For now always subscribe to content even if higher bandwidth then target\n                if (info.attendeeId.endsWith(ContentShareConstants_1.default.Modality)) {\n                    newPreferences.add(new VideoPreference_1.default(info.attendeeId, 1));\n                    containsContent = true;\n                }\n                else {\n                    newPreferences.add(new VideoPreference_1.default(info.attendeeId, 2));\n                }\n            }\n        }\n        if (containsContent) {\n            this.videoPreferences = newPreferences.build();\n            this.videoPreferencesUpdated = true;\n        }\n        else {\n            this.videoPreferences = undefined;\n        }\n    }\n    /**\n     * [[VideoAdaptiveProbePolicy]] does not allow setting video preferences and this function\n     * will be a no-op.  Please use [[VideoPriorityBasedPolicy]] directly if you would like to set\n     * preferences.\n     */\n    chooseRemoteVideoSources(_preferences) {\n        this.logger.error('chooseRemoteVideoSources should not be called by VideoAdaptiveProbePolicy');\n        return;\n    }\n}\nexports.default = VideoAdaptiveProbePolicy;\n//# sourceMappingURL=VideoAdaptiveProbePolicy.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst TargetDisplaySize_1 = __importDefault(require(\"./TargetDisplaySize\"));\nconst VideoQualityAdaptationPreference_1 = __importDefault(require(\"./VideoQualityAdaptationPreference\"));\nclass VideoPreference {\n    /** Initializes a [[VideoPreference]] with the given properties.\n     *\n     * @param attendeeId Attendee ID of the client\n     * @param priority The relative priority of this attendee against others.\n     * @param targetSize The desired maximum simulcast layers to receive.\n     * @param degradationPreference The preference on how to select between resolution and framerate on network constraint\n     */\n    constructor(attendeeId, priority, targetSize, degradationPreference) {\n        this.attendeeId = attendeeId;\n        this.priority = priority;\n        this.targetSize = targetSize !== undefined ? targetSize : TargetDisplaySize_1.default.Maximum;\n        this.degradationPreference =\n            degradationPreference !== undefined\n                ? degradationPreference\n                : VideoQualityAdaptationPreference_1.default.Balanced;\n    }\n    partialCompare(other) {\n        return this.priority - other.priority;\n    }\n    equals(other) {\n        return (this.attendeeId === other.attendeeId &&\n            this.targetSize === other.targetSize &&\n            this.priority === other.priority &&\n            this.degradationPreference === other.degradationPreference);\n    }\n    clone() {\n        return new VideoPreference(this.attendeeId, this.priority, this.targetSize, this.degradationPreference);\n    }\n    targetSizeToBitrateKbps(targetSize) {\n        switch (targetSize) {\n            case TargetDisplaySize_1.default.Maximum:\n                return VideoPreference.MAXIMUM_BITRATE_KBPS;\n            case TargetDisplaySize_1.default.High:\n                return VideoPreference.HIGH_BITRATE_KBPS;\n            case TargetDisplaySize_1.default.Medium:\n                return VideoPreference.MID_BITRATE_KBPS;\n            case TargetDisplaySize_1.default.Low:\n                return VideoPreference.LOW_BITRATE_KBPS;\n        }\n    }\n}\nexports.default = VideoPreference;\nVideoPreference.LOW_BITRATE_KBPS = 300;\nVideoPreference.MID_BITRATE_KBPS = 600;\nVideoPreference.HIGH_BITRATE_KBPS = 1500;\nVideoPreference.MAXIMUM_BITRATE_KBPS = 10000;\n//# sourceMappingURL=VideoPreference.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.VideoPreferences = exports.MutableVideoPreferences = exports.convertVideoPreferencesToSignalingClientVideoSubscriptionConfiguration = void 0;\nconst SignalingClientVideoSubscriptionConfiguration_1 = __importDefault(require(\"../signalingclient/SignalingClientVideoSubscriptionConfiguration\"));\nfunction convertVideoPreferencesToSignalingClientVideoSubscriptionConfiguration(context, receiveGroupIds, preferences) {\n    if (context.transceiverController.getMidForGroupId === undefined ||\n        context.videoStreamIndex.attendeeIdForGroupId === undefined ||\n        preferences === undefined) {\n        return [];\n    }\n    const configurations = new Array();\n    const attendeeIdToMid = new Map();\n    const attendeeIdToGroupId = new Map();\n    for (const groupId of receiveGroupIds) {\n        // The local description will have been set by the time this task is running, so all\n        // of the transceivers should have `mid` set by now (see comment above `getMidForStreamId`)\n        const mid = context.transceiverController.getMidForGroupId(groupId);\n        if (mid === undefined) {\n            continue;\n        }\n        const attendeeId = context.videoStreamIndex.attendeeIdForGroupId(groupId);\n        attendeeIdToMid.set(attendeeId, mid);\n        attendeeIdToGroupId.set(attendeeId, groupId);\n    }\n    for (const preference of preferences) {\n        const configuration = new SignalingClientVideoSubscriptionConfiguration_1.default();\n        const mid = attendeeIdToMid.get(preference.attendeeId);\n        if (mid === undefined) {\n            continue;\n        }\n        configuration.mid = mid;\n        configuration.attendeeId = preference.attendeeId;\n        configuration.groupId = attendeeIdToGroupId.get(preference.attendeeId);\n        // The signaling protocol expects 'higher' values for 'higher' priorities\n        configuration.priority = Number.MAX_SAFE_INTEGER - preference.priority;\n        configuration.targetBitrateKbps = preference.targetSizeToBitrateKbps(preference.targetSize);\n        configuration.qualityAdaptationPreference = preference.degradationPreference;\n        configurations.push(configuration);\n    }\n    return configurations;\n}\nexports.convertVideoPreferencesToSignalingClientVideoSubscriptionConfiguration = convertVideoPreferencesToSignalingClientVideoSubscriptionConfiguration;\nclass ObjectSet {\n    constructor(items = []) {\n        this.items = items;\n    }\n    static default() {\n        return new ObjectSet([]);\n    }\n    // Returns the items in sorted order.\n    [Symbol.iterator]() {\n        let i = 0;\n        const items = this.items;\n        return {\n            next() {\n                if (i < items.length) {\n                    return {\n                        done: false,\n                        value: items[i++],\n                    };\n                }\n                return {\n                    done: true,\n                    value: null,\n                };\n            },\n        };\n    }\n    first() {\n        return this.items[0];\n    }\n    add(item) {\n        // If this is used elsewhere, there needs to be a duplicate check here\n        this.items.push(item);\n    }\n    replaceFirst(newItem, f) {\n        const pos = this.items.findIndex(f);\n        if (pos === -1) {\n            // If this is used elsewhere, there needs to be a duplicate check here\n            this.items.push(newItem);\n        }\n        else if (!this.has(newItem)) {\n            this.items[pos] = newItem;\n        }\n        else {\n            this.items.splice(pos, 1);\n        }\n    }\n    remove(item) {\n        this.items = this.items.filter(a => !a.equals(item));\n    }\n    clear() {\n        this.items = [];\n    }\n    isEmpty() {\n        return this.items.length === 0;\n    }\n    equals(other) {\n        if (other === this) {\n            return true;\n        }\n        if (other.items.length !== this.items.length) {\n            return false;\n        }\n        // TODO: if we keep the arrays sorted correctly, not just by priority, then\n        // we don't need to do this painstaking O(n^2) work.\n        for (const item of this.items) {\n            if (!other.items.some(a => a.equals(item))) {\n                return false;\n            }\n        }\n        return true;\n    }\n    has(item) {\n        return this.items.some(a => a.equals(item));\n    }\n    some(f) {\n        return this.items.some(f);\n    }\n    clone() {\n        return new ObjectSet([...this.items]);\n    }\n    sort() {\n        this.items.sort((a, b) => a.partialCompare(b));\n    }\n    modify() {\n        // COW.\n        return new SetBuilder(this);\n    }\n}\nclass SetBuilder {\n    constructor(items = new ObjectSet()) {\n        this.items = items;\n        this.copied = false;\n    }\n    cow() {\n        if (this.copied) {\n            return;\n        }\n        this.items = this.items.clone();\n        this.copied = true;\n    }\n    add(item) {\n        // Don't actually need to COW unless the item is there to add.\n        if (this.items.has(item)) {\n            return;\n        }\n        this.cow();\n        this.items.add(item);\n    }\n    replaceFirst(newItem, f) {\n        // Don't actually need to COW unless the item is already there\n        // and there are no items to replace\n        if (this.items.has(newItem) && !this.items.some(f)) {\n            return;\n        }\n        this.cow();\n        this.items.replaceFirst(newItem, f);\n    }\n    remove(item) {\n        // Don't actually need to COW unless the item is there to remove.\n        if (!this.items.has(item)) {\n            return;\n        }\n        this.cow();\n        this.items.remove(item);\n    }\n    some(f) {\n        return this.items.some(f);\n    }\n    clear() {\n        if (this.items.isEmpty()) {\n            return;\n        }\n        this.cow();\n        this.items.clear();\n    }\n    build() {\n        // Immutable sets are always kept sorted!\n        if (this.copied) {\n            this.items.sort();\n        }\n        this.copied = false;\n        return this.items;\n    }\n}\nclass MutableVideoPreferences {\n    constructor(builder) {\n        this.builder = builder;\n    }\n    add(pref) {\n        this.builder.add(pref);\n    }\n    replaceFirst(newPref, f) {\n        this.builder.replaceFirst(newPref, f);\n    }\n    remove(pref) {\n        this.builder.remove(pref);\n    }\n    some(f) {\n        return this.builder.some(f);\n    }\n    clear() {\n        this.builder.clear();\n    }\n    build() {\n        return new VideoPreferences(this.builder.build());\n    }\n}\nexports.MutableVideoPreferences = MutableVideoPreferences;\nclass VideoPreferences {\n    /** @internal */\n    constructor(items) {\n        this.items = items;\n    }\n    static prepare() {\n        return new MutableVideoPreferences(new SetBuilder());\n    }\n    static default() {\n        return new VideoPreferences(ObjectSet.default());\n    }\n    [Symbol.iterator]() {\n        return this.items[Symbol.iterator]();\n    }\n    highestPriority() {\n        var _a;\n        return (_a = this.items.first()) === null || _a === void 0 ? void 0 : _a.priority;\n    }\n    // Our items happen to always be sorted!\n    sorted() {\n        return this.items[Symbol.iterator]();\n    }\n    equals(other) {\n        return other === this || this.items.equals(other.items);\n    }\n    modify() {\n        return new MutableVideoPreferences(this.items.modify());\n    }\n    some(f) {\n        return this.items.some(f);\n    }\n    isEmpty() {\n        return this.items.isEmpty();\n    }\n    clone() {\n        const videoPreferences = VideoPreferences.prepare();\n        for (const preference of this.items) {\n            videoPreferences.add(preference.clone());\n        }\n        return videoPreferences.build();\n    }\n}\nexports.VideoPreferences = VideoPreferences;\nexports.default = VideoPreferences;\n//# sourceMappingURL=VideoPreferences.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\n}) : (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    o[k2] = m[k];\n}));\nvar __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {\n    Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n    o[\"default\"] = v;\n});\nvar __importStar = (this && this.__importStar) || function (mod) {\n    if (mod && mod.__esModule) return mod;\n    var result = {};\n    if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n    __setModuleDefault(result, mod);\n    return result;\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst Attendee_1 = __importDefault(require(\"../attendee/Attendee\"));\nconst ClientMetricReportDirection_1 = __importDefault(require(\"../clientmetricreport/ClientMetricReportDirection\"));\nconst ClientMetricReportMediaType_1 = __importDefault(require(\"../clientmetricreport/ClientMetricReportMediaType\"));\nconst ContentShareConstants_1 = __importDefault(require(\"../contentsharecontroller/ContentShareConstants\"));\nconst LogLevel_1 = require(\"../logger/LogLevel\");\nconst ServerSideNetworkAdaption_1 = __importStar(require(\"../signalingclient/ServerSideNetworkAdaption\"));\nconst VideoSource_1 = __importDefault(require(\"../videosource/VideoSource\"));\nconst DefaultVideoStreamIdSet_1 = __importDefault(require(\"../videostreamidset/DefaultVideoStreamIdSet\"));\nconst AllHighestVideoBandwidthPolicy_1 = __importDefault(require(\"./AllHighestVideoBandwidthPolicy\"));\nconst TargetDisplaySize_1 = __importDefault(require(\"./TargetDisplaySize\"));\nconst VideoPreference_1 = __importDefault(require(\"./VideoPreference\"));\nconst VideoPreferences_1 = require(\"./VideoPreferences\");\nconst VideoPriorityBasedPolicyConfig_1 = __importDefault(require(\"./VideoPriorityBasedPolicyConfig\"));\nconst VideoQualityAdaptationPreference_1 = __importDefault(require(\"./VideoQualityAdaptationPreference\"));\n/** @internal */\nclass LinkMediaStats {\n    constructor() {\n        this.bandwidthEstimateKbps = 0;\n        this.usedBandwidthKbps = 0;\n        this.packetsLost = 0;\n        this.nackCount = 0;\n        this.rttMs = 0;\n    }\n}\nclass VideoPriorityBasedPolicy {\n    constructor(logger, videoPriorityBasedPolicyConfig = VideoPriorityBasedPolicyConfig_1.default.Default) {\n        this.logger = logger;\n        this.videoPriorityBasedPolicyConfig = videoPriorityBasedPolicyConfig;\n        this.shouldPauseTiles = true;\n        this.observerQueue = new Set();\n        this.pausedBwAttendeeIds = new Set();\n        this.wantsResubscribeObserver = undefined;\n        if (this.videoPriorityBasedPolicyConfig.serverSideNetworkAdaption ===\n            ServerSideNetworkAdaption_1.default.Default) {\n            this.videoPriorityBasedPolicyConfig.serverSideNetworkAdaption =\n                ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption;\n        }\n        this.reset();\n    }\n    reset() {\n        // Self-attendee is not actually necessary in 'AllHighestVideoBandwidthPolicy' since it just passes it to\n        // `highestQualityStreamFromEachGroupExcludingSelf` which itself doesn't need it as self attendees are not in the\n        // index\n        this.allHighestPolicy = new AllHighestVideoBandwidthPolicy_1.default('');\n        this.optimalReceiveSet = new DefaultVideoStreamIdSet_1.default();\n        this.optimalReceiveStreams = [];\n        this.optimalNonPausedReceiveStreams = [];\n        this.subscribedReceiveSet = new DefaultVideoStreamIdSet_1.default();\n        this.subscribedReceiveStreams = [];\n        this.videoPreferences = undefined;\n        this.defaultVideoPreferences = undefined;\n        this.shouldPauseTiles = true;\n        this.pausedStreamIds = new DefaultVideoStreamIdSet_1.default();\n        this.pausedBwAttendeeIds = new Set();\n        this.videoPreferencesUpdated = false;\n        this.logCount = 0;\n        this.startupPeriod = true;\n        this.usingPrevTargetRate = false;\n        this.rateProbeState = \"Not Probing\" /* NotProbing */;\n        this.firstEstimateTimestamp = 0;\n        this.lastUpgradeRateKbps = 0;\n        this.timeBeforeAllowSubscribeMs = VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_SUBSCRIBE_MS;\n        this.lastProbeTimestamp = Date.now();\n        this.timeBeforeAllowProbeMs = VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_PROBE_MS;\n        this.downlinkStats = new LinkMediaStats();\n        this.prevDownlinkStats = new LinkMediaStats();\n        this.probeFailed = false;\n        this.serverSideNetworkAdaption = this.videoPriorityBasedPolicyConfig.serverSideNetworkAdaption;\n        this.pendingActionAfterUpdatedPreferences = false;\n    }\n    bindToTileController(tileController) {\n        this.tileController = tileController;\n        this.logger.info('tileController bound');\n    }\n    setWantsResubscribeObserver(observer) {\n        this.wantsResubscribeObserver = observer;\n    }\n    // This function allows setting preferences without the need to inherit from this class\n    // which would require not using the internal keyword\n    chooseRemoteVideoSources(preferences) {\n        var _a;\n        if ((_a = this.videoPreferences) === null || _a === void 0 ? void 0 : _a.equals(preferences)) {\n            return;\n        }\n        this.videoPreferences = preferences === null || preferences === void 0 ? void 0 : preferences.clone();\n        if (!ServerSideNetworkAdaption_1.serverSideNetworkAdaptionIsNoneOrDefault(this.serverSideNetworkAdaption)) {\n            this.pendingActionAfterUpdatedPreferences = true;\n            if (this.wantsResubscribeObserver !== undefined) {\n                this.wantsResubscribeObserver();\n                this.pendingActionAfterUpdatedPreferences = false;\n            }\n            if (this.serverSideNetworkAdaption ===\n                ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption) {\n                const desiredVideoSources = new Array();\n                for (const preference of this.videoPreferences) {\n                    const source = new VideoSource_1.default();\n                    source.attendee = new Attendee_1.default();\n                    source.attendee.attendeeId = preference.attendeeId;\n                    desiredVideoSources.push(source);\n                }\n                this.allHighestPolicy.chooseRemoteVideoSources(desiredVideoSources);\n                return;\n            }\n        }\n        this.videoPreferencesUpdated = true;\n        this.logger.info(`bwe: setVideoPreferences bwe: new preferences: ${JSON.stringify(preferences)}`);\n        return;\n    }\n    updateIndex(videoIndex) {\n        this.videoIndex = videoIndex;\n        if (this.serverSideNetworkAdaption ===\n            ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption) {\n            this.allHighestPolicy.updateIndex(videoIndex);\n        }\n        if (!this.videoPreferences) {\n            this.updateDefaultVideoPreferences();\n        }\n    }\n    updateDefaultVideoPreferences() {\n        const attendeeIds = new Set();\n        for (const stream of this.videoIndex.remoteStreamDescriptions()) {\n            attendeeIds.add(stream.attendeeId);\n        }\n        const prefs = VideoPreferences_1.VideoPreferences.prepare();\n        const numAttendees = attendeeIds.size;\n        let targetDisplaySize = TargetDisplaySize_1.default.Maximum;\n        if (numAttendees > 8) {\n            targetDisplaySize = TargetDisplaySize_1.default.Low;\n        }\n        else if (numAttendees > 4) {\n            targetDisplaySize = TargetDisplaySize_1.default.Medium;\n        }\n        for (const attendeeId of attendeeIds) {\n            prefs.add(new VideoPreference_1.default(attendeeId, 1, targetDisplaySize));\n        }\n        this.defaultVideoPreferences = prefs.build();\n    }\n    updateMetrics(clientMetricReport) {\n        if (!this.videoIndex || this.videoIndex.allStreams().empty()) {\n            return;\n        }\n        this.prevDownlinkStats = this.downlinkStats;\n        this.downlinkStats = new LinkMediaStats();\n        const metricReport = clientMetricReport.getObservableMetrics();\n        // availableIncomingBitrate is the standard stat but is not available in Firefox yet so only Safari for now.\n        this.downlinkStats.bandwidthEstimateKbps = metricReport.availableIncomingBitrate / 1000;\n        for (const ssrcStr in clientMetricReport.streamMetricReports) {\n            const ssrc = Number.parseInt(ssrcStr, 10);\n            const metrics = clientMetricReport.streamMetricReports[ssrc];\n            if (metrics.direction === ClientMetricReportDirection_1.default.DOWNSTREAM && metrics.mediaType === ClientMetricReportMediaType_1.default.VIDEO) {\n                // Only use video stream metrics\n                if (metrics.currentMetrics.hasOwnProperty('nackCount')) {\n                    this.downlinkStats.nackCount += clientMetricReport.countPerSecond('nackCount', ssrc);\n                }\n                if (metrics.currentMetrics.hasOwnProperty('packetsLost')) {\n                    this.downlinkStats.packetsLost += clientMetricReport.countPerSecond('packetsLost', ssrc);\n                }\n                if (metrics.currentMetrics.hasOwnProperty('bytesReceived')) {\n                    this.downlinkStats.usedBandwidthKbps +=\n                        clientMetricReport.bitsPerSecond('bytesReceived', ssrc) / 1000;\n                }\n            }\n        }\n    }\n    wantsResubscribe() {\n        let wants = false;\n        if (!ServerSideNetworkAdaption_1.serverSideNetworkAdaptionIsNoneOrDefault(this.serverSideNetworkAdaption)) {\n            wants || (wants = this.pendingActionAfterUpdatedPreferences);\n            if (this.serverSideNetworkAdaption ===\n                ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption) {\n                return wants || this.allHighestPolicy.wantsResubscribe();\n            }\n        }\n        this.calculateOptimalReceiveSet();\n        wants || (wants = !this.subscribedReceiveSet.equal(this.optimalReceiveSet));\n        return wants;\n    }\n    chooseSubscriptions() {\n        if (!ServerSideNetworkAdaption_1.serverSideNetworkAdaptionIsNoneOrDefault(this.serverSideNetworkAdaption)) {\n            this.pendingActionAfterUpdatedPreferences = false;\n            if (this.serverSideNetworkAdaption ===\n                ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption) {\n                return this.allHighestPolicy.chooseSubscriptions();\n            }\n        }\n        if (!this.subscribedReceiveSet.equal(this.optimalReceiveSet)) {\n            this.lastSubscribeTimestamp = Date.now();\n        }\n        this.subscribedReceiveSet = this.optimalReceiveSet.clone();\n        this.subscribedReceiveStreams = this.optimalReceiveStreams.slice();\n        return this.subscribedReceiveSet.clone();\n    }\n    addObserver(observer) {\n        this.observerQueue.add(observer);\n    }\n    removeObserver(observer) {\n        this.observerQueue.delete(observer);\n    }\n    forEachObserver(observerFunc) {\n        for (const observer of this.observerQueue) {\n            observerFunc(observer);\n        }\n    }\n    setVideoPriorityBasedPolicyConfigs(config) {\n        this.videoPriorityBasedPolicyConfig = config;\n    }\n    calculateOptimalReceiveStreams() {\n        var _a;\n        const chosenStreams = [];\n        const remoteInfos = this.videoIndex.remoteStreamDescriptions();\n        if (remoteInfos.length === 0 || ((_a = this.videoPreferences) === null || _a === void 0 ? void 0 : _a.isEmpty())) {\n            this.optimalReceiveStreams = [];\n            return;\n        }\n        const lastProbeState = this.rateProbeState;\n        this.cleanBwPausedTiles(remoteInfos);\n        this.handleAppPausedStreams(chosenStreams, remoteInfos);\n        const attendeeIdToPreference = new Map();\n        for (const preference of this.getCurrentVideoPreferences()) {\n            attendeeIdToPreference.set(preference.attendeeId, preference);\n        }\n        // We can pre-emptively filter to just remote stream descriptions for remote video sources we already\n        // have configuration for. We do not care about streams we aren't going to subscribe to.\n        const remoteInfosWithPreference = remoteInfos.filter((stream) => {\n            return attendeeIdToPreference.has(stream.attendeeId);\n        });\n        const sameStreamChoices = !this.streamsWithPreferenceDidChange(remoteInfosWithPreference);\n        const noMajorChange = !this.startupPeriod && sameStreamChoices;\n        // If no major changes then don't allow subscribes for the allowed amount of time\n        if (noMajorChange &&\n            Date.now() - this.lastSubscribeTimestamp < this.timeBeforeAllowSubscribeMs) {\n            return;\n        }\n        // Sort streams by bitrate ascending.\n        remoteInfosWithPreference.sort((a, b) => {\n            if (a.maxBitrateKbps === b.maxBitrateKbps) {\n                return a.streamId - b.streamId;\n            }\n            return a.maxBitrateKbps - b.maxBitrateKbps;\n        });\n        // Convert 0 avg bitrates to max, handle special cases, and remove upgrades that downgrade resolution or framerate\n        for (const info of remoteInfosWithPreference) {\n            if (info.avgBitrateKbps === 0 || info.avgBitrateKbps > info.maxBitrateKbps) {\n                // Content can be a special case\n                if (info.attendeeId.endsWith(ContentShareConstants_1.default.Modality) && info.maxBitrateKbps < 100) {\n                    info.maxBitrateKbps = info.avgBitrateKbps;\n                }\n                else {\n                    info.avgBitrateKbps = info.maxBitrateKbps;\n                }\n            }\n        }\n        const streamIdsToRemove = new Set();\n        for (const preference of this.getCurrentVideoPreferences()) {\n            const streamsForAttendees = remoteInfosWithPreference.filter((description) => {\n                return description.attendeeId === preference.attendeeId;\n            });\n            if (streamsForAttendees.length < 3) {\n                // If `streamsForAttendees.length` is 0, they are stale preference for remote video that no longer exists\n                // if 1 or 2, there is nothing to filter out\n                continue;\n            }\n            let maxFrameRate = 15;\n            let maxPixels = 320 * 480;\n            streamsForAttendees.forEach((stream) => {\n                maxFrameRate = Math.max(maxFrameRate, stream.maxFrameRate);\n                maxPixels = Math.max(maxPixels, stream.totalPixels());\n            });\n            streamsForAttendees.sort((a, b) => {\n                if (preference.degradationPreference === VideoQualityAdaptationPreference_1.default.MaintainResolution) {\n                    // This may seem counter-intuitive but given we want to upgrade resolution first, and framerate\n                    // last, we want to sort by framerate first, and resolution only if framerate is close.\n                    //\n                    // That way, e.g. the first three streams will all contain increases in resolution. We will skip any down\n                    // grades in resolution (i.e. to hop to a higher framerate stream) in the section below\n                    if (Math.abs(a.maxFrameRate - b.maxFrameRate) < 2) {\n                        return a.totalPixels() - b.totalPixels();\n                    }\n                    return a.maxFrameRate - b.maxFrameRate;\n                }\n                else if (preference.degradationPreference === VideoQualityAdaptationPreference_1.default.MaintainFramerate) {\n                    // See note above, but swap resolution for framerate\n                    if (a.totalPixels() === b.totalPixels()) {\n                        return a.maxFrameRate - b.maxFrameRate;\n                    }\n                    return a.totalPixels() - b.totalPixels();\n                }\n                else {\n                    // In 'balanced' mode, we want a slight preference towards upgrading resolution first\n                    // when moving up the bitrate ladder, so we weigh the bitrate by a framerate\n                    // and resolution derived value that is tuned to be reasonable in most cases. This attempts to\n                    // mimic the diagrams in the guide.\n                    //\n                    // A higher constant makes the policy prefer resolution to framerate more at lower values\n                    const framerateExponentConstant = 5;\n                    // A higher constant here makes the policy prefer framerate to resolution more at higher values\n                    const resolutionBaseConstant = 2;\n                    const weighByFramerate = (stream) => {\n                        return (stream.avgBitrateKbps *\n                            Math.pow(2, ((framerateExponentConstant * stream.maxFrameRate) / maxFrameRate) *\n                                Math.pow(resolutionBaseConstant * 2, stream.totalPixels() / maxPixels)));\n                    };\n                    return weighByFramerate(a) - weighByFramerate(b);\n                }\n            });\n            let lastInfo = undefined;\n            // Downgrades during recovery are unusual and jarring. Here we will strip out any (these should result in paths that\n            // match those in the priority downlink policy guide).\n            for (const info of streamsForAttendees) {\n                if (lastInfo !== undefined) {\n                    if ((info.maxFrameRate < lastInfo.maxFrameRate &&\n                        Math.abs(lastInfo.maxFrameRate - info.maxFrameRate) > 2) ||\n                        info.totalPixels() < lastInfo.totalPixels()) {\n                        streamIdsToRemove.add(info.streamId);\n                        continue;\n                    }\n                }\n                lastInfo = info;\n            }\n        }\n        const filteredRemoteAttendeesWithPreference = remoteInfosWithPreference.filter((info) => {\n            return !streamIdsToRemove.has(info.streamId);\n        });\n        this.logger.info(`Filtered to ${JSON.stringify(filteredRemoteAttendeesWithPreference)}`);\n        const rates = {\n            targetDownlinkBitrate: 0,\n            chosenTotalBitrate: 0,\n            deltaToNextUpgrade: 0,\n        };\n        rates.targetDownlinkBitrate = this.determineTargetRate();\n        const numberOfParticipants = this.subscribedReceiveSet.size();\n        const currentEstimated = this.downlinkStats.bandwidthEstimateKbps;\n        // Use videoPriorityBasedPolicyConfig to add additional delays based on network conditions\n        const dontAllowSubscribe = !this.videoPriorityBasedPolicyConfig.allowSubscribe(numberOfParticipants, currentEstimated);\n        // `priorityPolicy` assumes that we have sorted `filteredRemoteAttendeesWithPreference` in order of quality\n        if (this.probeFailed) {\n            // When probe failed, we set timeBeforeAllowSubscribeMs to 3x longer\n            // Since we have passed the subscribe interval now, we will try to probe again\n            this.probeFailed = false;\n            // For the same reason above, reset time before allow subscribe to default\n            this.timeBeforeAllowSubscribeMs = VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_SUBSCRIBE_MS;\n            if (noMajorChange && dontAllowSubscribe)\n                return;\n        }\n        const upgradeStream = this.priorityPolicy(rates, filteredRemoteAttendeesWithPreference, chosenStreams);\n        const skipProbe = !ServerSideNetworkAdaption_1.serverSideNetworkAdaptionIsNoneOrDefault(this.serverSideNetworkAdaption);\n        let subscriptionChoice = 0 /* NewOptimal */;\n        // Look for probing or override opportunities\n        if (!skipProbe && !this.startupPeriod && sameStreamChoices) {\n            if (this.rateProbeState === \"Probing\" /* Probing */) {\n                subscriptionChoice = this.handleProbe(chosenStreams, rates.targetDownlinkBitrate);\n            }\n            else if (rates.deltaToNextUpgrade !== 0) {\n                subscriptionChoice = this.maybeOverrideOrProbe(chosenStreams, rates, upgradeStream);\n            }\n        }\n        else {\n            // If there was a change in streams to choose from, then cancel any probing or upgrades\n            this.setProbeState(\"Not Probing\" /* NotProbing */);\n            this.lastUpgradeRateKbps = 0;\n        }\n        this.previousStreamsWithPreference = remoteInfosWithPreference;\n        this.videoPreferencesUpdated = false;\n        if (subscriptionChoice === 1 /* PreviousOptimal */) {\n            this.logger.info(`bwe: keepSameSubscriptions stats:${JSON.stringify(this.downlinkStats)}`);\n            this.prevTargetRateKbps = rates.targetDownlinkBitrate;\n            return;\n        }\n        if (subscriptionChoice === 2 /* PreProbe */) {\n            const subscribedRate = this.calculateSubscribeRate(this.preProbeNonPausedReceiveStreams);\n            this.optimalReceiveStreams = this.preProbeReceiveStreams.slice();\n            this.processBwPausedStreams(remoteInfosWithPreference, this.preProbeNonPausedReceiveStreams);\n            this.logger.info('bwe: Use Pre-Probe subscription subscribedRate:' + subscribedRate);\n            return;\n        }\n        this.optimalNonPausedReceiveStreams = chosenStreams.slice();\n        const lastNumberPaused = this.pausedBwAttendeeIds.size;\n        this.processBwPausedStreams(remoteInfosWithPreference, chosenStreams);\n        if (this.logger.getLogLevel() <= LogLevel_1.LogLevel.INFO &&\n            (this.logCount % 15 === 0 ||\n                this.rateProbeState !== lastProbeState ||\n                this.optimalReceiveStreams.length !== chosenStreams.length ||\n                lastNumberPaused !== this.pausedBwAttendeeIds.size)) {\n            this.logger.info(this.policyStateLogStr(remoteInfos, rates.targetDownlinkBitrate));\n            this.logCount = 0;\n        }\n        this.logCount++;\n        this.prevTargetRateKbps = rates.targetDownlinkBitrate;\n        this.optimalReceiveStreams = chosenStreams.slice();\n    }\n    calculateOptimalReceiveSet() {\n        const streamSelectionSet = new DefaultVideoStreamIdSet_1.default();\n        this.calculateOptimalReceiveStreams();\n        for (const stream of this.optimalReceiveStreams) {\n            streamSelectionSet.add(stream.streamId);\n        }\n        if (!this.optimalReceiveSet.equal(streamSelectionSet)) {\n            const subscribedRate = this.calculateSubscribeRate(this.optimalReceiveStreams);\n            this.logger.info(`bwe: new streamSelection: ${JSON.stringify(streamSelectionSet)} subscribedRate:${subscribedRate}`);\n        }\n        this.optimalReceiveSet = streamSelectionSet;\n    }\n    determineTargetRate() {\n        let targetBitrate = 0;\n        const now = Date.now();\n        // Startup phase handling.  During this period the estimate can be 0 or\n        // could still be slowly hunting for a steady state.  This startup ramp up\n        // can cause a series of subscribes which can be distracting. During this\n        // time just use our configured default value\n        if (this.downlinkStats.bandwidthEstimateKbps !== 0) {\n            if (this.firstEstimateTimestamp === 0) {\n                this.firstEstimateTimestamp = now;\n            }\n            // handle startup state where estimator is still converging.\n            if (this.startupPeriod) {\n                // Drop out of startup period if\n                // - estimate is above default\n                // - get packet loss and have a valid estimate\n                // - startup period has expired and rate is not still increasing\n                if (this.downlinkStats.bandwidthEstimateKbps >\n                    VideoPriorityBasedPolicy.DEFAULT_BANDWIDTH_KBPS ||\n                    this.downlinkStats.packetsLost > 0 ||\n                    (now - this.firstEstimateTimestamp > VideoPriorityBasedPolicy.STARTUP_PERIOD_MS &&\n                        this.downlinkStats.bandwidthEstimateKbps <=\n                            this.prevDownlinkStats.bandwidthEstimateKbps)) {\n                    this.startupPeriod = false;\n                    this.prevTargetRateKbps = this.downlinkStats.bandwidthEstimateKbps;\n                }\n            }\n            // If we are in the startup period and we haven't detected any packet loss, then\n            // keep it at the default to let the estimation get to a steady state\n            if (this.startupPeriod) {\n                targetBitrate = VideoPriorityBasedPolicy.DEFAULT_BANDWIDTH_KBPS;\n            }\n            else {\n                // We rely on our target bitrate being above what we are receiving to mark a probe as complete,\n                // however in browsers, the estimate can heavily lag behind the actual receive rate, especially when low.\n                //\n                // To mitigate this we override with the actual estimate plus some buffer if we aren't seeing packet loss.\n                if (this.rateProbeState === \"Probing\" /* Probing */ &&\n                    this.downlinkStats.usedBandwidthKbps > this.downlinkStats.bandwidthEstimateKbps &&\n                    this.downlinkStats.packetsLost < VideoPriorityBasedPolicy.SPURIOUS_PACKET_LOST_THRESHOLD) {\n                    this.logger.info(`bwe: In probe state, overriding estimate ${this.downlinkStats.bandwidthEstimateKbps} with actual receive bitrate ${this.downlinkStats.usedBandwidthKbps}`);\n                    targetBitrate =\n                        this.downlinkStats.usedBandwidthKbps +\n                            VideoPriorityBasedPolicy.USED_BANDWIDTH_OVERRIDE_BUFFER_KBPS;\n                }\n                else {\n                    targetBitrate = this.downlinkStats.bandwidthEstimateKbps;\n                }\n            }\n        }\n        else {\n            if (this.firstEstimateTimestamp === 0) {\n                targetBitrate = VideoPriorityBasedPolicy.DEFAULT_BANDWIDTH_KBPS;\n            }\n            else {\n                targetBitrate = this.prevTargetRateKbps;\n            }\n        }\n        // Estimated downlink rate can follow actual bandwidth or fall for a short period of time\n        // due to the absolute send time estimator incorrectly thinking that a delay in packets is\n        // a precursor to packet loss.  We have seen too many false positives on this, so we\n        // will ignore largish drops in the estimate if there is no packet loss\n        if (!this.startupPeriod &&\n            ((this.usingPrevTargetRate &&\n                this.downlinkStats.bandwidthEstimateKbps < this.prevTargetRateKbps) ||\n                this.downlinkStats.bandwidthEstimateKbps <\n                    (this.prevTargetRateKbps *\n                        (100 - VideoPriorityBasedPolicy.LARGE_RATE_CHANGE_TRIGGER_PERCENT)) /\n                        100 ||\n                this.downlinkStats.bandwidthEstimateKbps <\n                    (this.downlinkStats.usedBandwidthKbps *\n                        VideoPriorityBasedPolicy.LARGE_RATE_CHANGE_TRIGGER_PERCENT) /\n                        100) &&\n            this.downlinkStats.packetsLost === 0) {\n            // Set target to be the same as last\n            this.logger.debug(() => {\n                return 'bwe: ValidateRate: Using Previous rate ' + this.prevTargetRateKbps;\n            });\n            this.usingPrevTargetRate = true;\n            targetBitrate = this.prevTargetRateKbps;\n        }\n        else {\n            this.usingPrevTargetRate = false;\n        }\n        // Cap total receive bitrate at 15000 kbps\n        if (targetBitrate > 15000) {\n            this.logger.warn('bwe: ' +\n                targetBitrate +\n                ' exceeds maximum limit (15000). Limit TargetDisplaySize with VideoPreferences to avoid this.');\n        }\n        return Math.min(targetBitrate, 15000);\n    }\n    setProbeState(newState) {\n        if (this.rateProbeState === newState) {\n            return false;\n        }\n        const now = Date.now();\n        switch (newState) {\n            case \"Not Probing\" /* NotProbing */:\n                this.probePendingStartTimestamp = 0;\n                break;\n            case \"Probe Pending\" /* ProbePending */:\n                if (this.lastProbeTimestamp === 0 ||\n                    now - this.lastProbeTimestamp > VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_PROBE_MS) {\n                    this.probePendingStartTimestamp = now;\n                }\n                else {\n                    // Too soon to do a probe again\n                    return false;\n                }\n                break;\n            case \"Probing\" /* Probing */:\n                if (now - this.probePendingStartTimestamp > this.timeBeforeAllowProbeMs) {\n                    this.lastProbeTimestamp = now;\n                    this.preProbeReceiveStreams = this.subscribedReceiveStreams.slice();\n                    this.preProbeNonPausedReceiveStreams = this.optimalNonPausedReceiveStreams;\n                    // Increase the time allowed until the next probe\n                    this.timeBeforeAllowProbeMs = Math.min(this.timeBeforeAllowProbeMs * 2, VideoPriorityBasedPolicy.MAX_HOLD_BEFORE_PROBE_MS);\n                }\n                else {\n                    // Too soon to do probe\n                    return false;\n                }\n                break;\n        }\n        this.logger.info('bwe: setProbeState to ' + newState + ' from ' + this.rateProbeState);\n        this.rateProbeState = newState;\n        return true;\n    }\n    // Upgrade the stream id from the appropriate group or add it if it wasn't already in the list.\n    // Return the added amount of bandwidth\n    upgradeToStream(chosenStreams, upgradeStream) {\n        for (let i = 0; i < chosenStreams.length; i++) {\n            if (chosenStreams[i].groupId === upgradeStream.groupId) {\n                const diffRate = upgradeStream.avgBitrateKbps - chosenStreams[i].avgBitrateKbps;\n                this.logger.info('bwe: upgradeStream from ' + JSON.stringify(chosenStreams[i]) + ' to ' + upgradeStream);\n                this.lastUpgradeRateKbps = diffRate;\n                chosenStreams[i] = upgradeStream;\n                return diffRate;\n            }\n        }\n        // We are adding a stream and not upgrading.\n        chosenStreams.push(upgradeStream);\n        this.lastUpgradeRateKbps = upgradeStream.avgBitrateKbps;\n        return this.lastUpgradeRateKbps;\n    }\n    // Do specific behavior while we are currently in probing state and metrics\n    // indicate environment is still valid to do probing.\n    // Return true if the caller should not change from the previous subscriptions.\n    handleProbe(chosenStreams, targetDownlinkBitrate) {\n        // Don't allow probe to happen indefinitely\n        if (Date.now() - this.lastProbeTimestamp > VideoPriorityBasedPolicy.MAX_ALLOWED_PROBE_TIME_MS) {\n            this.logger.info(`bwe: Canceling probe due to timeout`);\n            this.setProbeState(\"Not Probing\" /* NotProbing */);\n            return 0 /* NewOptimal */;\n        }\n        if (this.downlinkStats.packetsLost > 0) {\n            this.logger.info(`bwe: Probe encountering packets lost:${this.downlinkStats.packetsLost}`);\n            // See comment above `VideoPriorityBasedPolicy.SPURIOUS_PACKET_LOST_THRESHOLD`\n            if (this.downlinkStats.packetsLost > VideoPriorityBasedPolicy.SPURIOUS_PACKET_LOST_THRESHOLD) {\n                this.setProbeState(\"Not Probing\" /* NotProbing */);\n                this.logger.info(`bwe: Canceling probe due to packets lost:${this.downlinkStats.packetsLost}`);\n                this.probeFailed = true;\n                this.timeBeforeAllowSubscribeMs =\n                    Math.max(VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_SUBSCRIBE_MS, this.timeBeforeAllowSubscribeMs) * 3;\n                // packet lost indicates bad network and thus slowing down subscribing by extend delay by 3 times\n                return 2 /* PreProbe */;\n            }\n        }\n        const subscribedRate = this.calculateSubscribeRate(this.optimalReceiveStreams);\n        if (this.chosenStreamsSameAsLast(chosenStreams) || targetDownlinkBitrate > subscribedRate) {\n            this.logger.info(`bwe: Probe successful`);\n            // If target bitrate can sustain probe rate, then probe was successful.\n            this.setProbeState(\"Not Probing\" /* NotProbing */);\n            // Reset the time allowed between probes since this was successful\n            this.timeBeforeAllowProbeMs = VideoPriorityBasedPolicy.MIN_TIME_BETWEEN_PROBE_MS;\n            return 0 /* NewOptimal */;\n        }\n        return 1 /* PreviousOptimal */;\n    }\n    maybeOverrideOrProbe(chosenStreams, rates, upgradeStream) {\n        const sameSubscriptions = this.chosenStreamsSameAsLast(chosenStreams);\n        let useLastSubscriptions = 0 /* NewOptimal */;\n        // We want to minimize thrashing between between low res and high res of different\n        // participants due to avg bitrate fluctuations. If there hasn't been much of a change in estimated bandwidth\n        // and the number of streams and their max rates are the same, then reuse the previous subscription\n        const triggerPercent = rates.targetDownlinkBitrate > VideoPriorityBasedPolicy.LOW_BITRATE_THRESHOLD_KBPS\n            ? VideoPriorityBasedPolicy.TARGET_RATE_CHANGE_TRIGGER_PERCENT\n            : VideoPriorityBasedPolicy.TARGET_RATE_CHANGE_TRIGGER_PERCENT * 2;\n        const minTargetBitrateDelta = (rates.targetDownlinkBitrate * triggerPercent) / 100;\n        this.targetRateBaselineForDeltaCheckKbps =\n            this.targetRateBaselineForDeltaCheckKbps !== undefined\n                ? this.targetRateBaselineForDeltaCheckKbps\n                : this.prevTargetRateKbps;\n        if (!sameSubscriptions &&\n            Math.abs(rates.targetDownlinkBitrate - this.targetRateBaselineForDeltaCheckKbps) <\n                minTargetBitrateDelta) {\n            this.logger.info('bwe: MaybeOverrideOrProbe: Reuse last decision based on delta rate. {' +\n                JSON.stringify(this.subscribedReceiveSet) +\n                `}`);\n            useLastSubscriptions = 1 /* PreviousOptimal */;\n        }\n        else {\n            this.targetRateBaselineForDeltaCheckKbps = rates.targetDownlinkBitrate;\n        }\n        // If there has been packet loss, then reset to no probing state\n        if (this.downlinkStats.packetsLost > this.prevDownlinkStats.packetsLost) {\n            this.setProbeState(\"Not Probing\" /* NotProbing */);\n            this.lastUpgradeRateKbps = 0;\n            return useLastSubscriptions;\n        }\n        if (sameSubscriptions || useLastSubscriptions === 1 /* PreviousOptimal */) {\n            // If planned subscriptions are same as last, then either move to probe pending state\n            // or move to probing state if enough time has passed.\n            switch (this.rateProbeState) {\n                case \"Not Probing\" /* NotProbing */:\n                    this.setProbeState(\"Probe Pending\" /* ProbePending */);\n                    break;\n                case \"Probe Pending\" /* ProbePending */:\n                    if (this.setProbeState(\"Probing\" /* Probing */)) {\n                        this.upgradeToStream(chosenStreams, upgradeStream);\n                        useLastSubscriptions = 0 /* NewOptimal */;\n                    }\n                    break;\n            }\n        }\n        else {\n            this.setProbeState(\"Not Probing\" /* NotProbing */);\n        }\n        return useLastSubscriptions;\n    }\n    // Utility function to find max rate of streams in current decision\n    calculateSubscribeRate(streams) {\n        let subscribeRate = 0;\n        for (const stream of streams) {\n            if (!this.pausedStreamIds.contain(stream.streamId) &&\n                !this.pausedBwAttendeeIds.has(stream.attendeeId)) {\n                subscribeRate += stream.maxBitrateKbps;\n            }\n        }\n        return subscribeRate;\n    }\n    handleAppPausedStreams(chosenStreams, remoteInfos) {\n        if (!this.tileController) {\n            this.logger.warn('tileController not found!');\n            return;\n        }\n        this.pausedStreamIds = new DefaultVideoStreamIdSet_1.default();\n        const remoteTiles = this.tileController.getAllRemoteVideoTiles();\n        for (const tile of remoteTiles) {\n            const state = tile.state();\n            if (state.paused && !this.pausedBwAttendeeIds.has(state.boundAttendeeId)) {\n                let j = remoteInfos.length;\n                while (j--) {\n                    if (remoteInfos[j].attendeeId === state.boundAttendeeId) {\n                        this.logger.info('bwe: removed paused attendee ' +\n                            state.boundAttendeeId +\n                            ' streamId: ' +\n                            remoteInfos[j].streamId);\n                        this.pausedStreamIds.add(remoteInfos[j].streamId);\n                        // Add the stream to the selection set to keep the tile around\n                        if (this.subscribedReceiveSet.contain(remoteInfos[j].streamId)) {\n                            chosenStreams.push(remoteInfos[j]);\n                        }\n                        remoteInfos.splice(j, 1);\n                    }\n                }\n            }\n        }\n    }\n    processBwPausedStreams(remoteInfos, chosenStreams) {\n        if (!this.tileController) {\n            this.logger.warn('tileController not found!');\n            return;\n        }\n        const preferences = this.getCurrentVideoPreferences();\n        if (preferences && this.shouldPauseTiles) {\n            const videoTiles = this.tileController.getAllVideoTiles();\n            for (const preference of preferences) {\n                const videoTile = this.getVideoTileForAttendeeId(preference.attendeeId, videoTiles);\n                const paused = (videoTile === null || videoTile === void 0 ? void 0 : videoTile.state().paused) || false;\n                if (!chosenStreams.some(stream => stream.attendeeId === preference.attendeeId)) {\n                    // We cannot rely on the existance of video tile to indicate that the source exists in the call\n                    // because tiles will not be added or removed until after a full renegotiation (i.e. it will\n                    // be behind the state provided by the index)\n                    const streamExists = remoteInfos.some(stream => stream.attendeeId === preference.attendeeId);\n                    if (videoTile && streamExists) {\n                        const info = this.optimalReceiveStreams.find(stream => stream.attendeeId === preference.attendeeId);\n                        if (info !== undefined) {\n                            if (!paused) {\n                                this.logger.info(`bwe: pausing streamId ${info.streamId} attendee ${preference.attendeeId} due to bandwidth`);\n                                this.forEachObserver(observer => {\n                                    observer.tileWillBePausedByDownlinkPolicy(videoTile.id());\n                                });\n                                this.tileController.pauseVideoTile(videoTile.id());\n                            }\n                            chosenStreams.push(info);\n                        }\n                        this.pausedBwAttendeeIds.add(preference.attendeeId);\n                    }\n                    else if (streamExists) {\n                        // Create a tile for this participant if one doesn't already exist and mark it as paused\n                        // Don't include it in the chosen streams because we don't want to subscribe for it then have to pause it.\n                        const newTile = this.tileController.addVideoTile();\n                        newTile.bindVideoStream(preference.attendeeId, false, null, 0, 0, 0, null);\n                        this.forEachObserver(observer => {\n                            observer.tileWillBePausedByDownlinkPolicy(newTile.id());\n                        });\n                        newTile.pause();\n                        this.logger.info(`bwe: Created video tile ${newTile.id()} for bw paused attendee ${preference.attendeeId}`);\n                        this.pausedBwAttendeeIds.add(preference.attendeeId);\n                    }\n                }\n                else if (paused && this.pausedBwAttendeeIds.has(preference.attendeeId)) {\n                    this.logger.info(`bwe: unpausing attendee ${preference.attendeeId} due to bandwidth`);\n                    this.forEachObserver(observer => {\n                        observer.tileWillBeUnpausedByDownlinkPolicy(videoTile.id());\n                    });\n                    this.tileController.unpauseVideoTile(videoTile.id());\n                    this.pausedBwAttendeeIds.delete(preference.attendeeId);\n                }\n            }\n        }\n    }\n    cleanBwPausedTiles(remoteInfos) {\n        if (!this.tileController) {\n            this.logger.warn('tileController not found!');\n            return;\n        }\n        const tiles = this.tileController.getAllRemoteVideoTiles();\n        const preferences = this.getCurrentVideoPreferences();\n        for (const tile of tiles) {\n            const state = tile.state();\n            if (!state.boundVideoStream) {\n                if (!remoteInfos.some(stream => stream.attendeeId === state.boundAttendeeId)) {\n                    this.tileController.removeVideoTile(state.tileId);\n                    this.logger.info(`bwe: Removed video tile ${state.tileId} for bw paused attendee ${state.boundAttendeeId}`);\n                }\n                else if (preferences !== undefined &&\n                    !preferences.some(pref => pref.attendeeId === state.boundAttendeeId)) {\n                    this.tileController.removeVideoTile(state.tileId);\n                }\n            }\n        }\n    }\n    // `remoteInfos` is assumed to be provided in order of increasing quality\n    priorityPolicy(rates, remoteInfos, chosenStreams) {\n        let upgradeStream;\n        const videoPreferences = this.getCurrentVideoPreferences();\n        const highestPriority = videoPreferences.highestPriority();\n        let nextPriority;\n        let priority = highestPriority;\n        while (priority !== -1) {\n            nextPriority = -1;\n            for (const preference of videoPreferences) {\n                if (preference.priority === priority) {\n                    // First subscribe to at least low rate\n                    for (const info of remoteInfos) {\n                        if (info.attendeeId === preference.attendeeId) {\n                            if (!chosenStreams.some(stream => stream.groupId === info.groupId)) {\n                                if (rates.chosenTotalBitrate + info.avgBitrateKbps <= rates.targetDownlinkBitrate) {\n                                    chosenStreams.push(info);\n                                    rates.chosenTotalBitrate += info.avgBitrateKbps;\n                                }\n                                else if (rates.deltaToNextUpgrade === 0) {\n                                    // Keep track of step to next upgrade\n                                    rates.deltaToNextUpgrade = info.avgBitrateKbps;\n                                    upgradeStream = info;\n                                }\n                            }\n                        }\n                    }\n                }\n                else {\n                    if (preference.priority > priority) {\n                        nextPriority = preference.priority;\n                        break;\n                    }\n                }\n            }\n            // Now try to upgrade all attendee's with this priority\n            for (const preference of videoPreferences) {\n                if (preference.priority === priority) {\n                    for (const info of remoteInfos) {\n                        if (info.attendeeId === preference.attendeeId) {\n                            const index = chosenStreams.findIndex(stream => stream.groupId === info.groupId &&\n                                stream.maxBitrateKbps <= info.maxBitrateKbps &&\n                                stream.avgBitrateKbps < info.avgBitrateKbps);\n                            if (index !== -1) {\n                                const increaseKbps = info.avgBitrateKbps - chosenStreams[index].avgBitrateKbps;\n                                if (this.hasSimulcastStreams(remoteInfos, info.attendeeId, info.groupId) &&\n                                    this.canUpgrade(info.avgBitrateKbps, preference.targetSize, preference.targetSizeToBitrateKbps(preference.targetSize), info.attendeeId.endsWith(ContentShareConstants_1.default.Modality))) {\n                                    this.logger.info(`bwe: attendee: ${info.attendeeId} group: ${info.groupId} has simulcast and can upgrade avgBitrate: ${info.avgBitrateKbps} target: ${preference.targetSizeToBitrateKbps(preference.targetSize)} targetTotalBitrate: ${rates.targetDownlinkBitrate}`);\n                                    if (rates.chosenTotalBitrate + increaseKbps <= rates.targetDownlinkBitrate) {\n                                        rates.chosenTotalBitrate += increaseKbps;\n                                        chosenStreams[index] = info;\n                                    }\n                                    else if (rates.deltaToNextUpgrade === 0) {\n                                        // Keep track of step to next upgrade\n                                        rates.deltaToNextUpgrade = increaseKbps;\n                                        upgradeStream = info;\n                                    }\n                                }\n                                else {\n                                    this.logger.info('bwe: cannot upgrade stream quality beyond target size');\n                                }\n                            }\n                        }\n                    }\n                }\n                else {\n                    if (preference.priority > priority) {\n                        break;\n                    }\n                }\n            }\n            // If we haven't subscribed to the highest rate of the top priority videos then\n            // do not subscribe to any other sources\n            if (priority === highestPriority && rates.deltaToNextUpgrade !== 0) {\n                break;\n            }\n            priority = nextPriority;\n        }\n        return upgradeStream;\n    }\n    getVideoTileForAttendeeId(attendeeId, videoTiles) {\n        for (const tile of videoTiles) {\n            const state = tile.state();\n            if (state.boundAttendeeId === attendeeId) {\n                return tile;\n            }\n        }\n        return null;\n    }\n    canUpgrade(bitrateKbps, targetResolution, targetBitrateKbps, isContent) {\n        let canUpgrade = false;\n        // For both video and content, we want to interpret `TargetDisplaySize.High` as a request for the best quality\n        // video, and should ignore the value of the target bitrate.\n        if (targetResolution === TargetDisplaySize_1.default.High) {\n            // For both video and content, we want to interpret `TargetDisplaySize.High` as a request for the best quality\n            // video, and should ignore the value of the target bitrate.\n            canUpgrade = true;\n        }\n        else if (!isContent && bitrateKbps <= targetBitrateKbps) {\n            // For content share, even if the higher quality stream has a high max bitrate of 1200 kbps for example\n            // the avg bitrate can be way lower so have to make sure that we do not update to a higher bitrate than the\n            // target value (i.e. if `targetResolution === TargetDisplaySize.Low`).\n            //\n            // This does not apply to video as video uplink bandwidth could change the max bitrate value without resubscribing\n            // so the max bitrate value might not be up-to-date on the downlink side. Also in the case of video, the avg\n            // bitrate is close to the actual max bitrate.\n            canUpgrade = true;\n        }\n        else if (isContent &&\n            targetResolution === TargetDisplaySize_1.default.Medium &&\n            bitrateKbps <= targetBitrateKbps) {\n            // If the target resolution is medium then fall back to use avg bitrate as video.\n            canUpgrade = true;\n        }\n        if (canUpgrade) {\n            this.logger.info(`bwe: canUpgrade: bitrateKbps: ${bitrateKbps} targetBitrateKbps: ${targetBitrateKbps}`);\n            return true;\n        }\n        this.logger.info(`bwe: cannot Upgrade: bitrateKbps: ${bitrateKbps} targetBitrateKbps: ${targetBitrateKbps}`);\n        return false;\n    }\n    hasSimulcastStreams(remoteInfos, attendeeId, groupId) {\n        let streamCount = 0;\n        for (const info of remoteInfos) {\n            if (info.attendeeId === attendeeId && info.groupId === groupId) {\n                streamCount++;\n            }\n        }\n        this.logger.info(`bwe: attendeeId: ${attendeeId} groupId: ${groupId} hasSimulcastStreams: streamCount: ${streamCount}`);\n        return streamCount > 1;\n    }\n    streamsWithPreferenceDidChange(remoteInfos) {\n        if (this.previousStreamsWithPreference === undefined ||\n            remoteInfos.length !== this.previousStreamsWithPreference.length ||\n            this.videoPreferencesUpdated === true) {\n            return true;\n        }\n        for (const info of remoteInfos) {\n            const infoMatch = this.previousStreamsWithPreference.find(prevInfo => prevInfo.groupId === info.groupId &&\n                prevInfo.streamId === info.streamId &&\n                prevInfo.maxBitrateKbps === info.maxBitrateKbps);\n            if (infoMatch === undefined) {\n                return true;\n            }\n        }\n        return false;\n    }\n    chosenStreamsSameAsLast(chosenStreams) {\n        if (this.optimalNonPausedReceiveStreams.length !== chosenStreams.length) {\n            return false;\n        }\n        for (const lastStream of this.optimalNonPausedReceiveStreams) {\n            if (!chosenStreams.some(stream => stream.streamId === lastStream.streamId)) {\n                return false;\n            }\n        }\n        return true;\n    }\n    policyStateLogStr(remoteInfos, targetDownlinkBitrate) {\n        const subscribedRate = this.calculateSubscribeRate(this.optimalReceiveStreams);\n        const optimalReceiveSet = {\n            targetBitrate: targetDownlinkBitrate,\n            subscribedRate: subscribedRate,\n            probeState: this.rateProbeState,\n            startupPeriod: this.startupPeriod,\n        };\n        // Reduced remote info logging:\n        let remoteInfoStr = `remoteInfos: [`;\n        for (const info of remoteInfos) {\n            remoteInfoStr += `{grpId:${info.groupId} strId:${info.streamId} maxBr:${info.maxBitrateKbps} avgBr:${info.avgBitrateKbps}}, `;\n        }\n        remoteInfoStr += `]`;\n        let logString = `bwe: optimalReceiveSet ${JSON.stringify(optimalReceiveSet)}\\n` +\n            `bwe:   prev ${JSON.stringify(this.prevDownlinkStats)}\\n` +\n            `bwe:   now  ${JSON.stringify(this.downlinkStats)}\\n` +\n            `bwe:   ${remoteInfoStr}\\n`;\n        if (this.pausedStreamIds.size() > 0 || this.pausedBwAttendeeIds.size > 0) {\n            logString += `bwe:   paused: app stream ids ${JSON.stringify(this.pausedStreamIds)}  bw attendees { ${Array.from(this.pausedBwAttendeeIds).join(' ')} }\\n`;\n        }\n        if (this.videoPreferences) {\n            logString += `bwe:   preferences: ${JSON.stringify(this.videoPreferences)}`;\n        }\n        else {\n            logString += `bwe:   default preferences: ${JSON.stringify(this.defaultVideoPreferences)}`;\n        }\n        return logString;\n    }\n    getCurrentVideoPreferences() {\n        return this.videoPreferences || this.defaultVideoPreferences;\n    }\n    getServerSideNetworkAdaption() {\n        return this.serverSideNetworkAdaption;\n    }\n    setServerSideNetworkAdaption(adaption) {\n        this.serverSideNetworkAdaption = adaption;\n        this.setProbeState(\"Not Probing\" /* NotProbing */); // In case we were probing\n    }\n    supportedServerSideNetworkAdaptions() {\n        return [\n            ServerSideNetworkAdaption_1.default.None,\n            ServerSideNetworkAdaption_1.default.BandwidthProbing,\n            ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption,\n        ];\n    }\n    getVideoPreferences() {\n        let preferences = this.getCurrentVideoPreferences();\n        if (!preferences) {\n            const dummyPreferences = VideoPreferences_1.VideoPreferences.prepare();\n            // Can't be undefined, occasionally the audio video controller\n            // will call this function before the first index is received\n            preferences = dummyPreferences.build();\n        }\n        return preferences;\n    }\n    wantsAllTemporalLayersInIndex() {\n        return true;\n    }\n}\nexports.default = VideoPriorityBasedPolicy;\nVideoPriorityBasedPolicy.DEFAULT_BANDWIDTH_KBPS = 2800;\nVideoPriorityBasedPolicy.STARTUP_PERIOD_MS = 6000;\nVideoPriorityBasedPolicy.LARGE_RATE_CHANGE_TRIGGER_PERCENT = 20;\nVideoPriorityBasedPolicy.TARGET_RATE_CHANGE_TRIGGER_PERCENT = 15;\nVideoPriorityBasedPolicy.LOW_BITRATE_THRESHOLD_KBPS = 300;\nVideoPriorityBasedPolicy.MIN_TIME_BETWEEN_PROBE_MS = 5000;\nVideoPriorityBasedPolicy.MIN_TIME_BETWEEN_SUBSCRIBE_MS = 2000;\n// We apply exponentional backoff to probe attempts if they do not\n// succeed, so we need to set a reasonable maximum.\nVideoPriorityBasedPolicy.MAX_HOLD_BEFORE_PROBE_MS = 30000;\nVideoPriorityBasedPolicy.MAX_ALLOWED_PROBE_TIME_MS = 60000;\n// Occasionally we see that on unpause or upgrade we see a single packet lost\n// or two, even in completely unconstrained scenarios. We should look into\n// why this occurs on the backend, but for now we require a non-trivial\n// amount of packets lost to fail the probe. These could also be from\n// other senders given we don't yet use TWCC.\nVideoPriorityBasedPolicy.SPURIOUS_PACKET_LOST_THRESHOLD = 2;\n// See usage\nVideoPriorityBasedPolicy.USED_BANDWIDTH_OVERRIDE_BUFFER_KBPS = 100;\n//# sourceMappingURL=VideoPriorityBasedPolicy.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst ServerSideNetworkAdaption_1 = __importDefault(require(\"../signalingclient/ServerSideNetworkAdaption\"));\n/**\n * [[VideoPriorityBasedPolicyConfig]] contains the network issue response delay and network issue recovery delay.\n */\nclass VideoPriorityBasedPolicyConfig {\n    /** Initializes a [[VideoPriorityBasedPolicyConfig]] with the network event delays.\n     *\n     * @deprecated These values are not used when server side network adaptation is used, which will\n     * become the default in the future. Therefore these constructors\n     * will be deprecated and removed in a later releases.\n     *\n     * @param networkIssueResponseDelayFactor Delays before reducing subscribed video bitrate. Input should be a value between 0 and 1.\n     * @param networkIssueRecoveryDelayFactor Delays before starting to increase bitrates after a network event and\n     * delays between increasing video bitrates on each individual stream. Input should be a value between 0 and 1.\n     */\n    constructor(networkIssueResponseDelayFactor = 0, networkIssueRecoveryDelayFactor = 0) {\n        this.networkIssueResponseDelayFactor = networkIssueResponseDelayFactor;\n        this.networkIssueRecoveryDelayFactor = networkIssueRecoveryDelayFactor;\n        this.currentNetworkEvent = 0 /* Stable */;\n        this.bandwidthDecreaseTimestamp = 0; // the last time bandwidth decreases\n        this.referenceBitrate = 0;\n        /**\n         * Additional server side features to be enabled for network adaption. The only currently\n         * recommended value is `ServerSideNetworkAdaption.BandwidthProbingAndRemoteVideoQualityAdaption`\n         */\n        this.serverSideNetworkAdaption = ServerSideNetworkAdaption_1.default.BandwidthProbingAndRemoteVideoQualityAdaption;\n        if (networkIssueResponseDelayFactor < 0) {\n            networkIssueResponseDelayFactor = 0;\n        }\n        else if (networkIssueResponseDelayFactor > 1) {\n            networkIssueResponseDelayFactor = 1;\n        }\n        this.networkIssueResponseDelayFactor = networkIssueResponseDelayFactor;\n        if (networkIssueRecoveryDelayFactor < 0) {\n            networkIssueRecoveryDelayFactor = 0;\n        }\n        else if (networkIssueRecoveryDelayFactor > 1) {\n            networkIssueRecoveryDelayFactor = 1;\n        }\n        this.networkIssueRecoveryDelayFactor = networkIssueRecoveryDelayFactor;\n    }\n    // determine if subscribe is allowed based on network issue/recovery delays\n    allowSubscribe(numberOfParticipants, currentEstimated) {\n        let timeBeforeAllowSubscribeMs = 0;\n        const previousNetworkEvent = this.currentNetworkEvent;\n        if (currentEstimated > this.referenceBitrate) {\n            // if bw increases\n            this.currentNetworkEvent = 2 /* Increase */;\n            this.referenceBitrate = currentEstimated;\n            return true;\n        }\n        else if (currentEstimated < this.referenceBitrate) {\n            // if bw decreases, we use response delay\n            this.currentNetworkEvent = 1 /* Decrease */;\n            timeBeforeAllowSubscribeMs = this.getSubscribeDelay(this.currentNetworkEvent, numberOfParticipants);\n            if (previousNetworkEvent !== 1 /* Decrease */) {\n                this.bandwidthDecreaseTimestamp = Date.now();\n            }\n            else if (Date.now() - this.bandwidthDecreaseTimestamp > timeBeforeAllowSubscribeMs) {\n                this.referenceBitrate = currentEstimated;\n                return true;\n            }\n            return false;\n        }\n        else {\n            this.currentNetworkEvent = 0 /* Stable */;\n            return false;\n        }\n    }\n    // convert network event delay factor to actual delay in ms\n    getSubscribeDelay(event, numberOfParticipants) {\n        // left and right boundary of the delay\n        let subscribeDelay = VideoPriorityBasedPolicyConfig.MINIMUM_DELAY_MS;\n        const range = VideoPriorityBasedPolicyConfig.MAXIMUM_DELAY_MS -\n            VideoPriorityBasedPolicyConfig.MINIMUM_DELAY_MS;\n        const responseFactor = this.networkIssueResponseDelayFactor;\n        switch (event) {\n            case 1 /* Decrease */:\n                // we include number of participants here since bigger size of the meeting will generate higher bitrate\n                subscribeDelay += range * responseFactor * (1 + numberOfParticipants / 10);\n                subscribeDelay = Math.min(VideoPriorityBasedPolicyConfig.MAXIMUM_DELAY_MS, subscribeDelay);\n                break;\n        }\n        return subscribeDelay;\n    }\n}\nexports.default = VideoPriorityBasedPolicyConfig;\nVideoPriorityBasedPolicyConfig.MINIMUM_DELAY_MS = 2000;\nVideoPriorityBasedPolicyConfig.MAXIMUM_DELAY_MS = 8000;\nVideoPriorityBasedPolicyConfig.Default = new VideoPriorityBasedPolicyConfig(0, 0);\nVideoPriorityBasedPolicyConfig.UnstableNetworkPreset = new VideoPriorityBasedPolicyConfig(0, 1);\nVideoPriorityBasedPolicyConfig.StableNetworkPreset = new VideoPriorityBasedPolicyConfig(1, 0);\n//# sourceMappingURL=VideoPriorityBasedPolicyConfig.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.VideoQualityAdaptationPreference = void 0;\n/**\n * [[VideoQualityAdaptationPreference]] represents options in how to degrade/reduce quality when end-users\n * encounter network constraints that limit the bandwidth available to the SDK. Different resolutions and\n * framerates have different but sometimes similar bitrates (e.g. a 480p/30fps stream may have similar bitrate\n * to 720p/15fps), so the prefence selected will impact the visual experience of end-users on impairment and recovery,\n * as not all resolution/framerate combinations will be used.\n *\n * The SDK will follow the same path of resolution/framerate choices in recovery as it will during constraint.\n *\n * This preference will have no effect unless the sender is encoding in a way such that there\n * are both variable framerate and resolution options available. See the\n * [Priority based downlink policy guide](https://aws.github.io/amazon-chime-sdk-js/modules/prioritybased_downlink_policy.html)\n * for more detailed information and diagrams.\n */\nvar VideoQualityAdaptationPreference;\n(function (VideoQualityAdaptationPreference) {\n    /**\n     * Compromises between reducing resolution and framerate, with a slight preference towards framerate\n     * as framerate changes may be possible without a keyframe request to the sender. This value\n     * should be used in most use cases.\n     */\n    VideoQualityAdaptationPreference[VideoQualityAdaptationPreference[\"Balanced\"] = 0] = \"Balanced\";\n    /**\n     * Give preference to framerate under network constraint. This can be used, e.g.\n     * if the receiver is known to be displaying the video at a low resolution.\n     */\n    VideoQualityAdaptationPreference[VideoQualityAdaptationPreference[\"MaintainFramerate\"] = 1] = \"MaintainFramerate\";\n    /**\n     * Give preference to resolution under network constraint. This can be used, e.g.\n     * for screen share, or camera feed from a conference room or class room, that\n     * is displayed on a large screen\n     */\n    VideoQualityAdaptationPreference[VideoQualityAdaptationPreference[\"MaintainResolution\"] = 2] = \"MaintainResolution\";\n})(VideoQualityAdaptationPreference = exports.VideoQualityAdaptationPreference || (exports.VideoQualityAdaptationPreference = {}));\nexports.default = VideoQualityAdaptationPreference;\n//# sourceMappingURL=VideoQualityAdaptationPreference.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass NoOpVideoElementFactory {\n    create() {\n        const element = {\n            clientWidth: 400,\n            clientHeight: 300,\n            width: 400,\n            height: 300,\n            videoWidth: 400,\n            videoHeight: 300,\n            style: {\n                transform: '',\n            },\n            hasAttribute: () => {\n                return false;\n            },\n            removeAttribute: () => { },\n            setAttribute: () => { },\n            srcObject: false,\n            paused: true,\n            pause: () => {\n                element.paused = true;\n            },\n            play: () => {\n                element.paused = false;\n                return Promise.resolve();\n            },\n        };\n        // @ts-ignore\n        return element;\n    }\n}\nexports.default = NoOpVideoElementFactory;\n//# sourceMappingURL=NoOpVideoElementFactory.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\n/**\n * [[CanvasVideoFrameBuffer]] implements [[VideoFrameBuffer]]. It internally holds an `HTMLCanvasElement`.\n */\nclass CanvasVideoFrameBuffer {\n    constructor(canvas) {\n        this.canvas = canvas;\n        this.destroyed = false;\n    }\n    destroy() {\n        this.canvas = null;\n        this.destroyed = true;\n    }\n    asCanvasImageSource() {\n        return __awaiter(this, void 0, void 0, function* () {\n            if (this.destroyed) {\n                return Promise.reject('canvas buffer is destroyed');\n            }\n            return Promise.resolve(this.canvas);\n        });\n    }\n    asCanvasElement() {\n        return this.canvas;\n    }\n}\nexports.default = CanvasVideoFrameBuffer;\n//# sourceMappingURL=CanvasVideoFrameBuffer.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst CanvasVideoFrameBuffer_1 = __importDefault(require(\"./CanvasVideoFrameBuffer\"));\nconst DefaultVideoFrameProcessorTimer_1 = __importDefault(require(\"./DefaultVideoFrameProcessorTimer\"));\nconst DEFAULT_FRAMERATE = 15;\n/**\n * [[DefaultVideoFrameProcessorPipeline]] implements {@link VideoFrameProcessorPipeline}.\n * It constructs a buffer {@link CanvasVideoFrameBuffer} as source by default and invokes processor based on `framerate`.\n * The default output type is `MediaStream`.\n */\nclass DefaultVideoFrameProcessorPipeline {\n    constructor(logger, stages, timer = new DefaultVideoFrameProcessorTimer_1.default()) {\n        this.logger = logger;\n        this.stages = stages;\n        this.fr = DEFAULT_FRAMERATE;\n        // initialize with dummy inactive MediaStream to keep strict type\n        this.outputMediaStream = new MediaStream();\n        this.videoInput = document.createElement('video');\n        this.canvasOutput = document.createElement('canvas');\n        this.outputCtx = this.canvasOutput.getContext('2d');\n        this.canvasInput = document.createElement('canvas');\n        this.inputCtx = this.canvasInput.getContext('2d');\n        this.inputVideoStream = null;\n        this.sourceBuffers = [];\n        this.destBuffers = [];\n        this.observers = new Set();\n        this.hasStarted = false;\n        this.process = (_event) => __awaiter(this, void 0, void 0, function* () {\n            if (!this.inputVideoStream) {\n                return;\n            }\n            const processVideoStart = performance.now();\n            // videoWidth is intrinsic video width\n            if (this.videoInput.videoWidth) {\n                if (this.canvasInput.width !== this.videoInput.videoWidth) {\n                    this.canvasInput.width = this.videoInput.videoWidth;\n                    this.canvasInput.height = this.videoInput.videoHeight;\n                    this.sourceBuffers[0].height = this.canvasInput.height;\n                    this.sourceBuffers[0].width = this.canvasInput.width;\n                    this.sourceBuffers[0].framerate = this.framerate;\n                }\n                this.inputCtx.drawImage(this.videoInput, 0, 0);\n            }\n            // processes input buffers\n            let buffers = [];\n            buffers.push(this.sourceBuffers[0]);\n            try {\n                for (const proc of this.processors) {\n                    buffers = yield proc.process(buffers);\n                }\n            }\n            catch (_error) {\n                this.forEachObserver(obs => {\n                    if (obs.processingDidFailToStart) {\n                        obs.processingDidFailToStart();\n                    }\n                });\n                return;\n            }\n            this.destBuffers = buffers;\n            let imageSource;\n            try {\n                imageSource = yield this.destBuffers[0].asCanvasImageSource();\n            }\n            catch (error) {\n                /* istanbul ignore else: Check exists incase stop is interleaved with async function calls; hard to mock */\n                if (this.inputVideoStream) {\n                    this.logger.info('buffers are destroyed and pipeline could not start');\n                    this.forEachObserver(obs => {\n                        if (obs.processingDidFailToStart) {\n                            obs.processingDidFailToStart();\n                        }\n                    });\n                }\n                return;\n            }\n            // finally draws the image\n            const frameWidth = imageSource.width;\n            const frameHeight = imageSource.height;\n            if (frameWidth !== 0 && frameHeight !== 0) {\n                if (this.canvasOutput.width !== frameWidth && this.canvasOutput.height !== frameHeight) {\n                    this.canvasOutput.width = frameWidth;\n                    this.canvasOutput.height = frameHeight;\n                }\n                this.outputCtx.drawImage(imageSource, 0, 0, frameWidth, frameHeight, 0, 0, frameWidth, frameHeight);\n                if (!this.hasStarted) {\n                    this.hasStarted = true;\n                    this.forEachObserver(observer => {\n                        if (observer.processingDidStart) {\n                            observer.processingDidStart();\n                        }\n                    });\n                }\n            }\n            // measures time\n            const processVideoLatency = performance.now() - processVideoStart;\n            const leave = (1000 * 2) / this.framerate - processVideoLatency; // half fps\n            const nextFrameDelay = Math.max(0, 1000 / this.framerate - processVideoLatency);\n            if (leave <= 0) {\n                this.forEachObserver(obs => {\n                    if (obs.processingLatencyTooHigh) {\n                        obs.processingLatencyTooHigh(processVideoLatency);\n                    }\n                });\n            }\n            yield this.timer.start(nextFrameDelay, this.process.bind(this));\n        });\n        this.timer = timer;\n    }\n    destroy() {\n        this.stop();\n        if (this.stages) {\n            for (const stage of this.stages) {\n                stage.destroy();\n            }\n        }\n        this.timer.destroy();\n    }\n    get framerate() {\n        return this.fr;\n    }\n    // A negative framerate will cause `captureStream` to throw `NotSupportedError`.\n    // The setter prevents this by switching to the default framerate if less than 0.\n    set framerate(value) {\n        this.fr = value < 0 || value === undefined ? DEFAULT_FRAMERATE : value;\n    }\n    stop() {\n        // empty stream, stop pipeline\n        // null input media stream stops the pipeline.\n        this.videoInput.removeEventListener('loadedmetadata', this.process);\n        this.videoInput.srcObject = null;\n        // Clean the input stream and buffers.\n        this.destroyInputMediaStreamAndBuffers();\n        // Stop all the output tracks, but don't discard the media stream,\n        // because it's how other parts of the codebase recognize when\n        // a selected stream is part of this transform device.\n        if (this.outputMediaStream) {\n            for (const track of this.outputMediaStream.getVideoTracks()) {\n                track.stop();\n            }\n        }\n        if (this.hasStarted) {\n            this.hasStarted = false;\n            this.forEachObserver(observer => {\n                if (observer.processingDidStop) {\n                    observer.processingDidStop();\n                }\n            });\n        }\n    }\n    addObserver(observer) {\n        this.observers.add(observer);\n    }\n    removeObserver(observer) {\n        this.observers.delete(observer);\n    }\n    getInputMediaStream() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return this.inputVideoStream;\n        });\n    }\n    getActiveOutputMediaStream() {\n        if (this.isOutputMediaStreamActive()) {\n            return this.outputMediaStream;\n        }\n        this.outputMediaStream = this.canvasOutput.captureStream(this.framerate);\n        this.cloneInputAudioTracksToOutput();\n        return this.outputMediaStream;\n    }\n    /**\n     * `inputMediaStream` is by default used to construct one {@link CanvasVideoFrameBuffer}\n     * The buffer will be fed into the first {@link VideoFrameProcessor}.\n     */\n    setInputMediaStream(inputMediaStream) {\n        return __awaiter(this, void 0, void 0, function* () {\n            if (!inputMediaStream) {\n                this.stop();\n                return;\n            }\n            if (inputMediaStream.getVideoTracks().length === 0) {\n                this.logger.error('No video tracks in input media stream, ignoring');\n                return;\n            }\n            this.inputVideoStream = inputMediaStream;\n            const settings = this.inputVideoStream.getVideoTracks()[0].getSettings();\n            this.logger.info(`processing pipeline input stream settings ${JSON.stringify(settings)}`);\n            this.framerate = settings.frameRate;\n            this.canvasOutput.width = settings.width;\n            this.canvasOutput.height = settings.height;\n            this.videoInput.addEventListener('loadedmetadata', this.process);\n            this.videoInput.srcObject = this.inputVideoStream;\n            // avoid iOS safari full screen video\n            this.videoInput.setAttribute('playsinline', 'true');\n            // create sources\n            const canvasBuffer = new CanvasVideoFrameBuffer_1.default(this.canvasInput);\n            this.sourceBuffers.push(canvasBuffer);\n            this.videoInput.load();\n            try {\n                yield this.videoInput.play();\n            }\n            catch (_a) {\n                this.logger.warn('Video element play() overrided by another load().');\n            }\n            this.cloneInputAudioTracksToOutput();\n        });\n    }\n    cloneInputAudioTracksToOutput() {\n        if (!this.isOutputMediaStreamActive() || this.inputVideoStream === null) {\n            this.logger.info('Not cloning input audio tracks to output, do not have media streams ready');\n            return; // Just wait for `getActiveOutputMediaStream`\n        }\n        // Remove current audio tracks from output\n        for (const audioTrack of this.outputMediaStream.getAudioTracks()) {\n            this.logger.info(`Removing audio track ${audioTrack.id} from output stream`);\n            this.outputMediaStream.removeTrack(audioTrack);\n        }\n        for (const audioTrack of this.inputVideoStream.getAudioTracks()) {\n            this.logger.info(`Adding audio track ${audioTrack.id} to output stream`);\n            this.outputMediaStream.addTrack(audioTrack);\n        }\n    }\n    isOutputMediaStreamActive() {\n        return this.outputMediaStream && this.outputMediaStream.active;\n    }\n    set processors(stages) {\n        this.stages = stages;\n    }\n    get processors() {\n        return this.stages;\n    }\n    forEachObserver(observerFunc) {\n        for (const observer of this.observers) {\n            setTimeout(() => {\n                observerFunc(observer);\n            }, 0);\n        }\n    }\n    destroyInputMediaStreamAndBuffers() {\n        if (this.inputVideoStream) {\n            for (const track of this.inputVideoStream.getTracks()) {\n                track.stop();\n            }\n        }\n        this.inputVideoStream = null;\n        for (const buffer of this.sourceBuffers) {\n            buffer.destroy();\n        }\n        this.sourceBuffers = [];\n    }\n}\nexports.default = DefaultVideoFrameProcessorPipeline;\n//# sourceMappingURL=DefaultVideoFrameProcessorPipeline.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass DefaultVideoFrameProcessorTimer {\n    constructor() {\n        this.workerTimer = undefined;\n        this.callback = undefined;\n        this.workerTimer = this.createWorkerTimer();\n    }\n    // Create a timer that exists within a web worker. This timer will be used to\n    // retrigger the process call whenever time expires.\n    createWorkerTimer() {\n        try {\n            // Blob representing a script that will start a timer for the length\n            // of the message posted to it. After timer expiration, it will post\n            // a message back to the main thread holding the timerID\n            const timerBlob = new Blob([\n                `self.onmessage = async function(e){ \n            var timerID = null;\n            const awaitTimeout = delay => new Promise( resolve => {\n              timerID = setTimeout(resolve, delay);\n            })\n            await awaitTimeout(e.data);\n            postMessage(timerID);\n          }`,\n            ], { type: 'application/javascript' });\n            // Create the worker and link our process call to execute on\n            // every message it posts\n            const worker = new Worker(window.URL.createObjectURL(timerBlob));\n            return worker;\n        }\n        catch (error) {\n            // If blob: is not passed as a worker-src to csp, then the\n            // worker timer will fail... therefore no support\n        }\n    }\n    start(delay, callback) {\n        return __awaiter(this, void 0, void 0, function* () {\n            this.callback = callback;\n            if (this.workerTimer) {\n                this.workerTimer.onmessage = event => {\n                    this.lastTimeout = event.data;\n                    this.callback();\n                };\n                this.workerTimer.postMessage(delay);\n            }\n            else {\n                this.lastTimeout = setTimeout(() => {\n                    this.callback();\n                }, delay);\n            }\n        });\n    }\n    destroy() {\n        return __awaiter(this, void 0, void 0, function* () {\n            this.callback = undefined;\n            if (this.lastTimeout) {\n                clearTimeout(this.lastTimeout);\n                this.lastTimeout = undefined;\n            }\n            if (this.workerTimer) {\n                this.workerTimer.terminate();\n                this.workerTimer = undefined;\n            }\n        });\n    }\n}\nexports.default = DefaultVideoFrameProcessorTimer;\n//# sourceMappingURL=DefaultVideoFrameProcessorTimer.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst DefaultBrowserBehavior_1 = __importDefault(require(\"../browserbehavior/DefaultBrowserBehavior\"));\nconst DefaultVideoFrameProcessorPipeline_1 = __importDefault(require(\"./DefaultVideoFrameProcessorPipeline\"));\n/**\n * [[DefaultVideoTransformDevice]] is an augmented [[VideoInputDevice]].\n * It transform the input {@link Device} with an array of {@link VideoFrameProcessor} to produce a `MediaStream`.\n */\nclass DefaultVideoTransformDevice {\n    constructor(logger, device, processors, browserBehavior = new DefaultBrowserBehavior_1.default()) {\n        this.logger = logger;\n        this.device = device;\n        this.processors = processors;\n        this.browserBehavior = browserBehavior;\n        this.observers = new Set();\n        this.pipe = new DefaultVideoFrameProcessorPipeline_1.default(this.logger, this.processors);\n        this.pipe.addObserver(this);\n    }\n    /**\n     * getter for `outputMediaStream`.\n     * `outputMediaStream` is returned by internal {@link VideoFrameProcessorPipeline}.\n     * It is possible, but unlikely, that this accessor will throw.\n     */\n    get outputMediaStream() {\n        return this.pipe.outputMediaStream;\n    }\n    /** @internal */\n    passEventControllerToProcessors(eventController) {\n        /* istanbul ignore else */\n        if (this.pipe.processors) {\n            for (const processor of this.pipe.processors) {\n                // Only certain processors are configured to provide event reporting,\n                // so here will confirm that the processor has event controller capablility\n                if (typeof processor.setEventController === 'function') {\n                    processor.setEventController(eventController);\n                }\n            }\n        }\n    }\n    /**\n     * `chooseNewInnerDevice` preserves the inner pipeline and processing state and switches\n     * the inner device. Since the pipeline and processors are shared with the new transform device\n     * only one transform device can be used.\n     */\n    chooseNewInnerDevice(newDevice) {\n        const newTransformDevice = new DefaultVideoTransformDevice(this.logger, newDevice, this.processors, this.browserBehavior);\n        newTransformDevice.pipe = this.pipe;\n        return newTransformDevice;\n    }\n    /**\n     * Return the inner device as provided during construction.\n     */\n    getInnerDevice() {\n        return this.device;\n    }\n    intrinsicDevice() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return this.device;\n        });\n    }\n    /**\n     * Create {@link VideoFrameProcessorPipeline} if there is not a existing one and start video processors.\n     * Returns output `MediaStream` produced by {@link VideoFrameProcessorPipeline}.\n     */\n    transformStream(mediaStream) {\n        return __awaiter(this, void 0, void 0, function* () {\n            yield this.pipe.setInputMediaStream(mediaStream);\n            this.inputMediaStream = mediaStream;\n            return this.pipe.getActiveOutputMediaStream();\n        });\n    }\n    /**\n     * onOutputStreamDisconnect is called when device controller wants to detach\n     * the transform device. The default behavior is to stop the output\n     * media stream and release the input the media stream. If the input media stream\n     * is the provided device, it will not be released.\n     */\n    onOutputStreamDisconnect() {\n        this.logger.info('DefaultVideoTransformDevice: detach stopping input media stream');\n        const deviceIsMediaStream = this.device && this.device.id;\n        // Stop processing but keep the pipe and processors\n        this.pipe.stop();\n        // Turn off the camera, unless device is a MediaStream\n        if (!deviceIsMediaStream) {\n            if (this.inputMediaStream) {\n                for (const track of this.inputMediaStream.getVideoTracks()) {\n                    track.stop();\n                }\n            }\n        }\n    }\n    /**\n     * Dispose of the inner workings of the transform device, including pipeline and processors.\n     * `stop` can only be called when the transform device is not used by device controller anymore.\n     * After `stop` is called, all transform devices which share the pipeline must be discarded.\n     */\n    stop() {\n        return __awaiter(this, void 0, void 0, function* () {\n            if (this.inputMediaStream) {\n                for (const track of this.inputMediaStream.getVideoTracks()) {\n                    track.stop();\n                }\n            }\n            this.pipe.destroy();\n            this.inputMediaStream = null;\n        });\n    }\n    /**\n     * Add an observer to receive notifications about lifecycle events.\n     * See {@link DefaultVideoTransformDeviceObserver} for details.\n     * If the observer has already been added, this method call has no effect.\n     */\n    addObserver(observer) {\n        this.observers.add(observer);\n    }\n    /**\n     * Remove an existing observer. If the observer has not been previously. this method call has no effect.\n     */\n    removeObserver(observer) {\n        this.observers.delete(observer);\n    }\n    processingDidStart() {\n        this.logger.info('video transform device processing started');\n        this.forEachObserver(observer => {\n            if (observer.processingDidStart) {\n                observer.processingDidStart();\n            }\n        });\n    }\n    processingLatencyTooHigh(latencyMs) {\n        this.forEachObserver(observer => {\n            if (observer.processingLatencyTooHigh) {\n                observer.processingLatencyTooHigh(latencyMs);\n            }\n        });\n    }\n    processingDidFailToStart() {\n        this.logger.info('video transform device processing failed to start');\n        this.forEachObserver(observer => {\n            if (observer.processingDidFailToStart) {\n                observer.processingDidFailToStart();\n            }\n        });\n    }\n    processingDidStop() {\n        this.logger.info('video transform device processing stopped');\n        this.forEachObserver(observer => {\n            if (observer.processingDidStop) {\n                observer.processingDidStop();\n            }\n        });\n    }\n    forEachObserver(observerFunc) {\n        for (const observer of this.observers) {\n            setTimeout(() => {\n                observerFunc(observer);\n            }, 0);\n        }\n    }\n    get framerate() {\n        return this.pipe.framerate;\n    }\n    set framerate(value) {\n        this.pipe.framerate = value;\n    }\n}\nexports.default = DefaultVideoTransformDevice;\n//# sourceMappingURL=DefaultVideoTransformDevice.js.map","\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\n/**\n * [[NoOpVideoFrameProcessor]] implements [[VideoFrameProcessor]].\n * It's a no-op processor and input is passed to output directly.\n */\nclass NoOpVideoFrameProcessor {\n    process(buffers) {\n        return __awaiter(this, void 0, void 0, function* () {\n            return buffers;\n        });\n    }\n    destroy() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return;\n        });\n    }\n}\nexports.default = NoOpVideoFrameProcessor;\n//# sourceMappingURL=NoOpVideoFrameProcessor.js.map"],"names":[],"sourceRoot":""}