mediasoup
Version:
Cutting Edge WebRTC Video Conferencing
354 lines (353 loc) • 16 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const mediasoup = require("../");
const enhancedEvents_1 = require("../enhancedEvents");
const utils = require("../utils");
const ctx = {
sctpDataProducerOptions: utils.deepFreeze({
sctpStreamParameters: {
streamId: 12345,
ordered: false,
maxPacketLifeTime: 5000,
},
label: 'foo',
protocol: 'bar',
}),
};
beforeEach(async () => {
ctx.worker = await mediasoup.createWorker();
ctx.router = await ctx.worker.createRouter();
ctx.webRtcTransport1 = await ctx.router.createWebRtcTransport({
listenIps: ['127.0.0.1'],
enableSctp: true,
});
ctx.webRtcTransport2 = await ctx.router.createWebRtcTransport({
listenIps: ['127.0.0.1'],
enableSctp: true,
});
ctx.directTransport = await ctx.router.createDirectTransport();
ctx.sctpDataProducer = await ctx.webRtcTransport1.produceData(ctx.sctpDataProducerOptions);
ctx.directDataProducer = await ctx.directTransport.produceData();
});
afterEach(async () => {
ctx.worker?.close();
if (ctx.worker?.subprocessClosed === false) {
await (0, enhancedEvents_1.enhancedOnce)(ctx.worker, 'subprocessclose');
}
});
test('transport.consumeData() succeeds', async () => {
const onObserverNewDataConsumer = jest.fn();
ctx.webRtcTransport2.observer.once('newdataconsumer', onObserverNewDataConsumer);
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
maxPacketLifeTime: 4000,
// Valid values are 0...65535 so others and duplicated ones will be
// discarded.
subchannels: [0, 1, 1, 1, 2, 65535, 65536, 65537, 100],
appData: { baz: 'LOL' },
});
expect(onObserverNewDataConsumer).toHaveBeenCalledTimes(1);
expect(onObserverNewDataConsumer).toHaveBeenCalledWith(dataConsumer);
expect(typeof dataConsumer.id).toBe('string');
expect(dataConsumer.dataProducerId).toBe(ctx.sctpDataProducer.id);
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(4000);
expect(dataConsumer.sctpStreamParameters.maxRetransmits).toBeUndefined();
expect(dataConsumer.label).toBe('foo');
expect(dataConsumer.protocol).toBe('bar');
expect(dataConsumer.paused).toBe(false);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([0, 1, 2, 100, 65535]));
expect(dataConsumer.appData).toEqual({ baz: 'LOL' });
const dump = await ctx.router.dump();
expect(dump.mapDataProducerIdDataConsumerIds).toEqual(expect.arrayContaining([
{ key: ctx.sctpDataProducer.id, values: [dataConsumer.id] },
]));
expect(dump.mapDataConsumerIdDataProducerId).toEqual(expect.arrayContaining([
{ key: dataConsumer.id, value: ctx.sctpDataProducer.id },
]));
await expect(ctx.webRtcTransport2.dump()).resolves.toMatchObject({
id: ctx.webRtcTransport2.id,
dataProducerIds: [],
dataConsumerIds: [dataConsumer.id],
});
}, 2000);
test('dataConsumer.dump() succeeds', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
ordered: true,
// Valid values are 0...65535 so others and duplicated ones will be
// discarded.
subchannels: [0, 1, 1, 1, 2, 65535, 65536, 65537, 100],
appData: { baz: 'LOL' },
});
const dump = await dataConsumer.dump();
expect(dump.id).toBe(dataConsumer.id);
expect(dump.dataProducerId).toBe(dataConsumer.dataProducerId);
expect(dump.type).toBe('sctp');
expect(typeof dump.sctpStreamParameters).toBe('object');
expect(dump.sctpStreamParameters.streamId).toBe(dataConsumer.sctpStreamParameters.streamId);
expect(dump.sctpStreamParameters.ordered).toBe(true);
expect(dump.sctpStreamParameters.maxPacketLifeTime).toBeUndefined();
expect(dump.sctpStreamParameters.maxRetransmits).toBeUndefined();
expect(dump.label).toBe('foo');
expect(dump.protocol).toBe('bar');
expect(dump.paused).toBe(false);
expect(dump.dataProducerPaused).toBe(false);
expect(dump.subchannels).toEqual(expect.arrayContaining([0, 1, 2, 100, 65535]));
}, 2000);
test('dataConsumer.getStats() succeeds', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
await expect(dataConsumer.getStats()).resolves.toMatchObject([
{
type: 'data-consumer',
label: dataConsumer.label,
protocol: dataConsumer.protocol,
messagesSent: 0,
bytesSent: 0,
},
]);
}, 2000);
test('dataConsumer.setSubchannels() succeeds', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
await dataConsumer.setSubchannels([999, 999, 998, 65536]);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([0, 998, 999]));
}, 2000);
test('dataConsumer.addSubchannel() and .removeSubchannel() succeed', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
await dataConsumer.setSubchannels([]);
expect(dataConsumer.subchannels).toEqual([]);
await dataConsumer.addSubchannel(5);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([5]));
await dataConsumer.addSubchannel(10);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([5, 10]));
await dataConsumer.addSubchannel(5);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([5, 10]));
await dataConsumer.removeSubchannel(666);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([5, 10]));
await dataConsumer.removeSubchannel(5);
expect(dataConsumer.subchannels).toEqual(expect.arrayContaining([10]));
await dataConsumer.setSubchannels([]);
expect(dataConsumer.subchannels).toEqual([]);
}, 2000);
test('transport.consumeData() from a direct DataProducer succeeds', async () => {
const onObserverNewDataConsumer = jest.fn();
ctx.webRtcTransport2.observer.once('newdataconsumer', onObserverNewDataConsumer);
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.directDataProducer.id,
});
expect(onObserverNewDataConsumer).toHaveBeenCalledTimes(1);
expect(onObserverNewDataConsumer).toHaveBeenCalledWith(dataConsumer);
expect(typeof dataConsumer.id).toBe('string');
expect(dataConsumer.dataProducerId).toBe(ctx.directDataProducer.id);
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(true);
expect(dataConsumer.sctpStreamParameters.maxPacketLifeTime).toBeUndefined();
expect(dataConsumer.sctpStreamParameters.maxRetransmits).toBeUndefined();
expect(dataConsumer.label).toBe('');
expect(dataConsumer.protocol).toBe('');
expect(dataConsumer.paused).toBe(false);
expect(dataConsumer.subchannels).toEqual([]);
expect(dataConsumer.appData).toEqual({});
const dump = await ctx.router.dump();
expect(dump.mapDataProducerIdDataConsumerIds).toEqual(expect.arrayContaining([
{ key: ctx.directDataProducer.id, values: [dataConsumer.id] },
]));
expect(dump.mapDataConsumerIdDataProducerId).toEqual(expect.arrayContaining([
{ key: dataConsumer.id, value: ctx.directDataProducer.id },
]));
await expect(ctx.webRtcTransport2.dump()).resolves.toMatchObject({
id: ctx.webRtcTransport2.id,
dataProducerIds: [],
dataConsumerIds: [dataConsumer.id],
});
}, 2000);
test('dataConsumer.dump() consuming from a direct DataProducer succeeds', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.directDataProducer.id,
ordered: false,
maxRetransmits: 2,
subchannels: [0, 1],
});
const dump = await dataConsumer.dump();
expect(dump.id).toBe(dataConsumer.id);
expect(dump.dataProducerId).toBe(dataConsumer.dataProducerId);
expect(dump.type).toBe('sctp');
expect(typeof dump.sctpStreamParameters).toBe('object');
expect(dump.sctpStreamParameters.streamId).toBe(dataConsumer.sctpStreamParameters.streamId);
expect(dump.sctpStreamParameters.ordered).toBe(false);
expect(dump.sctpStreamParameters.maxPacketLifeTime).toBeUndefined();
expect(dump.sctpStreamParameters.maxRetransmits).toBe(2);
expect(dump.label).toBe('');
expect(dump.protocol).toBe('');
expect(dump.paused).toBe(false);
expect(dump.dataProducerPaused).toBe(false);
expect(dump.subchannels).toEqual(expect.arrayContaining([0, 1]));
}, 2000);
test('transport.consumeData() on a DirectTransport succeeds', async () => {
const onObserverNewDataConsumer = jest.fn();
ctx.directTransport.observer.once('newdataconsumer', onObserverNewDataConsumer);
const dataConsumer = await ctx.directTransport.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
paused: true,
appData: { hehe: 'HEHE' },
});
expect(onObserverNewDataConsumer).toHaveBeenCalledTimes(1);
expect(onObserverNewDataConsumer).toHaveBeenCalledWith(dataConsumer);
expect(typeof dataConsumer.id).toBe('string');
expect(dataConsumer.dataProducerId).toBe(ctx.sctpDataProducer.id);
expect(dataConsumer.closed).toBe(false);
expect(dataConsumer.type).toBe('direct');
expect(dataConsumer.sctpStreamParameters).toBeUndefined();
expect(dataConsumer.label).toBe('foo');
expect(dataConsumer.protocol).toBe('bar');
expect(dataConsumer.paused).toBe(true);
expect(dataConsumer.appData).toEqual({ hehe: 'HEHE' });
await expect(ctx.directTransport.dump()).resolves.toMatchObject({
id: ctx.directTransport.id,
dataProducerIds: [ctx.directDataProducer.id],
dataConsumerIds: [dataConsumer.id],
});
}, 2000);
test('dataConsumer.dump() on a DirectTransport succeeds', async () => {
const dataConsumer = await ctx.directTransport.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
paused: true,
});
const dump = await dataConsumer.dump();
expect(dump.id).toBe(dataConsumer.id);
expect(dump.dataProducerId).toBe(dataConsumer.dataProducerId);
expect(dump.type).toBe('direct');
expect(dump.sctpStreamParameters).toBeUndefined();
expect(dump.label).toBe('foo');
expect(dump.protocol).toBe('bar');
expect(dump.paused).toBe(true);
expect(dump.subchannels).toEqual([]);
}, 2000);
test('dataConsumer.getStats() on a DirectTransport succeeds', async () => {
const dataConsumer = await ctx.directTransport.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
await expect(dataConsumer.getStats()).resolves.toMatchObject([
{
type: 'data-consumer',
label: dataConsumer.label,
protocol: dataConsumer.protocol,
messagesSent: 0,
bytesSent: 0,
},
]);
}, 2000);
test('dataConsumer.pause() and resume() succeed', async () => {
const onObserverPause = jest.fn();
const onObserverResume = jest.fn();
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
dataConsumer.observer.on('pause', onObserverPause);
dataConsumer.observer.on('resume', onObserverResume);
await dataConsumer.pause();
expect(dataConsumer.paused).toBe(true);
const dump1 = await dataConsumer.dump();
expect(dump1.paused).toBe(true);
await dataConsumer.resume();
expect(dataConsumer.paused).toBe(false);
const dump2 = await dataConsumer.dump();
expect(dump2.paused).toBe(false);
// Even if we don't await for pause()/resume() completion, the observer must
// fire 'pause' and 'resume' events if state was the opposite.
void dataConsumer.pause();
void dataConsumer.resume();
void dataConsumer.pause();
void dataConsumer.pause();
void dataConsumer.pause();
await dataConsumer.resume();
expect(onObserverPause).toHaveBeenCalledTimes(3);
expect(onObserverResume).toHaveBeenCalledTimes(3);
}, 2000);
test('dataProducer.pause() and resume() emit events', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
const promises = [];
const events = [];
dataConsumer.observer.once('resume', () => {
events.push('resume');
});
dataConsumer.observer.once('pause', () => {
events.push('pause');
});
promises.push(ctx.sctpDataProducer.pause());
promises.push(ctx.sctpDataProducer.resume());
await Promise.all(promises);
// Must also wait a bit for the corresponding events in the data consumer.
await new Promise(resolve => setTimeout(resolve, 100));
expect(events).toEqual(['pause', 'resume']);
expect(dataConsumer.paused).toBe(false);
}, 2000);
test('dataConsumer.close() succeeds', async () => {
const onObserverClose = jest.fn();
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
dataConsumer.observer.once('close', onObserverClose);
dataConsumer.close();
expect(onObserverClose).toHaveBeenCalledTimes(1);
expect(dataConsumer.closed).toBe(true);
const dump = await ctx.router.dump();
expect(dump.mapDataProducerIdDataConsumerIds).toEqual(expect.arrayContaining([{ key: ctx.sctpDataProducer.id, values: [] }]));
expect(dump.mapDataConsumerIdDataProducerId).toEqual([]);
await expect(ctx.webRtcTransport2.dump()).resolves.toMatchObject({
id: ctx.webRtcTransport2.id,
dataProducerIds: [],
dataConsumerIds: [],
});
}, 2000);
test('Consumer methods reject if closed', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
dataConsumer.close();
await expect(dataConsumer.dump()).rejects.toThrow(Error);
await expect(dataConsumer.getStats()).rejects.toThrow(Error);
}, 2000);
test('DataConsumer emits "dataproducerclose" if DataProducer is closed', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
const onObserverClose = jest.fn();
dataConsumer.observer.once('close', onObserverClose);
const promise = (0, enhancedEvents_1.enhancedOnce)(dataConsumer, 'dataproducerclose');
ctx.sctpDataProducer.close();
await promise;
expect(onObserverClose).toHaveBeenCalledTimes(1);
expect(dataConsumer.closed).toBe(true);
}, 2000);
test('DataConsumer emits "transportclose" if Transport is closed', async () => {
const dataConsumer = await ctx.webRtcTransport2.consumeData({
dataProducerId: ctx.sctpDataProducer.id,
});
const onObserverClose = jest.fn();
dataConsumer.observer.once('close', onObserverClose);
const promise = (0, enhancedEvents_1.enhancedOnce)(dataConsumer, 'transportclose');
ctx.webRtcTransport2.close();
await promise;
expect(onObserverClose).toHaveBeenCalledTimes(1);
expect(dataConsumer.closed).toBe(true);
await expect(ctx.router.dump()).resolves.toMatchObject({
mapDataProducerIdDataConsumerIds: {},
mapDataConsumerIdDataProducerId: {},
});
}, 2000);