"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const pick_port_1 = require("pick-port");
const mediasoup = require("../");
const enhancedEvents_1 = require("../enhancedEvents");
const utils = require("../utils");
const ctx = {
    mediaCodecs: utils.deepFreeze([
        {
            kind: 'audio',
            mimeType: 'audio/opus',
            clockRate: 48000,
            channels: 2,
        },
        {
            kind: 'video',
            mimeType: 'video/VP8',
            clockRate: 90000,
        },
    ]),
    audioProducerOptions: utils.deepFreeze({
        kind: 'audio',
        rtpParameters: {
            mid: 'AUDIO',
            codecs: [
                {
                    mimeType: 'audio/opus',
                    payloadType: 111,
                    clockRate: 48000,
                    channels: 2,
                    parameters: {
                        useinbandfec: 1,
                        foo: 'bar1',
                    },
                },
            ],
            headerExtensions: [
                {
                    uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
                    id: 10,
                },
            ],
            encodings: [{ ssrc: 11111111 }],
            rtcp: {
                cname: 'FOOBAR',
            },
        },
        appData: { foo: 'bar1' },
    }),
    videoProducerOptions: utils.deepFreeze({
        kind: 'video',
        rtpParameters: {
            mid: 'VIDEO',
            codecs: [
                {
                    mimeType: 'video/VP8',
                    payloadType: 112,
                    clockRate: 90000,
                    rtcpFeedback: [
                        { type: 'nack' },
                        { type: 'nack', parameter: 'pli' },
                        { type: 'goog-remb' },
                        { type: 'lalala' },
                    ],
                },
            ],
            headerExtensions: [
                {
                    uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
                    id: 10,
                },
                {
                    uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
                    id: 11,
                },
                {
                    uri: 'urn:3gpp:video-orientation',
                    id: 13,
                },
            ],
            encodings: [{ ssrc: 22222222 }, { ssrc: 22222223 }, { ssrc: 22222224 }],
            rtcp: {
                cname: 'FOOBAR',
            },
        },
        appData: { foo: 'bar2' },
    }),
    dataProducerOptions: utils.deepFreeze({
        sctpStreamParameters: {
            streamId: 666,
            ordered: false,
            maxPacketLifeTime: 5000,
        },
        label: 'foo',
        protocol: 'bar',
    }),
    consumerDeviceCapabilities: utils.deepFreeze({
        codecs: [
            {
                kind: 'audio',
                mimeType: 'audio/opus',
                preferredPayloadType: 100,
                clockRate: 48000,
                channels: 2,
            },
            {
                kind: 'video',
                mimeType: 'video/VP8',
                preferredPayloadType: 101,
                clockRate: 90000,
                rtcpFeedback: [
                    { type: 'nack' },
                    { type: 'ccm', parameter: 'fir' },
                    { type: 'transport-cc' },
                ],
            },
            {
                kind: 'video',
                mimeType: 'video/rtx',
                preferredPayloadType: 102,
                clockRate: 90000,
                parameters: {
                    apt: 101,
                },
                rtcpFeedback: [],
            },
        ],
        headerExtensions: [
            {
                kind: 'video',
                uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
                preferredId: 4,
                preferredEncrypt: false,
                direction: 'sendrecv',
            },
            {
                kind: 'video',
                uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
                preferredId: 5,
                preferredEncrypt: false,
            },
            {
                kind: 'audio',
                uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
                preferredId: 10,
                preferredEncrypt: false,
            },
        ],
    }),
};
beforeEach(async () => {
    ctx.worker1 = await mediasoup.createWorker();
    ctx.worker2 = await mediasoup.createWorker();
    ctx.router1 = await ctx.worker1.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    ctx.router2 = await ctx.worker2.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    ctx.webRtcTransport1 = await ctx.router1.createWebRtcTransport({
        listenInfos: [{ protocol: 'udp', ip: '127.0.0.1' }],
        enableSctp: true,
    });
    ctx.webRtcTransport2 = await ctx.router2.createWebRtcTransport({
        listenIps: ['127.0.0.1'],
        enableSctp: true,
    });
    ctx.audioProducer = await ctx.webRtcTransport1.produce(ctx.audioProducerOptions);
    ctx.videoProducer = await ctx.webRtcTransport1.produce(ctx.videoProducerOptions);
    ctx.dataProducer = await ctx.webRtcTransport1.produceData(ctx.dataProducerOptions);
});
afterEach(async () => {
    ctx.worker1?.close();
    ctx.worker2?.close();
    if (ctx.worker1?.subprocessClosed === false) {
        await (0, enhancedEvents_1.enhancedOnce)(ctx.worker1, 'subprocessclose');
    }
    if (ctx.worker2?.subprocessClosed === false) {
        await (0, enhancedEvents_1.enhancedOnce)(ctx.worker2, 'subprocessclose');
    }
});
test('router.pipeToRouter() succeeds with audio', async () => {
    const { pipeConsumer, pipeProducer } = (await ctx.router1.pipeToRouter({
        producerId: ctx.audioProducer.id,
        router: ctx.router2,
    }));
    const dump1 = await ctx.router1.dump();
    // There should be two Transports in router1:
    // - WebRtcTransport for audioProducer and videoProducer.
    // - PipeTransport between router1 and router2.
    expect(dump1.transportIds.length).toBe(2);
    const dump2 = await ctx.router2.dump();
    // There should be two Transports in router2:
    // - WebRtcTransport for audioConsumer and videoConsumer.
    // - PipeTransport between router2 and router1.
    expect(dump2.transportIds.length).toBe(2);
    expect(typeof pipeConsumer.id).toBe('string');
    expect(pipeConsumer.closed).toBe(false);
    expect(pipeConsumer.kind).toBe('audio');
    expect(typeof pipeConsumer.rtpParameters).toBe('object');
    expect(pipeConsumer.rtpParameters.mid).toBeUndefined();
    expect(pipeConsumer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'audio/opus',
            clockRate: 48000,
            payloadType: 100,
            channels: 2,
            parameters: {
                useinbandfec: 1,
                foo: 'bar1',
            },
            rtcpFeedback: [],
        },
    ]);
    expect(pipeConsumer.rtpParameters.headerExtensions).toEqual([
        {
            uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
            id: 10,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
            id: 13,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
            id: 14,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(pipeConsumer.type).toBe('pipe');
    expect(pipeConsumer.paused).toBe(false);
    expect(pipeConsumer.producerPaused).toBe(false);
    expect(pipeConsumer.score).toEqual({
        score: 10,
        producerScore: 10,
        producerScores: [],
    });
    expect(pipeConsumer.appData).toEqual({});
    expect(pipeProducer.id).toBe(ctx.audioProducer.id);
    expect(pipeProducer.closed).toBe(false);
    expect(pipeProducer.kind).toBe('audio');
    expect(typeof pipeProducer.rtpParameters).toBe('object');
    expect(pipeProducer.rtpParameters.mid).toBeUndefined();
    expect(pipeProducer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'audio/opus',
            payloadType: 100,
            clockRate: 48000,
            channels: 2,
            parameters: {
                useinbandfec: 1,
                foo: 'bar1',
            },
            rtcpFeedback: [],
        },
    ]);
    expect(pipeProducer.rtpParameters.headerExtensions).toEqual([
        {
            uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
            id: 10,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
            id: 13,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
            id: 14,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(pipeProducer.paused).toBe(false);
}, 2000);
test('router.pipeToRouter() succeeds with video', async () => {
    await ctx.videoProducer.pause();
    const { pipeConsumer, pipeProducer } = (await ctx.router1.pipeToRouter({
        producerId: ctx.videoProducer.id,
        router: ctx.router2,
    }));
    const dump1 = await ctx.router1.dump();
    // No new PipeTransport should has been created. The existing one is used.
    expect(dump1.transportIds.length).toBe(2);
    const dump2 = await ctx.router2.dump();
    // No new PipeTransport should has been created. The existing one is used.
    expect(dump2.transportIds.length).toBe(2);
    expect(typeof pipeConsumer.id).toBe('string');
    expect(pipeConsumer.closed).toBe(false);
    expect(pipeConsumer.kind).toBe('video');
    expect(typeof pipeConsumer.rtpParameters).toBe('object');
    expect(pipeConsumer.rtpParameters.mid).toBeUndefined();
    expect(pipeConsumer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'video/VP8',
            payloadType: 101,
            clockRate: 90000,
            parameters: {},
            rtcpFeedback: [
                { type: 'nack', parameter: 'pli' },
                { type: 'ccm', parameter: 'fir' },
            ],
        },
    ]);
    expect(pipeConsumer.rtpParameters.headerExtensions).toEqual([
        // NOTE: Remove this once framemarking draft becomes RFC.
        {
            uri: 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07',
            id: 6,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:framemarking',
            id: 7,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:3gpp:video-orientation',
            id: 11,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:toffset',
            id: 12,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
            id: 13,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
            id: 14,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(pipeConsumer.type).toBe('pipe');
    expect(pipeConsumer.paused).toBe(false);
    expect(pipeConsumer.producerPaused).toBe(true);
    expect(pipeConsumer.score).toEqual({
        score: 10,
        producerScore: 10,
        producerScores: [],
    });
    expect(pipeConsumer.appData).toEqual({});
    expect(pipeProducer.id).toBe(ctx.videoProducer.id);
    expect(pipeProducer.closed).toBe(false);
    expect(pipeProducer.kind).toBe('video');
    expect(typeof pipeProducer.rtpParameters).toBe('object');
    expect(pipeProducer.rtpParameters.mid).toBeUndefined();
    expect(pipeProducer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'video/VP8',
            payloadType: 101,
            clockRate: 90000,
            parameters: {},
            rtcpFeedback: [
                { type: 'nack', parameter: 'pli' },
                { type: 'ccm', parameter: 'fir' },
            ],
        },
    ]);
    expect(pipeProducer.rtpParameters.headerExtensions).toEqual([
        // NOTE: Remove this once framemarking draft becomes RFC.
        {
            uri: 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07',
            id: 6,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:framemarking',
            id: 7,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:3gpp:video-orientation',
            id: 11,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:toffset',
            id: 12,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
            id: 13,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
            id: 14,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(pipeProducer.paused).toBe(true);
}, 2000);
test('router.createPipeTransport() with wrong arguments rejects with TypeError', async () => {
    // @ts-expect-error --- Testing purposes.
    await expect(ctx.router1.createPipeTransport({})).rejects.toThrow(TypeError);
    await expect(ctx.router1.createPipeTransport({
        listenInfo: {
            protocol: 'udp',
            ip: '127.0.0.1',
            portRange: { min: 4000, max: 3000 },
        },
    })).rejects.toThrow(TypeError);
    await expect(ctx.router1.createPipeTransport({ listenIp: '123' })).rejects.toThrow(TypeError);
    await expect(
    // @ts-expect-error --- Testing purposes.
    ctx.router1.createPipeTransport({ listenIp: ['127.0.0.1'] })).rejects.toThrow(TypeError);
    await expect(ctx.router1.createPipeTransport({
        listenInfo: { protocol: 'tcp', ip: '127.0.0.1' },
    })).rejects.toThrow(TypeError);
    await expect(ctx.router1.createPipeTransport({
        listenInfo: { protocol: 'udp', ip: '127.0.0.1' },
        // @ts-expect-error --- Testing purposes.
        appData: 'NOT-AN-OBJECT',
    })).rejects.toThrow(TypeError);
}, 2000);
test('router.createPipeTransport() with enableRtx succeeds', async () => {
    const pipeTransport = await ctx.router1.createPipeTransport({
        listenInfo: {
            protocol: 'udp',
            ip: '127.0.0.1',
            portRange: { min: 2000, max: 3000 },
        },
        enableRtx: true,
    });
    const pipeConsumer = await pipeTransport.consume({
        producerId: ctx.videoProducer.id,
    });
    expect(typeof pipeConsumer.id).toBe('string');
    expect(pipeConsumer.closed).toBe(false);
    expect(pipeConsumer.kind).toBe('video');
    expect(typeof pipeConsumer.rtpParameters).toBe('object');
    expect(pipeConsumer.rtpParameters.mid).toBeUndefined();
    expect(pipeConsumer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'video/VP8',
            payloadType: 101,
            clockRate: 90000,
            parameters: {},
            rtcpFeedback: [
                { type: 'nack', parameter: '' },
                { type: 'nack', parameter: 'pli' },
                { type: 'ccm', parameter: 'fir' },
            ],
        },
        {
            mimeType: 'video/rtx',
            payloadType: 102,
            clockRate: 90000,
            parameters: { apt: 101 },
            rtcpFeedback: [],
        },
    ]);
    expect(pipeConsumer.rtpParameters.headerExtensions).toEqual([
        // NOTE: Remove this once framemarking draft becomes RFC.
        {
            uri: 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07',
            id: 6,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:framemarking',
            id: 7,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:3gpp:video-orientation',
            id: 11,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'urn:ietf:params:rtp-hdrext:toffset',
            id: 12,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time',
            id: 13,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
            id: 14,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(pipeConsumer.type).toBe('pipe');
    expect(pipeConsumer.paused).toBe(false);
    expect(pipeConsumer.producerPaused).toBe(false);
    expect(pipeConsumer.score).toEqual({
        score: 10,
        producerScore: 10,
        producerScores: [],
    });
    expect(pipeConsumer.appData).toEqual({});
}, 2000);
test('pipeTransport.connect() with valid SRTP parameters succeeds', async () => {
    const pipeTransport = await ctx.router1.createPipeTransport({
        listenIp: '127.0.0.1',
        enableSrtp: true,
    });
    expect(typeof pipeTransport.srtpParameters).toBe('object');
    // The master length of AEAD_AES_256_GCM.
    expect(pipeTransport.srtpParameters?.keyBase64.length).toBe(60);
    // Valid srtpParameters.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        srtpParameters: {
            cryptoSuite: 'AEAD_AES_256_GCM',
            keyBase64: 'YTdjcDBvY2JoMGY5YXNlNDc0eDJsdGgwaWRvNnJsamRrdG16aWVpZHphdHo=',
        },
    })).resolves.toBeUndefined();
}, 2000);
test('pipeTransport.connect() with srtpParameters fails if enableSrtp is unset', async () => {
    const pipeTransport = await ctx.router1.createPipeTransport({
        listenInfo: {
            protocol: 'udp',
            ip: '127.0.0.1',
            portRange: { min: 2000, max: 3000 },
        },
        enableRtx: true,
    });
    expect(pipeTransport.srtpParameters).toBeUndefined();
    // No SRTP enabled so passing srtpParameters must fail.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        srtpParameters: {
            cryptoSuite: 'AEAD_AES_256_GCM',
            keyBase64: 'YTdjcDBvY2JoMGY5YXNlNDc0eDJsdGgwaWRvNnJsamRrdG16aWVpZHphdHo=',
        },
    })).rejects.toThrow(TypeError);
    // No SRTP enabled so passing srtpParameters (even if invalid) must fail.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        // @ts-expect-error --- Testing purposes.
        srtpParameters: 'invalid',
    })).rejects.toThrow(TypeError);
});
test('pipeTransport.connect() with invalid srtpParameters fails', async () => {
    const pipeTransport = await ctx.router1.createPipeTransport({
        listenIp: '127.0.0.1',
        enableSrtp: true,
    });
    expect(typeof pipeTransport.id).toBe('string');
    expect(typeof pipeTransport.srtpParameters).toBe('object');
    // The master length of AEAD_AES_256_GCM.
    expect(pipeTransport.srtpParameters?.keyBase64.length).toBe(60);
    // Missing srtpParameters.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
    })).rejects.toThrow(TypeError);
    // Invalid srtpParameters.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        // @ts-expect-error --- Testing purposes.
        srtpParameters: 1,
    })).rejects.toThrow(TypeError);
    // Missing srtpParameters.cryptoSuite.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        // @ts-expect-error --- Testing purposes.
        srtpParameters: {
            keyBase64: 'YTdjcDBvY2JoMGY5YXNlNDc0eDJsdGgwaWRvNnJsamRrdG16aWVpZHphdHo=',
        },
    })).rejects.toThrow(TypeError);
    // Missing srtpParameters.keyBase64.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        // @ts-expect-error --- Testing purposes.
        srtpParameters: {
            cryptoSuite: 'AEAD_AES_256_GCM',
        },
    })).rejects.toThrow(TypeError);
    // Invalid srtpParameters.cryptoSuite.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        srtpParameters: {
            // @ts-expect-error --- Testing purposes.
            cryptoSuite: 'FOO',
            keyBase64: 'YTdjcDBvY2JoMGY5YXNlNDc0eDJsdGgwaWRvNnJsamRrdG16aWVpZHphdHo=',
        },
    })).rejects.toThrow(TypeError);
    // Invalid srtpParameters.cryptoSuite.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        srtpParameters: {
            // @ts-expect-error --- Testing purposes.
            cryptoSuite: 123,
            keyBase64: 'YTdjcDBvY2JoMGY5YXNlNDc0eDJsdGgwaWRvNnJsamRrdG16aWVpZHphdHo=',
        },
    })).rejects.toThrow(TypeError);
    // Invalid srtpParameters.keyBase64.
    await expect(pipeTransport.connect({
        ip: '127.0.0.2',
        port: 9999,
        srtpParameters: {
            cryptoSuite: 'AEAD_AES_256_GCM',
            // @ts-expect-error --- Testing purposes.
            keyBase64: [],
        },
    })).rejects.toThrow(TypeError);
}, 2000);
test('router.createPipeTransport() with fixed port succeeds', async () => {
    const port = await (0, pick_port_1.pickPort)({
        type: 'udp',
        ip: '127.0.0.1',
        reserveTimeout: 0,
    });
    const pipeTransport = await ctx.router1.createPipeTransport({
        listenInfo: { protocol: 'udp', ip: '127.0.0.1', port },
    });
    expect(pipeTransport.tuple.localPort).toEqual(port);
}, 2000);
test('transport.consume() for a pipe Producer succeeds', async () => {
    await ctx.router1.pipeToRouter({
        producerId: ctx.videoProducer.id,
        router: ctx.router2,
    });
    const videoConsumer = await ctx.webRtcTransport2.consume({
        producerId: ctx.videoProducer.id,
        rtpCapabilities: ctx.consumerDeviceCapabilities,
    });
    expect(typeof videoConsumer.id).toBe('string');
    expect(videoConsumer.closed).toBe(false);
    expect(videoConsumer.kind).toBe('video');
    expect(typeof videoConsumer.rtpParameters).toBe('object');
    expect(videoConsumer.rtpParameters.mid).toBe('0');
    expect(videoConsumer.rtpParameters.codecs).toEqual([
        {
            mimeType: 'video/VP8',
            payloadType: 101,
            clockRate: 90000,
            parameters: {},
            rtcpFeedback: [
                { type: 'nack', parameter: '' },
                { type: 'ccm', parameter: 'fir' },
                { type: 'transport-cc', parameter: '' },
            ],
        },
        {
            mimeType: 'video/rtx',
            payloadType: 102,
            clockRate: 90000,
            parameters: {
                apt: 101,
            },
            rtcpFeedback: [],
        },
    ]);
    expect(videoConsumer.rtpParameters.headerExtensions).toEqual([
        {
            uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
            id: 4,
            encrypt: false,
            parameters: {},
        },
        {
            uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
            id: 5,
            encrypt: false,
            parameters: {},
        },
    ]);
    expect(videoConsumer.rtpParameters.encodings?.length).toBe(1);
    expect(typeof videoConsumer.rtpParameters.encodings?.[0].ssrc).toBe('number');
    expect(typeof videoConsumer.rtpParameters.encodings?.[0].rtx).toBe('object');
    expect(typeof videoConsumer.rtpParameters.encodings?.[0].rtx?.ssrc).toBe('number');
    expect(videoConsumer.type).toBe('simulcast');
    expect(videoConsumer.paused).toBe(false);
    expect(videoConsumer.producerPaused).toBe(false);
    expect(videoConsumer.score).toEqual({
        score: 10,
        producerScore: 0,
        producerScores: [0, 0, 0],
    });
    expect(videoConsumer.appData).toEqual({});
}, 2000);
test('producer.pause() and producer.resume() are transmitted to pipe Consumer', async () => {
    await ctx.videoProducer.pause();
    // We need to obtain the pipeProducer to await for its 'puase' and 'resume'
    // events, otherwise we may get errors like this:
    // InvalidStateError: Channel closed, pending request aborted [method:PRODUCER_PAUSE, id:8]
    // See related fixed issue:
    // https://github.com/versatica/mediasoup/issues/1374
    const { pipeProducer: pipeVideoProducer } = await ctx.router1.pipeToRouter({
        producerId: ctx.videoProducer.id,
        router: ctx.router2,
    });
    const videoConsumer = await ctx.webRtcTransport2.consume({
        producerId: ctx.videoProducer.id,
        rtpCapabilities: ctx.consumerDeviceCapabilities,
    });
    expect(ctx.videoProducer.paused).toBe(true);
    expect(videoConsumer.producerPaused).toBe(true);
    expect(videoConsumer.paused).toBe(false);
    // NOTE: Let's use a Promise since otherwise there may be race conditions
    // between events and await lines below.
    const promise1 = (0, enhancedEvents_1.enhancedOnce)(videoConsumer, 'producerresume');
    const promise2 = (0, enhancedEvents_1.enhancedOnce)(pipeVideoProducer.observer, 'resume');
    await ctx.videoProducer.resume();
    await Promise.all([promise1, promise2]);
    expect(videoConsumer.producerPaused).toBe(false);
    expect(videoConsumer.paused).toBe(false);
    expect(pipeVideoProducer.paused).toBe(false);
    const promise3 = (0, enhancedEvents_1.enhancedOnce)(videoConsumer, 'producerpause');
    const promise4 = (0, enhancedEvents_1.enhancedOnce)(pipeVideoProducer.observer, 'pause');
    await ctx.videoProducer.pause();
    await Promise.all([promise3, promise4]);
    expect(videoConsumer.producerPaused).toBe(true);
    expect(videoConsumer.paused).toBe(false);
    expect(pipeVideoProducer.paused).toBe(true);
}, 2000);
test('producer.close() is transmitted to pipe Consumer', async () => {
    await ctx.router1.pipeToRouter({
        producerId: ctx.videoProducer.id,
        router: ctx.router2,
    });
    const videoConsumer = await ctx.webRtcTransport2.consume({
        producerId: ctx.videoProducer.id,
        rtpCapabilities: ctx.consumerDeviceCapabilities,
    });
    ctx.videoProducer.close();
    expect(ctx.videoProducer.closed).toBe(true);
    if (!videoConsumer.closed) {
        await (0, enhancedEvents_1.enhancedOnce)(videoConsumer, 'producerclose');
    }
    expect(videoConsumer.closed).toBe(true);
}, 2000);
test('router.pipeToRouter() fails if both Routers belong to the same Worker', async () => {
    const router1bis = await ctx.worker1.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    await expect(ctx.router1.pipeToRouter({
        producerId: ctx.videoProducer.id,
        router: router1bis,
    })).rejects.toThrow(Error);
}, 2000);
test('router.pipeToRouter() succeeds with data', async () => {
    const { pipeDataConsumer, pipeDataProducer } = (await ctx.router1.pipeToRouter({
        dataProducerId: ctx.dataProducer.id,
        router: ctx.router2,
    }));
    const dump1 = await ctx.router1.dump();
    // There should be two Transports in router1:
    // - WebRtcTransport for audioProducer, videoProducer and dataProducer.
    // - PipeTransport between router1 and router2.
    expect(dump1.transportIds.length).toBe(2);
    const dump2 = await ctx.router2.dump();
    // There should be two Transports in router2:
    // - WebRtcTransport for audioConsumer, videoConsumer and dataConsumer.
    // - PipeTransport between router2 and router1.
    expect(dump2.transportIds.length).toBe(2);
    expect(typeof pipeDataConsumer.id).toBe('string');
    expect(pipeDataConsumer.closed).toBe(false);
    expect(pipeDataConsumer.type).toBe('sctp');
    expect(typeof pipeDataConsumer.sctpStreamParameters).toBe('object');
    expect(typeof pipeDataConsumer.sctpStreamParameters?.streamId).toBe('number');
    expect(pipeDataConsumer.sctpStreamParameters?.ordered).toBe(false);
    expect(pipeDataConsumer.sctpStreamParameters?.maxPacketLifeTime).toBe(5000);
    expect(pipeDataConsumer.sctpStreamParameters?.maxRetransmits).toBeUndefined();
    expect(pipeDataConsumer.label).toBe('foo');
    expect(pipeDataConsumer.protocol).toBe('bar');
    expect(pipeDataProducer.id).toBe(ctx.dataProducer.id);
    expect(pipeDataProducer.closed).toBe(false);
    expect(pipeDataProducer.type).toBe('sctp');
    expect(typeof pipeDataProducer.sctpStreamParameters).toBe('object');
    expect(typeof pipeDataProducer.sctpStreamParameters?.streamId).toBe('number');
    expect(pipeDataProducer.sctpStreamParameters?.ordered).toBe(false);
    expect(pipeDataProducer.sctpStreamParameters?.maxPacketLifeTime).toBe(5000);
    expect(pipeDataProducer.sctpStreamParameters?.maxRetransmits).toBeUndefined();
    expect(pipeDataProducer.label).toBe('foo');
    expect(pipeDataProducer.protocol).toBe('bar');
}, 2000);
test('transport.dataConsume() for a pipe DataProducer succeeds', async () => {
    await ctx.router1.pipeToRouter({
        dataProducerId: ctx.dataProducer.id,
        router: ctx.router2,
    });
    const dataConsumer = await ctx.webRtcTransport2.consumeData({
        dataProducerId: ctx.dataProducer.id,
    });
    expect(typeof dataConsumer.id).toBe('string');
    expect(dataConsumer.closed).toBe(false);
    expect(dataConsumer.type).toBe('sctp');
    expect(typeof dataConsumer.sctpStreamParameters).toBe('object');
    expect(typeof dataConsumer.sctpStreamParameters?.streamId).toBe('number');
    expect(dataConsumer.sctpStreamParameters?.ordered).toBe(false);
    expect(dataConsumer.sctpStreamParameters?.maxPacketLifeTime).toBe(5000);
    expect(dataConsumer.sctpStreamParameters?.maxRetransmits).toBeUndefined();
    expect(dataConsumer.label).toBe('foo');
    expect(dataConsumer.protocol).toBe('bar');
}, 2000);
test('dataProducer.close() is transmitted to pipe DataConsumer', async () => {
    await ctx.router1.pipeToRouter({
        dataProducerId: ctx.dataProducer.id,
        router: ctx.router2,
    });
    const dataConsumer = await ctx.webRtcTransport2.consumeData({
        dataProducerId: ctx.dataProducer.id,
    });
    ctx.dataProducer.close();
    expect(ctx.dataProducer.closed).toBe(true);
    if (!dataConsumer.closed) {
        await (0, enhancedEvents_1.enhancedOnce)(dataConsumer, 'dataproducerclose');
    }
    expect(dataConsumer.closed).toBe(true);
}, 2000);
test('router.pipeToRouter() called twice generates a single PipeTransport pair', async () => {
    const routerA = await ctx.worker1.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    const routerB = await ctx.worker2.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    const transportA1 = await routerA.createWebRtcTransport({
        listenIps: ['127.0.0.1'],
    });
    const transportA2 = await routerA.createWebRtcTransport({
        listenIps: ['127.0.0.1'],
    });
    const audioProducerA1 = await transportA1.produce(ctx.audioProducerOptions);
    const audioProducerA2 = await transportA2.produce(ctx.audioProducerOptions);
    await Promise.all([
        routerA.pipeToRouter({
            producerId: audioProducerA1.id,
            router: routerB,
        }),
        routerA.pipeToRouter({
            producerId: audioProducerA2.id,
            router: routerB,
        }),
    ]);
    const dump1 = await routerA.dump();
    // There should be 3 Transports in routerA:
    // - WebRtcTransport for audioProducerA1 and audioProducerA2.
    // - PipeTransport between routerA and routerB.
    expect(dump1.transportIds.length).toBe(3);
    const dump2 = await routerB.dump();
    // There should be 1 Transport in routerB:
    // - PipeTransport between routerA and routerB.
    expect(dump2.transportIds.length).toBe(1);
}, 2000);
test('router.pipeToRouter() called in two Routers passing one to each other as argument generates a single PipeTransport pair', async () => {
    const routerA = await ctx.worker1.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    const routerB = await ctx.worker2.createRouter({
        mediaCodecs: ctx.mediaCodecs,
    });
    const transportA = await routerA.createWebRtcTransport({
        listenIps: ['127.0.0.1'],
    });
    const transportB = await routerB.createWebRtcTransport({
        listenIps: ['127.0.0.1'],
    });
    const audioProducerA = await transportA.produce(ctx.audioProducerOptions);
    const audioProducerB = await transportB.produce(ctx.audioProducerOptions);
    const pipeTransportsA = new Map();
    const pipeTransportsB = new Map();
    routerA.observer.on('newtransport', transport => {
        if (transport.constructor.name !== 'PipeTransport') {
            return;
        }
        pipeTransportsA.set(transport.id, transport);
        transport.observer.on('close', () => pipeTransportsA.delete(transport.id));
    });
    routerB.observer.on('newtransport', transport => {
        if (transport.constructor.name !== 'PipeTransport') {
            return;
        }
        pipeTransportsB.set(transport.id, transport);
        transport.observer.on('close', () => pipeTransportsB.delete(transport.id));
    });
    await Promise.all([
        routerA.pipeToRouter({
            producerId: audioProducerA.id,
            router: routerB,
        }),
        routerB.pipeToRouter({
            producerId: audioProducerB.id,
            router: routerA,
        }),
    ]);
    // There should be a single PipeTransport in each Router and they must be
    // connected.
    expect(pipeTransportsA.size).toBe(1);
    expect(pipeTransportsB.size).toBe(1);
    const pipeTransportA = Array.from(pipeTransportsA.values())[0];
    const pipeTransportB = Array.from(pipeTransportsB.values())[0];
    expect(pipeTransportA.tuple.localPort).toBe(pipeTransportB.tuple.remotePort);
    expect(pipeTransportB.tuple.localPort).toBe(pipeTransportA.tuple.remotePort);
    routerA.close();
    expect(pipeTransportsA.size).toBe(0);
    expect(pipeTransportsB.size).toBe(0);
}, 2000);
