UNPKG

mediasoup

Version:

Cutting Edge WebRTC Video Conferencing

305 lines (304 loc) 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const mediasoup = require("../"); const enhancedEvents_1 = require("../enhancedEvents"); const ctx = {}; beforeEach(async () => { ctx.worker = await mediasoup.createWorker(); ctx.router = await ctx.worker.createRouter(); }); afterEach(async () => { ctx.worker?.close(); if (ctx.worker?.subprocessClosed === false) { await (0, enhancedEvents_1.enhancedOnce)(ctx.worker, 'subprocessclose'); } }); test('router.createDirectTransport() succeeds', async () => { const onObserverNewTransport = jest.fn(); ctx.router.observer.once('newtransport', onObserverNewTransport); const directTransport = await ctx.router.createDirectTransport({ maxMessageSize: 1024, appData: { foo: 'bar' }, }); await expect(ctx.router.dump()).resolves.toMatchObject({ transportIds: [directTransport.id], }); expect(onObserverNewTransport).toHaveBeenCalledTimes(1); expect(onObserverNewTransport).toHaveBeenCalledWith(directTransport); expect(typeof directTransport.id).toBe('string'); expect(directTransport.closed).toBe(false); expect(directTransport.appData).toEqual({ foo: 'bar' }); const dump = await directTransport.dump(); expect(dump.id).toBe(directTransport.id); expect(dump.direct).toBe(true); expect(dump.producerIds).toEqual([]); expect(dump.consumerIds).toEqual([]); expect(dump.dataProducerIds).toEqual([]); expect(dump.dataConsumerIds).toEqual([]); expect(dump.recvRtpHeaderExtensions).toBeDefined(); expect(typeof dump.rtpListener).toBe('object'); directTransport.close(); expect(directTransport.closed).toBe(true); }, 2000); test('router.createDirectTransport() with wrong arguments rejects with TypeError', async () => { await expect( // @ts-ignore ctx.router.createDirectTransport({ maxMessageSize: 'foo' })).rejects.toThrow(TypeError); await expect(ctx.router.createDirectTransport({ maxMessageSize: -2000 })).rejects.toThrow(TypeError); }, 2000); test('directTransport.getStats() succeeds', async () => { const directTransport = await ctx.router.createDirectTransport(); const stats = await directTransport.getStats(); expect(Array.isArray(stats)).toBe(true); expect(stats.length).toBe(1); expect(stats[0].type).toBe('direct-transport'); expect(stats[0].transportId).toBe(directTransport.id); expect(typeof stats[0].timestamp).toBe('number'); expect(stats[0].bytesReceived).toBe(0); expect(stats[0].recvBitrate).toBe(0); expect(stats[0].bytesSent).toBe(0); expect(stats[0].sendBitrate).toBe(0); expect(stats[0].rtpBytesReceived).toBe(0); expect(stats[0].rtpRecvBitrate).toBe(0); expect(stats[0].rtpBytesSent).toBe(0); expect(stats[0].rtpSendBitrate).toBe(0); expect(stats[0].rtxBytesReceived).toBe(0); expect(stats[0].rtxRecvBitrate).toBe(0); expect(stats[0].rtxBytesSent).toBe(0); expect(stats[0].rtxSendBitrate).toBe(0); expect(stats[0].probationBytesSent).toBe(0); expect(stats[0].probationSendBitrate).toBe(0); }, 2000); test('directTransport.connect() succeeds', async () => { const directTransport = await ctx.router.createDirectTransport(); await expect(directTransport.connect()).resolves.toBeUndefined(); }, 2000); test('dataProducer.send() succeeds', async () => { const directTransport = await ctx.router.createDirectTransport(); const dataProducer = await directTransport.produceData({ label: 'foo', protocol: 'bar', appData: { foo: 'bar' }, }); const dataConsumer = await directTransport.consumeData({ dataProducerId: dataProducer.id, }); const numMessages = 200; const pauseSendingAtMessage = 10; const resumeSendingAtMessage = 20; const pauseReceivingAtMessage = 40; const resumeReceivingAtMessage = 60; const expectedReceivedNumMessages = numMessages - (resumeSendingAtMessage - pauseSendingAtMessage) - (resumeReceivingAtMessage - pauseReceivingAtMessage); let sentMessageBytes = 0; let effectivelySentMessageBytes = 0; let recvMessageBytes = 0; let numSentMessages = 0; let numReceivedMessages = 0; async function sendNextMessage() { const id = ++numSentMessages; let message; if (id === pauseSendingAtMessage) { await dataProducer.pause(); } else if (id === resumeSendingAtMessage) { await dataProducer.resume(); } else if (id === pauseReceivingAtMessage) { await dataConsumer.pause(); } else if (id === resumeReceivingAtMessage) { await dataConsumer.resume(); } // Send string (WebRTC DataChannel string). if (id < numMessages / 2) { message = String(id); } // Send string (WebRTC DataChannel binary). else { message = Buffer.from(String(id)); } dataProducer.send(message); const messageSize = Buffer.from(message).byteLength; sentMessageBytes += messageSize; if (!dataProducer.paused && !dataConsumer.paused) { effectivelySentMessageBytes += messageSize; } if (id < numMessages) { void sendNextMessage(); } } await new Promise((resolve, reject) => { dataProducer.on('listenererror', (eventName, error) => { reject(new Error(`dataProducer 'listenererror' [eventName:${eventName}]: ${error}`)); }); dataConsumer.on('listenererror', (eventName, error) => { reject(new Error(`dataConsumer 'listenererror' [eventName:${eventName}]: ${error}`)); }); dataConsumer.on('message', (message, ppid) => { ++numReceivedMessages; // message is always a Buffer. recvMessageBytes += message.byteLength; const id = Number(message.toString('utf8')); if (id === numMessages) { resolve(); } // PPID of WebRTC DataChannel string. else if (id < numMessages / 2 && ppid !== 51) { reject(new Error(`ppid in message with id ${id} should be 51 but it is ${ppid}`)); } // PPID of WebRTC DataChannel binary. else if (id > numMessages / 2 && ppid !== 53) { reject(new Error(`ppid in message with id ${id} should be 53 but it is ${ppid}`)); } }); void sendNextMessage(); }); expect(numSentMessages).toBe(numMessages); expect(numReceivedMessages).toBe(expectedReceivedNumMessages); expect(recvMessageBytes).toBe(effectivelySentMessageBytes); await expect(dataProducer.getStats()).resolves.toMatchObject([ { type: 'data-producer', label: dataProducer.label, protocol: dataProducer.protocol, messagesReceived: numMessages, bytesReceived: sentMessageBytes, }, ]); await expect(dataConsumer.getStats()).resolves.toMatchObject([ { type: 'data-consumer', label: dataConsumer.label, protocol: dataConsumer.protocol, messagesSent: expectedReceivedNumMessages, bytesSent: recvMessageBytes, }, ]); }, 5000); test('dataProducer.send() with subchannels succeeds', async () => { const directTransport = await ctx.router.createDirectTransport(); const dataProducer = await directTransport.produceData(); const dataConsumer1 = await directTransport.consumeData({ dataProducerId: dataProducer.id, subchannels: [1, 11, 666], }); const dataConsumer2 = await directTransport.consumeData({ dataProducerId: dataProducer.id, subchannels: [2, 22, 666], }); const expectedReceivedNumMessages1 = 7; const expectedReceivedNumMessages2 = 5; const receivedMessages1 = []; const receivedMessages2 = []; await new Promise(resolve => { // Must be received by dataConsumer1 and dataConsumer2. dataProducer.send('both', /* ppid */ undefined, /* subchannels */ undefined, /* requiredSubchannel */ undefined); // Must be received by dataConsumer1 and dataConsumer2. dataProducer.send('both', /* ppid */ undefined, /* subchannels */ [1, 2], /* requiredSubchannel */ undefined); // Must be received by dataConsumer1 and dataConsumer2. dataProducer.send('both', /* ppid */ undefined, /* subchannels */ [11, 22, 33], /* requiredSubchannel */ 666); // Must not be received by neither dataConsumer1 nor dataConsumer2. dataProducer.send('none', /* ppid */ undefined, /* subchannels */ [3], /* requiredSubchannel */ 666); // Must not be received by neither dataConsumer1 nor dataConsumer2. dataProducer.send('none', /* ppid */ undefined, /* subchannels */ [666], /* requiredSubchannel */ 3); // Must be received by dataConsumer1. dataProducer.send('dc1', /* ppid */ undefined, /* subchannels */ [1], /* requiredSubchannel */ undefined); // Must be received by dataConsumer1. dataProducer.send('dc1', /* ppid */ undefined, /* subchannels */ [11], /* requiredSubchannel */ 1); // Must be received by dataConsumer1. dataProducer.send('dc1', /* ppid */ undefined, /* subchannels */ [666], /* requiredSubchannel */ 11); // Must be received by dataConsumer2. dataProducer.send('dc2', /* ppid */ undefined, /* subchannels */ [666], /* requiredSubchannel */ 2); // Make dataConsumer2 also subscribe to subchannel 1. // NOTE: No need to await for this call. void dataConsumer2.setSubchannels([...dataConsumer2.subchannels, 1]); // Must be received by dataConsumer1 and dataConsumer2. dataProducer.send('both', /* ppid */ undefined, /* subchannels */ [1], /* requiredSubchannel */ 666); dataConsumer1.on('message', message => { receivedMessages1.push(message.toString('utf8')); if (receivedMessages1.length === expectedReceivedNumMessages1 && receivedMessages2.length === expectedReceivedNumMessages2) { resolve(); } }); dataConsumer2.on('message', message => { receivedMessages2.push(message.toString('utf8')); if (receivedMessages1.length === expectedReceivedNumMessages1 && receivedMessages2.length === expectedReceivedNumMessages2) { resolve(); } }); }); expect(receivedMessages1.length).toBe(expectedReceivedNumMessages1); expect(receivedMessages2.length).toBe(expectedReceivedNumMessages2); for (const message of receivedMessages1) { expect(['both', 'dc1'].includes(message)).toBe(true); expect(['dc2'].includes(message)).toBe(false); } for (const message of receivedMessages2) { expect(['both', 'dc2'].includes(message)).toBe(true); expect(['dc1'].includes(message)).toBe(false); } }, 5000); test('DirectTransport methods reject if closed', async () => { const directTransport = await ctx.router.createDirectTransport(); const onObserverClose = jest.fn(); directTransport.observer.once('close', onObserverClose); directTransport.close(); expect(onObserverClose).toHaveBeenCalledTimes(1); expect(directTransport.closed).toBe(true); await expect(directTransport.dump()).rejects.toThrow(Error); await expect(directTransport.getStats()).rejects.toThrow(Error); }, 2000); test('DirectTransport emits "routerclose" if Router is closed', async () => { const directTransport = await ctx.router.createDirectTransport(); const onObserverClose = jest.fn(); directTransport.observer.once('close', onObserverClose); const promise = (0, enhancedEvents_1.enhancedOnce)(directTransport, 'routerclose'); ctx.router.close(); await promise; expect(onObserverClose).toHaveBeenCalledTimes(1); expect(directTransport.closed).toBe(true); }, 2000); test('DirectTransport emits "routerclose" if Worker is closed', async () => { const directTransport = await ctx.router.createDirectTransport(); const onObserverClose = jest.fn(); directTransport.observer.once('close', onObserverClose); const promise = (0, enhancedEvents_1.enhancedOnce)(directTransport, 'routerclose'); ctx.worker.close(); await promise; expect(onObserverClose).toHaveBeenCalledTimes(1); expect(directTransport.closed).toBe(true); }, 2000);