twilio-video 2.22.2 → 2.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -1
- package/README.md +1 -1
- package/dist/twilio-video.js +767 -204
- package/dist/twilio-video.min.js +21 -20
- package/es5/connect.js +1 -1
- package/es5/connect.js.map +1 -1
- package/es5/createlocaltrack.js +38 -8
- package/es5/createlocaltrack.js.map +1 -1
- package/es5/createlocaltracks.js +36 -11
- package/es5/createlocaltracks.js.map +1 -1
- package/es5/index.js +1 -1
- package/es5/index.js.map +1 -1
- package/es5/localparticipant.js +10 -1
- package/es5/localparticipant.js.map +1 -1
- package/es5/media/track/localaudiotrack.js +30 -1
- package/es5/media/track/localaudiotrack.js.map +1 -1
- package/es5/media/track/localvideotrack.js +1 -1
- package/es5/media/track/localvideotrack.js.map +1 -1
- package/es5/media/track/noisecancellationimpl.js +228 -0
- package/es5/media/track/noisecancellationimpl.js.map +1 -0
- package/es5/noisecancellationadapter.js +155 -0
- package/es5/noisecancellationadapter.js.map +1 -0
- package/es5/preflight/preflighttest.js +2 -2
- package/es5/preflight/preflighttest.js.map +1 -1
- package/es5/room.js +12 -0
- package/es5/room.js.map +1 -1
- package/es5/signaling/localparticipant.js +4 -2
- package/es5/signaling/localparticipant.js.map +1 -1
- package/es5/signaling/v2/cancelableroomsignalingpromise.js +0 -2
- package/es5/signaling/v2/cancelableroomsignalingpromise.js.map +1 -1
- package/es5/signaling/v2/localparticipant.js +16 -4
- package/es5/signaling/v2/localparticipant.js.map +1 -1
- package/es5/signaling/v2/localtrackpublication.js +12 -3
- package/es5/signaling/v2/localtrackpublication.js.map +1 -1
- package/es5/signaling/v2/room.js +8 -1
- package/es5/signaling/v2/room.js.map +1 -1
- package/es5/vendor/dynamicimport.js +44 -0
- package/es5/vendor/dynamicimport.js.map +1 -0
- package/karma/makeconf.js +11 -1
- package/lib/connect.js +2 -1
- package/lib/createlocaltrack.js +41 -8
- package/lib/createlocaltracks.ts +35 -11
- package/lib/index.ts +5 -3
- package/lib/localparticipant.js +10 -1
- package/lib/media/track/localaudiotrack.js +30 -0
- package/lib/media/track/localvideotrack.js +1 -1
- package/lib/media/track/noisecancellationimpl.ts +176 -0
- package/lib/noisecancellationadapter.ts +134 -0
- package/lib/preflight/preflighttest.ts +1 -1
- package/lib/room.js +13 -0
- package/lib/signaling/localparticipant.js +3 -2
- package/lib/signaling/v2/cancelableroomsignalingpromise.js +0 -3
- package/lib/signaling/v2/localparticipant.js +17 -4
- package/lib/signaling/v2/localtrackpublication.js +14 -3
- package/lib/signaling/v2/room.js +13 -1
- package/lib/vendor/dynamicimport.js +44 -0
- package/package.json +2 -2
- package/tsconfig.json +1 -0
- package/tsdef/AudioProcessor.d.ts +65 -0
- package/tsdef/LocalAudioTrack.d.ts +2 -0
- package/tsdef/LocalTrackOptions.d.ts +1 -1
- package/tsdef/index.d.ts +7 -2
- package/tsdef/types.d.ts +23 -2
|
@@ -15,6 +15,9 @@ const LocalMediaAudioTrack = mixinLocalMediaTrack(AudioTrack);
|
|
|
15
15
|
* @property {Track.ID} id - The {@link LocalAudioTrack}'s ID
|
|
16
16
|
* @property {boolean} isStopped - Whether or not the {@link LocalAudioTrack} is
|
|
17
17
|
* stopped
|
|
18
|
+
* @property {NoiseCancellation?} noiseCancellation - When a LocalAudioTrack is created
|
|
19
|
+
* with {@link NoiseCancellationOptions}, this property provides interface
|
|
20
|
+
* to enable or disable the noise cancellation at runtime.
|
|
18
21
|
* @emits LocalAudioTrack#disabled
|
|
19
22
|
* @emits LocalAudioTrack#enabled
|
|
20
23
|
* @emits LocalAudioTrack#started
|
|
@@ -27,7 +30,16 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
|
|
|
27
30
|
* @param {LocalTrackOptions} [options] - {@link LocalTrack} options
|
|
28
31
|
*/
|
|
29
32
|
constructor(mediaStreamTrack, options) {
|
|
33
|
+
const noiseCancellation = options?.noiseCancellation || null;
|
|
30
34
|
super(mediaStreamTrack, options);
|
|
35
|
+
|
|
36
|
+
Object.defineProperties(this, {
|
|
37
|
+
noiseCancellation: {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
value: noiseCancellation,
|
|
40
|
+
writable: false
|
|
41
|
+
},
|
|
42
|
+
});
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
toString() {
|
|
@@ -47,6 +59,20 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
|
|
|
47
59
|
return super._end.apply(this, arguments);
|
|
48
60
|
}
|
|
49
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
_reacquireTrack(constraints) {
|
|
66
|
+
this._log.debug('_reacquireTrack: ', constraints);
|
|
67
|
+
if (this.noiseCancellation) {
|
|
68
|
+
return this.noiseCancellation.reacquireTrack(() => {
|
|
69
|
+
return super._reacquireTrack.call(this, constraints);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return super._reacquireTrack.call(this, constraints);
|
|
74
|
+
}
|
|
75
|
+
|
|
50
76
|
/**
|
|
51
77
|
* Disable the {@link LocalAudioTrack}. This is effectively "mute".
|
|
52
78
|
* @returns {this}
|
|
@@ -118,6 +144,9 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
|
|
|
118
144
|
* @fires LocalAudioTrack#stopped
|
|
119
145
|
*/
|
|
120
146
|
stop() {
|
|
147
|
+
if (this.noiseCancellation) {
|
|
148
|
+
this.noiseCancellation.stop();
|
|
149
|
+
}
|
|
121
150
|
return super.stop.apply(this, arguments);
|
|
122
151
|
}
|
|
123
152
|
}
|
|
@@ -151,3 +180,4 @@ class LocalAudioTrack extends LocalMediaAudioTrack {
|
|
|
151
180
|
*/
|
|
152
181
|
|
|
153
182
|
module.exports = LocalAudioTrack;
|
|
183
|
+
|
|
@@ -312,7 +312,7 @@ function workaroundSilentLocalVideo(localVideoTrack, doc) {
|
|
|
312
312
|
}).finally(() => {
|
|
313
313
|
// If silent frames were not detected, then pause the dummy element again.
|
|
314
314
|
el = localVideoTrack._dummyEl;
|
|
315
|
-
if (!el.paused) {
|
|
315
|
+
if (el && !el.paused) {
|
|
316
316
|
el.pause();
|
|
317
317
|
}
|
|
318
318
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { NoiseCancellation, NoiseCancellationOptions, NoiseCancellationVendor } from '../../../tsdef/types';
|
|
4
|
+
import { AudioProcessor } from '../../../tsdef/AudioProcessor';
|
|
5
|
+
import { createNoiseCancellationAudioProcessor } from '../../noisecancellationadapter';
|
|
6
|
+
const Log = require('../../util/log');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* {@link NoiseCancellation} interface provides methods to control noise cancellation at runtime. This interface is exposed
|
|
10
|
+
* on {@link LocalAudioTrack} property `noiseCancellation`. It is available only when {@link NoiseCancellationOptions} are
|
|
11
|
+
* specified when creating a {@link LocalAudioTrack}, and the plugin is successfully loaded.
|
|
12
|
+
* @alias NoiseCancellation
|
|
13
|
+
* @interface
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const { connect, createLocalAudioTrack } = require('twilio-video');
|
|
17
|
+
*
|
|
18
|
+
* // Create a LocalAudioTrack with Krisp noise cancellation enabled.
|
|
19
|
+
* const localAudioTrack = await createLocalAudioTrack({
|
|
20
|
+
* noiseCancellationOptions: {
|
|
21
|
+
* sdkAssetsPath: 'path/to/hosted/twilio/krisp/audio/plugin/1.0.0/dist',
|
|
22
|
+
* vendor: 'krisp'
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* if (!localAudioTrack.noiseCancellation) {
|
|
27
|
+
* // If the Krisp audio plugin fails to load, then a warning message will be logged
|
|
28
|
+
* // in the browser console, and the "noiseCancellation" property will be set to null.
|
|
29
|
+
* // You can still use the LocalAudioTrack to join a Room. However, it will use the
|
|
30
|
+
* // browser's noise suppression instead of the Krisp noise cancellation. Make sure
|
|
31
|
+
* // the "sdkAssetsPath" provided in "noiseCancellationOptions" points to the correct
|
|
32
|
+
* // hosted path of the plugin assets.
|
|
33
|
+
* } else {
|
|
34
|
+
* // Join a Room with the LocalAudioTrack.
|
|
35
|
+
* const room = await connect('token', {
|
|
36
|
+
* name: 'my-cool-room',
|
|
37
|
+
* tracks: [localAudioTrack]
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* if (!localAudioTrack.noiseCancellation.isEnabled) {
|
|
41
|
+
* // Krisp noise cancellation is permanently disabled in Peer-to-Peer and Go Rooms.
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* //
|
|
46
|
+
* // Enable/disable noise cancellation.
|
|
47
|
+
* // @param {boolean} enable - whether noise cancellation should be enabled
|
|
48
|
+
* //
|
|
49
|
+
* function setNoiseCancellation(enable) {
|
|
50
|
+
* const { noiseCancellation } = localAudioTrack;
|
|
51
|
+
* if (noiseCancellation) {
|
|
52
|
+
* if (enable) {
|
|
53
|
+
* // If enabled, then the LocalAudioTrack will use the Krisp noise
|
|
54
|
+
* // cancellation instead of the browser's noise suppression.
|
|
55
|
+
* noiseCancellation.enable();
|
|
56
|
+
* } else {
|
|
57
|
+
* // If disabled, then the LocalAudioTrack will use the browser's
|
|
58
|
+
* // noise suppression instead of the Krisp noise cancellation.
|
|
59
|
+
* noiseCancellation.disable();
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
*/
|
|
64
|
+
export class NoiseCancellationImpl implements NoiseCancellation {
|
|
65
|
+
private _processor: AudioProcessor;
|
|
66
|
+
private _sourceTrack: MediaStreamTrack;
|
|
67
|
+
private _disabledPermanent: boolean;
|
|
68
|
+
|
|
69
|
+
constructor(processor: AudioProcessor, originalTrack: MediaStreamTrack) {
|
|
70
|
+
this._processor = processor;
|
|
71
|
+
this._sourceTrack = originalTrack;
|
|
72
|
+
this._disabledPermanent = false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Name of the noise cancellation vendor.
|
|
77
|
+
* @type {NoiseCancellationVendor}
|
|
78
|
+
*/
|
|
79
|
+
get vendor(): NoiseCancellationVendor {
|
|
80
|
+
return this._processor.vendor;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The underlying MediaStreamTrack of the {@link LocalAudioTrack}.
|
|
85
|
+
* @type {MediaStreamTrack}
|
|
86
|
+
*/
|
|
87
|
+
get sourceTrack(): MediaStreamTrack {
|
|
88
|
+
return this._sourceTrack;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether noise cancellation is enabled.
|
|
93
|
+
* @type {boolean}
|
|
94
|
+
*/
|
|
95
|
+
get isEnabled(): boolean {
|
|
96
|
+
return this._processor.isEnabled();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Enable noise cancellation.
|
|
101
|
+
* @returns {Promise<void>} Promise that resolves when the operation is complete
|
|
102
|
+
* @throws {Error} Throws an error if noise cancellation is disabled permanently
|
|
103
|
+
* for the {@link LocalAudioTrack}
|
|
104
|
+
*/
|
|
105
|
+
enable() : Promise<void> {
|
|
106
|
+
if (this._disabledPermanent) {
|
|
107
|
+
throw new Error(`${this.vendor} noise cancellation is disabled permanently for this track`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this._processor.enable();
|
|
111
|
+
return Promise.resolve();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Disable noise cancellation.
|
|
116
|
+
* @returns {Promise<void>} Promise that resolves when the operation is complete
|
|
117
|
+
*/
|
|
118
|
+
disable() : Promise<void> {
|
|
119
|
+
this._processor.disable();
|
|
120
|
+
return Promise.resolve();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
async reacquireTrack(reacquire: () => Promise<MediaStreamTrack>) : Promise<MediaStreamTrack> {
|
|
127
|
+
const processorWasEnabled = this._processor.isEnabled();
|
|
128
|
+
this._processor.disconnect();
|
|
129
|
+
|
|
130
|
+
const track = await reacquire();
|
|
131
|
+
this._sourceTrack = track;
|
|
132
|
+
|
|
133
|
+
const processedTrack = await this._processor.connect(track);
|
|
134
|
+
if (processorWasEnabled) {
|
|
135
|
+
this._processor.enable();
|
|
136
|
+
} else {
|
|
137
|
+
this._processor.disable();
|
|
138
|
+
}
|
|
139
|
+
return processedTrack;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
disablePermanently(): Promise<void> {
|
|
146
|
+
this._disabledPermanent = true;
|
|
147
|
+
return this.disable();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
stop(): void {
|
|
155
|
+
this._processor.disconnect();
|
|
156
|
+
this._sourceTrack.stop();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
export async function applyNoiseCancellation(
|
|
162
|
+
mediaStreamTrack: MediaStreamTrack,
|
|
163
|
+
noiseCancellationOptions: NoiseCancellationOptions,
|
|
164
|
+
log: typeof Log
|
|
165
|
+
) : Promise<{ cleanTrack: MediaStreamTrack, noiseCancellation?: NoiseCancellation }> {
|
|
166
|
+
try {
|
|
167
|
+
const processor = await createNoiseCancellationAudioProcessor(noiseCancellationOptions, log);
|
|
168
|
+
const cleanTrack = processor.connect(mediaStreamTrack);
|
|
169
|
+
const noiseCancellation = new NoiseCancellationImpl(processor, mediaStreamTrack);
|
|
170
|
+
return { cleanTrack, noiseCancellation };
|
|
171
|
+
} catch (ex) {
|
|
172
|
+
// in case of failures to load noise cancellation library just return original media stream.
|
|
173
|
+
log.warn(`Failed to create noise cancellation. Returning normal audio track: ${ex}`);
|
|
174
|
+
return { cleanTrack: mediaStreamTrack };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
'use strict';
|
|
3
|
+
import { AudioProcessor } from '../tsdef/AudioProcessor';
|
|
4
|
+
import { NoiseCancellationOptions } from '../tsdef/types';
|
|
5
|
+
const Log = require('./util/log');
|
|
6
|
+
|
|
7
|
+
const dynamicImport = require('./vendor/dynamicimport');
|
|
8
|
+
|
|
9
|
+
const PLUGIN_CONFIG = {
|
|
10
|
+
krisp: {
|
|
11
|
+
supportedVersion: '1.0.0',
|
|
12
|
+
pluginFile: 'krispsdk.mjs'
|
|
13
|
+
},
|
|
14
|
+
rnnoise: {
|
|
15
|
+
supportedVersion: '0.6.0',
|
|
16
|
+
pluginFile: 'rnnoise_sdk.mjs'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
// AudioProcessor assumes following interface from the Plugin
|
|
20
|
+
interface NoiseCancellationPlugin {
|
|
21
|
+
init(options: { rootDir: string }): Promise<void>;
|
|
22
|
+
isInitialized(): boolean;
|
|
23
|
+
isConnected(): boolean;
|
|
24
|
+
isEnabled(): boolean
|
|
25
|
+
connect(input: MediaStream): MediaStream;
|
|
26
|
+
disconnect(): void;
|
|
27
|
+
enable(): void;
|
|
28
|
+
disable(): void;
|
|
29
|
+
destroy(): void;
|
|
30
|
+
setLogging(enable: boolean):void;
|
|
31
|
+
isSupported(audioContext: AudioContext):boolean;
|
|
32
|
+
getVersion(): string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ensureVersionSupported = ({ supportedVersion, plugin, log }: {supportedVersion: string, plugin: NoiseCancellationPlugin, log: typeof Log}) : void => {
|
|
36
|
+
if (!plugin.getVersion || !plugin.isSupported) {
|
|
37
|
+
throw new Error('Plugin does not export getVersion/isSupported api. Are you using old version of the plugin ?');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const pluginVersion = plugin.getVersion();
|
|
41
|
+
log.debug(`Plugin Version = ${pluginVersion}`);
|
|
42
|
+
const supportedVersions = supportedVersion.split('.').map(version => Number(version));
|
|
43
|
+
const pluginVersions = pluginVersion.split('.').map(version => Number(version));
|
|
44
|
+
if (supportedVersions.length !== 3 || pluginVersions.length !== 3) {
|
|
45
|
+
throw new Error(`Unsupported Plugin version format: ${supportedVersion}, ${pluginVersion}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (supportedVersions[0] !== pluginVersions[0]) {
|
|
49
|
+
throw new Error(`Major version mismatch: [Plugin version ${pluginVersion}], [Supported Version ${supportedVersion}]`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (pluginVersions[1] < supportedVersions[1]) {
|
|
53
|
+
throw new Error(`Minor version mismatch: [Plugin version ${pluginVersion}] < [Supported Version ${supportedVersion}]`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const tempContext = new AudioContext();
|
|
57
|
+
const isSupported = plugin.isSupported(tempContext);
|
|
58
|
+
tempContext.close();
|
|
59
|
+
if (!isSupported) {
|
|
60
|
+
throw new Error('Noise Cancellation plugin is not supported on your browser');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
let audioProcessors = new Map<string, AudioProcessor>();
|
|
65
|
+
export async function createNoiseCancellationAudioProcessor(
|
|
66
|
+
noiseCancellationOptions: NoiseCancellationOptions,
|
|
67
|
+
log: typeof Log
|
|
68
|
+
) : Promise<AudioProcessor> {
|
|
69
|
+
let audioProcessor = audioProcessors.get(noiseCancellationOptions.vendor);
|
|
70
|
+
if (!audioProcessor) {
|
|
71
|
+
let pluginConfig = PLUGIN_CONFIG[noiseCancellationOptions.vendor];
|
|
72
|
+
if (!pluginConfig) {
|
|
73
|
+
throw new Error(`Unsupported NoiseCancellationOptions.vendor: ${noiseCancellationOptions.vendor}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { supportedVersion, pluginFile } = pluginConfig;
|
|
77
|
+
const rootDir = noiseCancellationOptions.sdkAssetsPath;
|
|
78
|
+
const sdkFilePath = `${rootDir}/${pluginFile}`;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
log.debug('loading noise cancellation sdk: ', sdkFilePath);
|
|
82
|
+
const dynamicModule = await dynamicImport(sdkFilePath);
|
|
83
|
+
log.debug('Loaded noise cancellation sdk:', dynamicModule);
|
|
84
|
+
|
|
85
|
+
const plugin = dynamicModule.default as NoiseCancellationPlugin;
|
|
86
|
+
ensureVersionSupported({
|
|
87
|
+
supportedVersion,
|
|
88
|
+
plugin,
|
|
89
|
+
log
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!plugin.isInitialized()) {
|
|
93
|
+
log.debug('initializing noise cancellation sdk: ', rootDir);
|
|
94
|
+
await plugin.init({ rootDir });
|
|
95
|
+
log.debug('noise cancellation sdk initialized!');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
audioProcessor = {
|
|
99
|
+
vendor: noiseCancellationOptions.vendor,
|
|
100
|
+
isInitialized: () => plugin.isInitialized(),
|
|
101
|
+
isConnected: () => plugin.isConnected(),
|
|
102
|
+
isEnabled: () => plugin.isEnabled(),
|
|
103
|
+
disconnect: () => plugin.disconnect(),
|
|
104
|
+
enable: () => plugin.enable(),
|
|
105
|
+
disable: () => plugin.disable(),
|
|
106
|
+
destroy: () => plugin.destroy(),
|
|
107
|
+
setLogging: (enable: boolean) => plugin.setLogging(enable),
|
|
108
|
+
connect: (sourceTrack: MediaStreamTrack) => {
|
|
109
|
+
log.debug('connect: ', sourceTrack.id);
|
|
110
|
+
if (plugin.isConnected()) {
|
|
111
|
+
plugin.disconnect();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const mediaStream = plugin.connect(new MediaStream([sourceTrack]));
|
|
115
|
+
if (!mediaStream) {
|
|
116
|
+
throw new Error('Error connecting with noise cancellation sdk');
|
|
117
|
+
}
|
|
118
|
+
const cleanTrack = mediaStream.getAudioTracks()[0];
|
|
119
|
+
if (!cleanTrack) {
|
|
120
|
+
throw new Error('Error getting clean track from noise cancellation sdk');
|
|
121
|
+
}
|
|
122
|
+
plugin.enable();
|
|
123
|
+
return cleanTrack;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
audioProcessors.set(noiseCancellationOptions.vendor, audioProcessor);
|
|
127
|
+
|
|
128
|
+
} catch (er) {
|
|
129
|
+
log.error(`Error loading noise cancellation sdk:${sdkFilePath}`, er);
|
|
130
|
+
throw er;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return audioProcessor;
|
|
134
|
+
}
|
|
@@ -9,7 +9,7 @@ import { getTurnCredentials } from './getturncredentials';
|
|
|
9
9
|
import { makeStat } from './makestat';
|
|
10
10
|
import { syntheticAudio } from './syntheticaudio';
|
|
11
11
|
import { syntheticVideo } from './syntheticvideo';
|
|
12
|
-
import { waitForSometime } from '../util';
|
|
12
|
+
import { waitForSometime } from '../util/index';
|
|
13
13
|
|
|
14
14
|
const { WS_SERVER } = require('../util/constants');
|
|
15
15
|
const Log = require('../util/log');
|
package/lib/room.js
CHANGED
|
@@ -130,6 +130,7 @@ class Room extends EventEmitter {
|
|
|
130
130
|
handleLocalParticipantEvents(this, localParticipant);
|
|
131
131
|
handleRecordingEvents(this, signaling.recording);
|
|
132
132
|
handleSignalingEvents(this, signaling);
|
|
133
|
+
verifyNoiseCancellation(this);
|
|
133
134
|
|
|
134
135
|
log.info('Created a new Room:', this.name);
|
|
135
136
|
log.debug('Initial RemoteParticipants:', Array.from(this._participants.values()));
|
|
@@ -139,6 +140,7 @@ class Room extends EventEmitter {
|
|
|
139
140
|
return `[Room #${this._instanceId}: ${this.sid}]`;
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
|
|
142
144
|
/**
|
|
143
145
|
* Disconnect from the {@link Room}.
|
|
144
146
|
* @returns {this}
|
|
@@ -171,6 +173,17 @@ class Room extends EventEmitter {
|
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
function verifyNoiseCancellation(room) {
|
|
177
|
+
const allowedAudioProcessors = room.localParticipant._signaling.audioProcessors;
|
|
178
|
+
room.localParticipant.audioTracks.forEach(({ track }) => {
|
|
179
|
+
const noiseCancellation = track.noiseCancellation;
|
|
180
|
+
if (noiseCancellation && !allowedAudioProcessors.includes(noiseCancellation.vendor)) {
|
|
181
|
+
room._log.warn(`${noiseCancellation.vendor} is not supported in this room. disabling it permanently`);
|
|
182
|
+
noiseCancellation.disablePermanently();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
174
187
|
function rewriteLocalTrackIds(room, trackStats) {
|
|
175
188
|
const localParticipantSignaling = room.localParticipant._signaling;
|
|
176
189
|
return trackStats.reduce((trackStats, trackStat) => {
|
|
@@ -19,10 +19,11 @@ class LocalParticipantSignaling extends ParticipantSignaling {
|
|
|
19
19
|
* @param {DataTrackSender|MediaTrackSender} trackSender
|
|
20
20
|
* @param {string} name
|
|
21
21
|
* @param {Track.Priority} priority
|
|
22
|
+
* @param {?NoiseCancellationVendor} noiseCancellationVendor
|
|
22
23
|
* @returns {LocalTrackPublicationSignaling} publication
|
|
23
24
|
*/
|
|
24
|
-
addTrack(trackSender, name, priority) {
|
|
25
|
-
const publication = this._createLocalTrackPublicationSignaling(trackSender, name, priority);
|
|
25
|
+
addTrack(trackSender, name, priority, noiseCancellationVendor = null) {
|
|
26
|
+
const publication = this._createLocalTrackPublicationSignaling(trackSender, name, priority, noiseCancellationVendor);
|
|
26
27
|
this._trackSendersToPublications.set(trackSender, publication);
|
|
27
28
|
this._publicationsToTrackSenders.set(publication, trackSender);
|
|
28
29
|
super.addTrack(publication);
|
|
@@ -124,9 +124,6 @@ function createCancelableRoomSignalingPromise(token, wsServer, localParticipant,
|
|
|
124
124
|
reject(new SignalingIncomingMessageInvalidError());
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
const { options: { signaling_region: signalingRegion } } = initialState;
|
|
129
|
-
localParticipant.setSignalingRegion(signalingRegion);
|
|
130
127
|
resolve(new RoomV2(localParticipant, initialState, transport, peerConnectionManager, options));
|
|
131
128
|
});
|
|
132
129
|
|
|
@@ -65,6 +65,10 @@ class LocalParticipantV2 extends LocalParticipantSignaling {
|
|
|
65
65
|
value: null,
|
|
66
66
|
writable: true
|
|
67
67
|
},
|
|
68
|
+
audioProcessors: {
|
|
69
|
+
value: [],
|
|
70
|
+
writable: true
|
|
71
|
+
},
|
|
68
72
|
bandwidthProfile: {
|
|
69
73
|
enumerable: true,
|
|
70
74
|
get() {
|
|
@@ -125,6 +129,14 @@ class LocalParticipantV2 extends LocalParticipantSignaling {
|
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Sets the AudioProcessors enabled for this room.
|
|
134
|
+
* @param {string[]} audioProcessors
|
|
135
|
+
*/
|
|
136
|
+
setAudioProcessors(audioProcessors) {
|
|
137
|
+
this.audioProcessors = audioProcessors;
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
/**
|
|
129
141
|
* returns current {@link EncodingParametersImpl}.
|
|
130
142
|
* @returns {EncodingParametersImpl}
|
|
@@ -184,10 +196,11 @@ class LocalParticipantV2 extends LocalParticipantSignaling {
|
|
|
184
196
|
* @param {DataTrackSender|MediaTrackSender} trackSender
|
|
185
197
|
* @param {string} name
|
|
186
198
|
* @param {Track.Priority} priority
|
|
199
|
+
* @param {?NoiseCancellationVendor} noiseCancellationVendor
|
|
187
200
|
* @returns {LocalTrackPublicationV2}
|
|
188
201
|
*/
|
|
189
|
-
_createLocalTrackPublicationSignaling(trackSender, name, priority) {
|
|
190
|
-
return new this._LocalTrackPublicationV2(trackSender, name, priority, { log: this._log });
|
|
202
|
+
_createLocalTrackPublicationSignaling(trackSender, name, priority, noiseCancellationVendor) {
|
|
203
|
+
return new this._LocalTrackPublicationV2(trackSender, name, priority, noiseCancellationVendor, { log: this._log });
|
|
191
204
|
}
|
|
192
205
|
|
|
193
206
|
/**
|
|
@@ -198,8 +211,8 @@ class LocalParticipantV2 extends LocalParticipantSignaling {
|
|
|
198
211
|
* @param {Track.Priority} priority
|
|
199
212
|
* @returns {this}
|
|
200
213
|
*/
|
|
201
|
-
addTrack(trackSender, name, priority) {
|
|
202
|
-
super.addTrack(trackSender, name, priority);
|
|
214
|
+
addTrack(trackSender, name, priority, noiseCancellationVendor) {
|
|
215
|
+
super.addTrack(trackSender, name, priority, noiseCancellationVendor);
|
|
203
216
|
const publication = this.getPublication(trackSender);
|
|
204
217
|
|
|
205
218
|
let {
|
|
@@ -13,10 +13,12 @@ class LocalTrackPublicationV2 extends LocalTrackPublicationSignaling {
|
|
|
13
13
|
* @param {DataTrackSender|MediaTrackSender} trackSender
|
|
14
14
|
* @param {string} name
|
|
15
15
|
* @param {Track.Priority} priority
|
|
16
|
+
* @param {?NoiseCancellationVendor} noiseCancellationVendor
|
|
16
17
|
* @param {object} [options]
|
|
17
18
|
*/
|
|
18
|
-
constructor(trackSender, name, priority, options) {
|
|
19
|
+
constructor(trackSender, name, priority, noiseCancellationVendor, options) {
|
|
19
20
|
super(trackSender, name, priority);
|
|
21
|
+
|
|
20
22
|
Object.defineProperties(this, {
|
|
21
23
|
_log: {
|
|
22
24
|
value: options.log.createLog('default', this)
|
|
@@ -24,6 +26,9 @@ class LocalTrackPublicationV2 extends LocalTrackPublicationSignaling {
|
|
|
24
26
|
_mediaStates: {
|
|
25
27
|
value: { recordings: null },
|
|
26
28
|
writable: true
|
|
29
|
+
},
|
|
30
|
+
_noiseCancellationVendor: {
|
|
31
|
+
value: noiseCancellationVendor,
|
|
27
32
|
}
|
|
28
33
|
});
|
|
29
34
|
}
|
|
@@ -33,13 +38,19 @@ class LocalTrackPublicationV2 extends LocalTrackPublicationSignaling {
|
|
|
33
38
|
* @returns {LocalTrackPublicationV2#Representation} - without the SID
|
|
34
39
|
*/
|
|
35
40
|
getState() {
|
|
36
|
-
|
|
41
|
+
const state = {
|
|
37
42
|
enabled: this.isEnabled,
|
|
38
43
|
id: this.id,
|
|
39
44
|
kind: this.kind,
|
|
40
45
|
name: this.name,
|
|
41
|
-
priority: this.updatedPriority
|
|
46
|
+
priority: this.updatedPriority,
|
|
42
47
|
};
|
|
48
|
+
|
|
49
|
+
if (this._noiseCancellationVendor) {
|
|
50
|
+
// eslint-disable-next-line camelcase
|
|
51
|
+
state.audio_processor = this._noiseCancellationVendor;
|
|
52
|
+
}
|
|
53
|
+
return state;
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
toString() {
|
package/lib/signaling/v2/room.js
CHANGED
|
@@ -52,6 +52,18 @@ class RoomV2 extends RoomSignaling {
|
|
|
52
52
|
}, options);
|
|
53
53
|
|
|
54
54
|
localParticipant.setBandwidthProfile(options.bandwidthProfile);
|
|
55
|
+
|
|
56
|
+
const { options: { signaling_region: signalingRegion, audio_processors: audioProcessors = [] } } = initialState;
|
|
57
|
+
localParticipant.setSignalingRegion(signalingRegion);
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if (audioProcessors.includes('krisp')) {
|
|
61
|
+
// Note(mpatwardhan): we add rnnoise as allowed_processor to enable testing our pipeline e2e.
|
|
62
|
+
audioProcessors.push('rnnoise');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
localParticipant.setAudioProcessors(audioProcessors);
|
|
66
|
+
|
|
55
67
|
peerConnectionManager.setIceReconnectTimeout(options.sessionTimeout);
|
|
56
68
|
|
|
57
69
|
super(localParticipant, initialState.sid, initialState.name, options);
|
|
@@ -154,7 +166,7 @@ class RoomV2 extends RoomSignaling {
|
|
|
154
166
|
this._update(initialState);
|
|
155
167
|
|
|
156
168
|
// NOTE(mpatwardhan) after initial state we know if publisher_hints are enabled or not
|
|
157
|
-
// if they are not enabled. we need to undo simulcast that
|
|
169
|
+
// if they are not enabled. we need to undo simulcast that was enabled with initial offer.
|
|
158
170
|
this._peerConnectionManager.setEffectiveAdaptiveSimulcast(this._publisherHintsSignaling.isSetup);
|
|
159
171
|
}
|
|
160
172
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 uupaa and 2019 Google LLC
|
|
3
|
+
* Licensed under the MIT license.
|
|
4
|
+
*
|
|
5
|
+
* Copied from https://github.com/GoogleChromeLabs/dynamic-import-polyfill
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
module.exports = (function(scope) {
|
|
9
|
+
const { document, location, Blob, URL } = scope;
|
|
10
|
+
if ([document, location, Blob, URL].some(api => !api)) {
|
|
11
|
+
return function dynamicImportNotSupported(module) {
|
|
12
|
+
return Promise.reject(new Error(`Failed to import: ${module}: dynamicImport is not supported`));
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const cleanupScript = script => {
|
|
16
|
+
URL.revokeObjectURL(script.src);
|
|
17
|
+
script.remove();
|
|
18
|
+
};
|
|
19
|
+
scope.__twilioImportedModules = {
|
|
20
|
+
// Imported module map.
|
|
21
|
+
};
|
|
22
|
+
return function dynamicImport(module) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const absURL = new URL(module, location);
|
|
25
|
+
const moduleBlob = new Blob([
|
|
26
|
+
`import * as m from '${absURL}';`,
|
|
27
|
+
`window.__twilioImportedModules['${absURL}']=m;`
|
|
28
|
+
], { type: 'text/javascript' });
|
|
29
|
+
const script = Object.assign(document.createElement('script'), {
|
|
30
|
+
type: 'module',
|
|
31
|
+
src: URL.createObjectURL(moduleBlob),
|
|
32
|
+
onerror() {
|
|
33
|
+
cleanupScript(script);
|
|
34
|
+
reject(new Error(`Failed to import: ${absURL}`));
|
|
35
|
+
},
|
|
36
|
+
onload() {
|
|
37
|
+
cleanupScript(script);
|
|
38
|
+
resolve(scope.__twilioImportedModules[absURL]);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
document.head.appendChild(script);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
}(globalThis));
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "twilio-video",
|
|
3
3
|
"title": "Twilio Video",
|
|
4
4
|
"description": "Twilio Video JavaScript Library",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.24.1",
|
|
6
6
|
"homepage": "https://twilio.com",
|
|
7
7
|
"author": "Mark Andrus Roberts <mroberts@twilio.com>",
|
|
8
8
|
"contributors": [
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"test:framework:install": "npm install chromedriver && npm install selenium-webdriver && npm install geckodriver && npm install puppeteer",
|
|
136
136
|
"test:framework": "npm-run-all test:framework:install test:framework:no-framework test:framework:react",
|
|
137
137
|
"test": "npm-run-all test:unit test:integration",
|
|
138
|
-
"build:es5": "rimraf ./es5 && tsc tsdef/twilio-video-tests.ts --noEmit --lib es2018,dom && tsc
|
|
138
|
+
"build:es5": "rimraf ./es5 && mkdir -p es5 && tsc tsdef/twilio-video-tests.ts --noEmit --lib es2018,dom && tsc",
|
|
139
139
|
"build:js": "node ./scripts/build.js ./src/twilio-video.js ./LICENSE.md ./dist/twilio-video.js",
|
|
140
140
|
"build:min.js": "uglifyjs ./dist/twilio-video.js -o ./dist/twilio-video.min.js --comments \"/^! twilio-video.js/\" -b beautify=false,ascii_only=true",
|
|
141
141
|
"build": "npm-run-all clean lint docs test:unit test:integration build:es5 build:js build:min.js test:umd",
|