"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const event_target_shim_1 = require("event-target-shim");
const react_native_1 = require("react-native");
const EventEmitter_1 = require("./EventEmitter");
const Logger_1 = __importDefault(require("./Logger"));
const MediaStream_1 = __importDefault(require("./MediaStream"));
const MediaStreamTrack_1 = __importDefault(require("./MediaStreamTrack"));
const MediaStreamTrackEvent_1 = __importDefault(require("./MediaStreamTrackEvent"));
const RTCDataChannel_1 = __importDefault(require("./RTCDataChannel"));
const RTCDataChannelEvent_1 = __importDefault(require("./RTCDataChannelEvent"));
const RTCEvent_1 = __importDefault(require("./RTCEvent"));
const RTCIceCandidate_1 = __importDefault(require("./RTCIceCandidate"));
const RTCIceCandidateEvent_1 = __importDefault(require("./RTCIceCandidateEvent"));
const RTCRtpReceiveParameters_1 = __importDefault(require("./RTCRtpReceiveParameters"));
const RTCRtpReceiver_1 = __importDefault(require("./RTCRtpReceiver"));
const RTCRtpSendParameters_1 = __importDefault(require("./RTCRtpSendParameters"));
const RTCRtpSender_1 = __importDefault(require("./RTCRtpSender"));
const RTCRtpTransceiver_1 = __importDefault(require("./RTCRtpTransceiver"));
const RTCSessionDescription_1 = __importDefault(require("./RTCSessionDescription"));
const RTCTrackEvent_1 = __importDefault(require("./RTCTrackEvent"));
const RTCUtil = __importStar(require("./RTCUtil"));
const log = new Logger_1.default('pc');
const { WebRTCModule } = react_native_1.NativeModules;
const PEER_CONNECTION_EVENTS = [
    'connectionstatechange',
    'icecandidate',
    'icecandidateerror',
    'iceconnectionstatechange',
    'icegatheringstatechange',
    'negotiationneeded',
    'signalingstatechange',
    'datachannel',
    'track',
    'error'
];
let nextPeerConnectionId = 0;
class RTCPeerConnection extends event_target_shim_1.defineCustomEventTarget(...PEER_CONNECTION_EVENTS) {
    constructor(configuration) {
        super();
        this.localDescription = null;
        this.remoteDescription = null;
        this.signalingState = 'stable';
        this.iceGatheringState = 'new';
        this.connectionState = 'new';
        this.iceConnectionState = 'new';
        this._pcId = nextPeerConnectionId++;
        WebRTCModule.peerConnectionInit(configuration, this._pcId);
        this._transceivers = [];
        this._remoteStreams = new Map();
        this._pendingTrackEvents = [];
        this._registerEvents();
        log.debug(`${this._pcId} ctor`);
    }
    createOffer(options) {
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} createOffer`);
            const { sdpInfo, transceiversInfo } = yield WebRTCModule.peerConnectionCreateOffer(this._pcId, RTCUtil.normalizeOfferOptions(options));
            log.debug(`${this._pcId} createOffer OK`);
            this._updateTransceivers(transceiversInfo);
            return sdpInfo;
        });
    }
    createAnswer() {
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} createAnswer`);
            const { sdpInfo, transceiversInfo } = yield WebRTCModule.peerConnectionCreateAnswer(this._pcId, {});
            this._updateTransceivers(transceiversInfo);
            return sdpInfo;
        });
    }
    setConfiguration(configuration) {
        WebRTCModule.peerConnectionSetConfiguration(configuration, this._pcId);
    }
    setLocalDescription(sessionDescription) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} setLocalDescription`);
            let desc;
            if (sessionDescription) {
                desc = {
                    type: sessionDescription.type,
                    sdp: (_a = sessionDescription.sdp) !== null && _a !== void 0 ? _a : ''
                };
                if (!RTCUtil.isSdpTypeValid(desc.type)) {
                    throw new Error(`Invalid session description: invalid type: ${desc.type}`);
                }
            }
            else {
                desc = null;
            }
            const { sdpInfo, transceiversInfo } = yield WebRTCModule.peerConnectionSetLocalDescription(this._pcId, desc);
            if (sdpInfo.type && sdpInfo.sdp) {
                this.localDescription = new RTCSessionDescription_1.default(sdpInfo);
            }
            else {
                this.localDescription = null;
            }
            this._updateTransceivers(transceiversInfo, /* removeStopped */ (desc === null || desc === void 0 ? void 0 : desc.type) === 'answer');
            log.debug(`${this._pcId} setLocalDescription OK`);
        });
    }
    setRemoteDescription(sessionDescription) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} setRemoteDescription`);
            if (!sessionDescription) {
                return Promise.reject(new Error('No session description provided'));
            }
            const desc = {
                type: sessionDescription.type,
                sdp: (_a = sessionDescription.sdp) !== null && _a !== void 0 ? _a : ''
            };
            if (!RTCUtil.isSdpTypeValid((_b = desc.type) !== null && _b !== void 0 ? _b : '')) {
                throw new Error(`Invalid session description: invalid type: ${desc.type}`);
            }
            const { sdpInfo, newTransceivers, transceiversInfo } = yield WebRTCModule.peerConnectionSetRemoteDescription(this._pcId, desc);
            if (sdpInfo.type && sdpInfo.sdp) {
                this.remoteDescription = new RTCSessionDescription_1.default(sdpInfo);
            }
            else {
                this.remoteDescription = null;
            }
            newTransceivers === null || newTransceivers === void 0 ? void 0 : newTransceivers.forEach(t => {
                const { transceiverOrder, transceiver } = t;
                const newSender = new RTCRtpSender_1.default(Object.assign(Object.assign({}, transceiver.sender), { track: null }));
                const remoteTrack = transceiver.receiver.track ? new MediaStreamTrack_1.default(transceiver.receiver.track) : null;
                const newReceiver = new RTCRtpReceiver_1.default(Object.assign(Object.assign({}, transceiver.receiver), { track: remoteTrack }));
                const newTransceiver = new RTCRtpTransceiver_1.default(Object.assign(Object.assign({}, transceiver), { sender: newSender, receiver: newReceiver }));
                this._insertTransceiverSorted(transceiverOrder, newTransceiver);
            });
            this._updateTransceivers(transceiversInfo, /* removeStopped */ desc.type === 'answer');
            // Fire track events. They must fire before sRD resolves.
            const pendingTrackEvents = this._pendingTrackEvents;
            this._pendingTrackEvents = [];
            for (const ev of pendingTrackEvents) {
                const [transceiver] = this
                    .getTransceivers()
                    .filter(t => t.receiver.id === ev.receiver.id);
                // We need to fire this event for an existing track sometimes, like
                // when the transceiver direction (on the sending side) switches from
                // sendrecv to recvonly and then back.
                // @ts-ignore
                const track = transceiver.receiver.track;
                transceiver._mid = ev.transceiver.mid;
                transceiver._currentDirection = ev.transceiver.currentDirection;
                transceiver._direction = ev.transceiver.direction;
                // Get the stream object from the event. Create if necessary.
                const streams = ev.streams.map(streamInfo => {
                    // Here we are making sure that we don't create stream objects that already exist
                    // So that event listeners do get the same object if it has been created before.
                    if (!this._remoteStreams.has(streamInfo.streamId)) {
                        const stream = new MediaStream_1.default({
                            streamId: streamInfo.streamId,
                            streamReactTag: streamInfo.streamReactTag,
                            tracks: []
                        });
                        this._remoteStreams.set(streamInfo.streamId, stream);
                    }
                    const stream = this._remoteStreams.get(streamInfo.streamId);
                    if (!(stream === null || stream === void 0 ? void 0 : stream._tracks.includes(track))) {
                        stream === null || stream === void 0 ? void 0 : stream._tracks.push(track);
                    }
                    return stream;
                });
                const eventData = {
                    streams,
                    transceiver,
                    track,
                    receiver: transceiver.receiver
                };
                // @ts-ignore
                this.dispatchEvent(new RTCTrackEvent_1.default('track', eventData));
                streams.forEach(stream => {
                    // @ts-ignore
                    stream.dispatchEvent(new MediaStreamTrackEvent_1.default('addtrack', { track }));
                });
                // Dispatch an unmute event for the track.
                track._setMutedInternal(false);
            }
            log.debug(`${this._pcId} setRemoteDescription OK`);
        });
    }
    addIceCandidate(candidate) {
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} addIceCandidate`);
            if (!candidate || !candidate.candidate) {
                // XXX end-of candidates is not implemented: https://bugs.chromium.org/p/webrtc/issues/detail?id=9218
                return;
            }
            if (candidate.sdpMLineIndex === null ||
                candidate.sdpMLineIndex === undefined ||
                candidate.sdpMid === null ||
                candidate.sdpMid === undefined) {
                throw new TypeError('`sdpMLineIndex` and `sdpMid` must not null or undefined');
            }
            const newSdp = yield WebRTCModule.peerConnectionAddICECandidate(this._pcId, candidate.toJSON ? candidate.toJSON() : candidate);
            this.remoteDescription = new RTCSessionDescription_1.default(newSdp);
        });
    }
    /**
     * @brief Adds a new track to the {@link RTCPeerConnection},
     * and indicates that it is contained in the specified {@link MediaStream}s.
     * This method has to be synchronous as the W3C API expects a track to be returned
     * @param {MediaStreamTrack} track The track to be added
     * @param {...MediaStream} streams One or more {@link MediaStream}s the track needs to be added to
     * https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtrack
     */
    addTrack(track, ...streams) {
        log.debug(`${this._pcId} addTrack`);
        if (this.connectionState === 'closed') {
            throw new Error('Peer Connection is closed');
        }
        if (this._trackExists(track)) {
            throw new Error('Track already exists in a sender');
        }
        const streamIds = streams.map(s => s.id);
        const result = WebRTCModule.peerConnectionAddTrack(this._pcId, track.id, { streamIds });
        if (result === null) {
            throw new Error('Could not add sender');
        }
        const { transceiverOrder, transceiver, sender } = result;
        // According to the W3C docs, the sender could have been reused, and
        // so we check if that is the case, and update accordingly.
        const [existingSender] = this
            .getSenders()
            .filter(s => s.id === sender.id);
        if (existingSender) {
            // Update sender
            existingSender._track = track;
            // Update the corresponding transceiver as well
            const [existingTransceiver] = this
                .getTransceivers()
                .filter(t => t.sender.id === existingSender.id);
            existingTransceiver._direction = transceiver.direction;
            existingTransceiver._currentDirection = transceiver.currentDirection;
            return existingSender;
        }
        // This is a new transceiver, should create a transceiver for it and add it
        const newSender = new RTCRtpSender_1.default(Object.assign(Object.assign({}, transceiver.sender), { track }));
        const remoteTrack = transceiver.receiver.track ? new MediaStreamTrack_1.default(transceiver.receiver.track) : null;
        const newReceiver = new RTCRtpReceiver_1.default(Object.assign(Object.assign({}, transceiver.receiver), { track: remoteTrack }));
        const newTransceiver = new RTCRtpTransceiver_1.default(Object.assign(Object.assign({}, transceiver), { sender: newSender, receiver: newReceiver }));
        this._insertTransceiverSorted(transceiverOrder, newTransceiver);
        return newSender;
    }
    addTransceiver(source, init) {
        log.debug(`${this._pcId} addTransceiver`);
        let src = {};
        if (source === 'audio') {
            src = { type: 'audio' };
        }
        else if (source === 'video') {
            src = { type: 'video' };
        }
        else {
            src = { trackId: source.id };
        }
        // Extract the stream ids
        if (init && init.streams) {
            init.streamIds = init.streams.map(stream => stream.id);
        }
        const result = WebRTCModule.peerConnectionAddTransceiver(this._pcId, Object.assign(Object.assign({}, src), { init: Object.assign({}, init) }));
        if (result === null) {
            throw new Error('Transceiver could not be added');
        }
        const t = result.transceiver;
        let track = null;
        if (typeof source === 'string') {
            if (t.sender.track) {
                track = new MediaStreamTrack_1.default(t.sender.track);
            }
        }
        else {
            // 'source' is a MediaStreamTrack
            track = source;
        }
        const sender = new RTCRtpSender_1.default(Object.assign(Object.assign({}, t.sender), { track }));
        const remoteTrack = t.receiver.track ? new MediaStreamTrack_1.default(t.receiver.track) : null;
        const receiver = new RTCRtpReceiver_1.default(Object.assign(Object.assign({}, t.receiver), { track: remoteTrack }));
        const transceiver = new RTCRtpTransceiver_1.default(Object.assign(Object.assign({}, result.transceiver), { sender,
            receiver }));
        this._insertTransceiverSorted(result.transceiverOrder, transceiver);
        return transceiver;
    }
    removeTrack(sender) {
        log.debug(`${this._pcId} removeTrack`);
        if (this._pcId !== sender._peerConnectionId) {
            throw new Error('Sender does not belong to this peer connection');
        }
        if (this.connectionState === 'closed') {
            throw new Error('Peer Connection is closed');
        }
        const existingSender = this
            .getSenders()
            .find(s => s === sender);
        if (!existingSender) {
            throw new Error('Sender does not exist');
        }
        if (existingSender.track === null) {
            return;
        }
        // Blocking!
        WebRTCModule.peerConnectionRemoveTrack(this._pcId, sender.id);
        existingSender._track = null;
        const [existingTransceiver] = this
            .getTransceivers()
            .filter(t => t.sender.id === existingSender.id);
        existingTransceiver._direction = existingTransceiver.direction === 'sendrecv' ? 'recvonly' : 'inactive';
    }
    getStats(selector) {
        return __awaiter(this, void 0, void 0, function* () {
            log.debug(`${this._pcId} getStats`);
            if (!selector) {
                const data = yield WebRTCModule.peerConnectionGetStats(this._pcId);
                /**
                 * On both Android and iOS it is faster to construct a single
                 * JSON string representing the Map of StatsReports and have it
                 * pass through the React Native bridge rather than the Map of
                 * StatsReports. While the implementations do try to be faster in
                 * general, the stress is on being faster to pass through the React
                 * Native bridge which is a bottleneck that tends to be visible in
                 * the UI when there is congestion involving UI-related passing.
                 */
                return new Map(JSON.parse(data));
            }
            else {
                const senders = this.getSenders().filter(s => s.track === selector);
                const receivers = this.getReceivers().filter(r => r.track === selector);
                const matches = senders.length + receivers.length;
                if (matches === 0) {
                    throw new Error('Invalid selector: could not find matching sender / receiver');
                }
                else if (matches > 1) {
                    throw new Error('Invalid selector: multiple matching senders / receivers');
                }
                else {
                    const sr = senders[0] || receivers[0];
                    return sr.getStats();
                }
            }
        });
    }
    getTransceivers() {
        return this._transceivers.map(e => e.transceiver);
    }
    getSenders() {
        // @ts-ignore
        return this._transceivers.map(e => !e.transceiver.stopped && e.transceiver.sender).filter(Boolean);
    }
    getReceivers() {
        // @ts-ignore
        return this._transceivers.map(e => !e.transceiver.stopped && e.transceiver.receiver).filter(Boolean);
    }
    close() {
        log.debug(`${this._pcId} close`);
        if (this.connectionState === 'closed') {
            return;
        }
        WebRTCModule.peerConnectionClose(this._pcId);
        // Mark transceivers as stopped.
        this._transceivers.forEach(({ transceiver }) => {
            transceiver._setStopped();
        });
    }
    restartIce() {
        WebRTCModule.peerConnectionRestartIce(this._pcId);
    }
    _registerEvents() {
        EventEmitter_1.addListener(this, 'peerConnectionOnRenegotiationNeeded', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            // @ts-ignore
            this.dispatchEvent(new RTCEvent_1.default('negotiationneeded'));
        });
        EventEmitter_1.addListener(this, 'peerConnectionIceConnectionChanged', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            this.iceConnectionState = ev.iceConnectionState;
            // @ts-ignore
            this.dispatchEvent(new RTCEvent_1.default('iceconnectionstatechange'));
        });
        EventEmitter_1.addListener(this, 'peerConnectionStateChanged', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            this.connectionState = ev.connectionState;
            // @ts-ignore
            this.dispatchEvent(new RTCEvent_1.default('connectionstatechange'));
            if (ev.connectionState === 'closed') {
                // This PeerConnection is done, clean up.
                EventEmitter_1.removeListener(this);
                WebRTCModule.peerConnectionDispose(this._pcId);
            }
        });
        EventEmitter_1.addListener(this, 'peerConnectionSignalingStateChanged', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            this.signalingState = ev.signalingState;
            // @ts-ignore
            this.dispatchEvent(new RTCEvent_1.default('signalingstatechange'));
        });
        // Consider moving away from this event: https://github.com/WebKit/WebKit/pull/3953
        EventEmitter_1.addListener(this, 'peerConnectionOnTrack', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            log.debug(`${this._pcId} ontrack`);
            // NOTE: We need to make sure the track event fires right before sRD completes,
            // so we queue them up here and dispatch the events when sRD fires, but before completing it.
            // In the future we should probably implement out own logic and drop this event altogether.
            this._pendingTrackEvents.push(ev);
        });
        EventEmitter_1.addListener(this, 'peerConnectionOnRemoveTrack', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            log.debug(`${this._pcId} onremovetrack ${ev.receiverId}`);
            const receiver = this.getReceivers().find(r => r.id === ev.receiverId);
            const track = receiver === null || receiver === void 0 ? void 0 : receiver.track;
            if (receiver && track) {
                // As per the spec:
                // - Remove the track from any media streams that were previously passed to the `track` event.
                // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-removetrack,
                // - Mark the track as muted:
                // https://w3c.github.io/webrtc-pc/#process-remote-track-removal
                for (const stream of this._remoteStreams.values()) {
                    if (stream._tracks.includes(track)) {
                        const trackIdx = stream._tracks.indexOf(track);
                        log.debug(`${this._pcId} removetrack ${track.id}`);
                        stream._tracks.splice(trackIdx, 1);
                        // @ts-ignore
                        stream.dispatchEvent(new MediaStreamTrackEvent_1.default('removetrack', { track }));
                        // Dispatch a mute event for the track.
                        track._setMutedInternal(true);
                    }
                }
            }
        });
        EventEmitter_1.addListener(this, 'peerConnectionGotICECandidate', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            const sdpInfo = ev.sdp;
            // Can happen when doing a rollback.
            if (sdpInfo.type && sdpInfo.sdp) {
                this.localDescription = new RTCSessionDescription_1.default(sdpInfo);
            }
            else {
                this.localDescription = null;
            }
            const candidate = new RTCIceCandidate_1.default(ev.candidate);
            // @ts-ignore
            this.dispatchEvent(new RTCIceCandidateEvent_1.default('icecandidate', { candidate }));
        });
        EventEmitter_1.addListener(this, 'peerConnectionIceGatheringChanged', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            this.iceGatheringState = ev.iceGatheringState;
            if (this.iceGatheringState === 'complete') {
                const sdpInfo = ev.sdp;
                // Can happen when doing a rollback.
                if (sdpInfo.type && sdpInfo.sdp) {
                    this.localDescription = new RTCSessionDescription_1.default(sdpInfo);
                }
                else {
                    this.localDescription = null;
                }
                // @ts-ignore
                this.dispatchEvent(new RTCIceCandidateEvent_1.default('icecandidate', { candidate: null }));
            }
            // @ts-ignore
            this.dispatchEvent(new RTCEvent_1.default('icegatheringstatechange'));
        });
        EventEmitter_1.addListener(this, 'peerConnectionDidOpenDataChannel', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            const channel = new RTCDataChannel_1.default(ev.dataChannel);
            // @ts-ignore
            this.dispatchEvent(new RTCDataChannelEvent_1.default('datachannel', { channel }));
        });
        EventEmitter_1.addListener(this, 'mediaStreamTrackMuteChanged', (ev) => {
            if (ev.pcId !== this._pcId) {
                return;
            }
            const [track] = this.getReceivers().map(r => r.track).filter(t => (t === null || t === void 0 ? void 0 : t.id) === ev.trackId);
            if (track) {
                track._setMutedInternal(ev.muted);
            }
        });
    }
    /**
     * Creates a new RTCDataChannel object with the given label. The
     * RTCDataChannelInit dictionary can be used to configure properties of the
     * underlying channel such as data reliability.
     *
     * @param {string} label - the value with which the label attribute of the new
     * instance is to be initialized
     * @param {RTCDataChannelInit} dataChannelDict - an optional dictionary of
     * values with which to initialize corresponding attributes of the new
     * instance such as id
     */
    createDataChannel(label, dataChannelDict) {
        if (dataChannelDict && 'id' in dataChannelDict) {
            const id = dataChannelDict.id;
            if (typeof id !== 'number') {
                throw new TypeError('DataChannel id must be a number: ' + id);
            }
        }
        const channelInfo = WebRTCModule.createDataChannel(this._pcId, label, dataChannelDict);
        if (channelInfo === null) {
            throw new TypeError('Failed to create new DataChannel');
        }
        return new RTCDataChannel_1.default(channelInfo);
    }
    /**
     * Check whether a media stream track exists already in a sender.
     * See https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtrack for more information
     */
    _trackExists(track) {
        const [sender] = this
            .getSenders()
            .filter(sender => { var _a; return ((_a = sender.track) === null || _a === void 0 ? void 0 : _a.id) === track.id; });
        return sender ? true : false;
    }
    /**
     * Updates transceivers after offer/answer updates if necessary.
     */
    _updateTransceivers(transceiverUpdates, removeStopped = false) {
        for (const update of transceiverUpdates) {
            const [transceiver] = this
                .getTransceivers()
                .filter(t => t.sender.id === update.transceiverId);
            if (!transceiver) {
                continue;
            }
            transceiver._currentDirection = update.currentDirection;
            transceiver._mid = update.mid;
            transceiver._stopped = Boolean(update.isStopped);
            transceiver._sender._rtpParameters = new RTCRtpSendParameters_1.default(update.senderRtpParameters);
            transceiver._receiver._rtpParameters = new RTCRtpReceiveParameters_1.default(update.receiverRtpParameters);
        }
        if (removeStopped) {
            const stopped = this.getTransceivers().filter(t => t.stopped);
            const newTransceivers = this._transceivers.filter(t => !stopped.includes(t.transceiver));
            this._transceivers = newTransceivers;
        }
    }
    /**
     * Inserts transceiver into the transceiver array in the order they are created (timestamp).
     * @param order an index that refers to when it it was created relatively.
     * @param transceiver the transceiver object to be inserted.
     */
    _insertTransceiverSorted(order, transceiver) {
        this._transceivers.push({ order, transceiver });
        this._transceivers.sort((a, b) => a.order - b.order);
    }
}
exports.default = RTCPeerConnection;
//# sourceMappingURL=RTCPeerConnection.js.map