@4players/odin-plugin-web 1.0.0-alpha.13 → 1.0.0-alpha.15

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.
Files changed (39) hide show
  1. package/lib/common/msgpack.d.ts +2 -0
  2. package/lib/common/msgpack.js +117 -0
  3. package/lib/plugin/config.d.ts +5 -3
  4. package/lib/plugin/config.js +15 -3
  5. package/lib/plugin/connectionPool.d.ts +3 -2
  6. package/lib/plugin/connectionPool.js +31 -13
  7. package/lib/plugin/deviceManager.d.ts +12 -0
  8. package/lib/plugin/deviceManager.js +120 -0
  9. package/lib/plugin/h3/mainConnection.d.ts +40 -0
  10. package/lib/plugin/h3/mainConnection.js +197 -0
  11. package/lib/plugin/index.d.ts +17 -6
  12. package/lib/plugin/index.js +158 -56
  13. package/lib/plugin/mainConnection.d.ts +23 -30
  14. package/lib/plugin/mainConnection.js +20 -291
  15. package/lib/plugin/{audioController.d.ts → mediaController.d.ts} +6 -46
  16. package/lib/plugin/mediaController.js +215 -0
  17. package/lib/plugin/medias/audio-capture.d.ts +29 -0
  18. package/lib/plugin/medias/audio-capture.js +203 -0
  19. package/lib/plugin/medias/audio-playback.d.ts +13 -0
  20. package/lib/plugin/medias/audio-playback.js +24 -0
  21. package/lib/plugin/medias/video-capture.d.ts +15 -0
  22. package/lib/plugin/medias/video-capture.js +70 -0
  23. package/lib/plugin/medias/video-playback.d.ts +13 -0
  24. package/lib/plugin/medias/video-playback.js +20 -0
  25. package/lib/plugin/roomConnection.d.ts +5 -2
  26. package/lib/plugin/roomConnection.js +54 -31
  27. package/lib/plugin/rpcConnection.d.ts +13 -7
  28. package/lib/plugin/rpcConnection.js +103 -33
  29. package/lib/plugin/token.d.ts +0 -2
  30. package/lib/plugin/token.js +0 -11
  31. package/lib/plugin/webrtc/mainConnection.d.ts +52 -0
  32. package/lib/plugin/webrtc/mainConnection.js +455 -0
  33. package/lib/plugin/{websocket.js → webrtc/websocket.js} +2 -2
  34. package/lib/version.d.ts +1 -0
  35. package/lib/version.js +2 -0
  36. package/lib/worker/index.js +1 -1
  37. package/package.json +18 -11
  38. package/lib/plugin/audioController.js +0 -513
  39. /package/lib/plugin/{websocket.d.ts → webrtc/websocket.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4players/odin-plugin-web",
3
- "version": "1.0.0-alpha.13",
3
+ "version": "1.0.0-alpha.15",
4
4
  "description": "A backend plugin providing the custom WebAssembly audio processing module for ODIN web clients",
5
5
  "homepage": "https://www.4players.io",
6
6
  "repository": {
@@ -11,36 +11,43 @@
11
11
  "main": "./lib/plugin/index.js",
12
12
  "types": "./lib/plugin/index.d.ts",
13
13
  "scripts": {
14
- "clean": "rm -rf build lib",
14
+ "clean": "rimraf build lib",
15
15
  "configure": "npm-run-all -pl configure:*",
16
16
  "configure:shared": "emcmake cmake -B build/shared -DCMAKE_BUILD_TYPE=Release -DSHARED_MEMORY=1 -S .",
17
17
  "configure:unshared": "emcmake cmake -B build/unshared -DCMAKE_BUILD_TYPE=Release -S .",
18
- "build": "npm-run-all -s configure build:wasm build:plugin build:worker",
18
+ "build": "npm-run-all -s configure build:genversion build:wasm build:plugin build:worker",
19
+ "build:genversion": "genversion -se src/version.ts",
19
20
  "build:wasm": "npm-run-all -pl build:wasm:* && node install-wasm.js",
20
21
  "build:wasm:shared": "emmake cmake --build build/shared",
21
22
  "build:wasm:unshared": "emmake cmake --build build/unshared",
22
23
  "build:worker": "ts-node src/build.ts && tsc -b ./tsconfig.worker.json",
23
- "build:plugin": "tsc",
24
+ "build:plugin": "tsc --build --verbose --force",
24
25
  "format": "prettier 'src/**/*.ts' --write",
25
26
  "lint": "eslint . --ext .ts",
26
- "test": "npm --prefix test run serve"
27
+ "test": "npm --prefix test run start",
28
+ "bundle": "npm run build && esbuild lib/plugin/index.js --bundle --outfile=dist/plugin.js"
27
29
  },
28
30
  "dependencies": {
29
- "@4players/odin-common": "~1.7.0"
31
+ "@4players/odin-common": "~2.4.12"
30
32
  },
31
33
  "devDependencies": {
32
- "@types/node": "~20.11.0",
33
- "@typescript-eslint/eslint-plugin": "~7.2.0",
34
- "@typescript-eslint/parser": "~7.2.0",
35
- "esbuild": "~0.20.0",
34
+ "@types/node": "~20.12.0",
35
+ "@typescript-eslint/eslint-plugin": "~7.7.0",
36
+ "@typescript-eslint/parser": "~7.7.0",
37
+ "esbuild": "^0.20.2",
36
38
  "eslint": "~8.57.0",
37
39
  "eslint-config-semistandard": "~17.0.0",
38
40
  "eslint-plugin-import": "~2.29.0",
39
41
  "eslint-plugin-node": "~11.1.0",
40
42
  "eslint-plugin-promise": "~6.1.0",
43
+ "genversion": "^3.2.0",
41
44
  "npm-run-all2": "~6.1.0",
42
45
  "prettier": "~3.2.0",
46
+ "rimraf": "~5.0.0",
43
47
  "ts-node": "~10.9.0",
44
- "typescript": "~5.3.0"
48
+ "typescript": "~5.4.0"
49
+ },
50
+ "peerDependencies": {
51
+ "zod": "*"
45
52
  }
46
53
  }
@@ -1,513 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { Strand, assert, failure, sleep, success, } from '@4players/odin-common';
11
- import { createCaptureWorker, createPlaybackWorker } from '../worker';
12
- import { Silence, } from '../worker/common';
13
- const defaultVad = {
14
- voiceActivity: {
15
- attackThreshold: 0.9,
16
- releaseThreshold: 0.8,
17
- },
18
- volumeGate: {
19
- attackThreshold: -30,
20
- releaseThreshold: -40,
21
- },
22
- };
23
- const defaultApm = {
24
- echoCanceller: false,
25
- highPassFilter: false,
26
- preAmplifier: false,
27
- captureLevelAdjustment: false,
28
- noiseSuppression: 'None',
29
- transientSuppressor: false,
30
- gainController: false,
31
- };
32
- export class AudioContextCollection {
33
- constructor(audioContextFactory) {
34
- this.audioContextFactory = audioContextFactory;
35
- this.entries = [];
36
- }
37
- acquire(sampleRate) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- for (const entry of this.entries) {
40
- if (entry.sampleRate === sampleRate) {
41
- entry.references += 1;
42
- return entry.audioContext;
43
- }
44
- else if (entry.sampleRate === undefined) {
45
- const audioContext = entry.audioContext;
46
- if (audioContext.sampleRate === sampleRate) {
47
- entry.references += 1;
48
- return entry.audioContext;
49
- }
50
- }
51
- }
52
- const entry = {
53
- sampleRate,
54
- audioContext: yield this.audioContextFactory(sampleRate),
55
- references: 1,
56
- };
57
- this.entries.push(entry);
58
- return entry.audioContext;
59
- });
60
- }
61
- release(audioContext) {
62
- return __awaiter(this, void 0, void 0, function* () {
63
- const index = this.entries.findIndex((entry) => entry.audioContext === audioContext);
64
- if (index < 0)
65
- return;
66
- this.entries[index].references -= 1;
67
- if (this.entries[index].references <= 0) {
68
- const context = this.entries[index].audioContext;
69
- this.entries.splice(index, 1);
70
- yield context.close();
71
- }
72
- });
73
- }
74
- close() {
75
- for (const entry of this.entries) {
76
- entry.audioContext.close();
77
- }
78
- }
79
- }
80
- let hasAudioPermissions = false;
81
- export class AudioController {
82
- create(audioContexts, sink) {
83
- return __awaiter(this, void 0, void 0, function* () {
84
- return new AudioController(audioContexts, sink);
85
- });
86
- }
87
- constructor(audioContexts, sink) {
88
- this.audioContexts = audioContexts;
89
- this.sink = sink;
90
- this.globalVolume = 1;
91
- this.playbacks = new Map();
92
- this.setPlaybackStrand = new Strand();
93
- }
94
- receivePacket(connectionId, packet) {
95
- var _a;
96
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.receivePacket(connectionId, packet);
97
- }
98
- createAudioPlayback(parameters) {
99
- return __awaiter(this, void 0, void 0, function* () {
100
- let entry = this.playbacks.get(parameters.uid);
101
- let volume = parameters.volume;
102
- if (entry === undefined) {
103
- volume = volume !== null && volume !== void 0 ? volume : 1;
104
- const playback = new AudioPlayback(this, parameters.uid);
105
- entry = { id: undefined, volume, playback };
106
- this.playbacks.set(parameters.uid, entry);
107
- }
108
- if (volume !== undefined) {
109
- entry.playback.setVolume(volume);
110
- }
111
- return entry.playback;
112
- });
113
- }
114
- createAudioCapture(parameters) {
115
- return __awaiter(this, void 0, void 0, function* () {
116
- const capture = new AudioCapture(this, parameters);
117
- yield capture.setDevice(parameters);
118
- return capture;
119
- });
120
- }
121
- enumerateDevices() {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- hasAudioPermissions = yield this._requestMediaPermissions();
124
- const mediaDeviceInfos = yield navigator.mediaDevices.enumerateDevices();
125
- const devices = [];
126
- let outputDeviceIndex = 0;
127
- let inputDeviceIndex = 0;
128
- for (const mediaDeviceInfo of mediaDeviceInfos) {
129
- if (mediaDeviceInfo.kind === 'audioinput') {
130
- if (inputDeviceIndex === 0) {
131
- devices.push(new Device(mediaDeviceInfo, true));
132
- }
133
- else {
134
- devices.push(new Device(mediaDeviceInfo, false));
135
- }
136
- inputDeviceIndex++;
137
- }
138
- if (mediaDeviceInfo.kind === 'audiooutput') {
139
- if (outputDeviceIndex === 0) {
140
- devices.push(new Device(mediaDeviceInfo, true));
141
- }
142
- else {
143
- devices.push(new Device(mediaDeviceInfo, false));
144
- }
145
- outputDeviceIndex++;
146
- }
147
- }
148
- return devices;
149
- });
150
- }
151
- /**
152
- * Requests audio and video browser permissions. Optionally, requests either audio or video permissions.
153
- * @private
154
- * @throws
155
- */
156
- _requestMediaPermissions() {
157
- return __awaiter(this, void 0, void 0, function* () {
158
- if (hasAudioPermissions)
159
- return true;
160
- const constraints = {};
161
- constraints.audio = {
162
- sampleRate: undefined,
163
- deviceId: '',
164
- autoGainControl: false,
165
- noiseSuppression: false,
166
- advanced: undefined,
167
- aspectRatio: undefined,
168
- echoCancellation: false,
169
- channelCount: 0,
170
- facingMode: undefined,
171
- frameRate: 0,
172
- };
173
- console.log('Plugin _requestMediaPermissions: ', constraints);
174
- try {
175
- const ms = yield navigator.mediaDevices.getUserMedia(constraints);
176
- ms === null || ms === void 0 ? void 0 : ms.getTracks().forEach((track) => {
177
- track.stop();
178
- ms.removeTrack(track);
179
- });
180
- }
181
- catch (e) {
182
- console.error(`Requesting permissions for ${'audio'} failed.`);
183
- console.error(e);
184
- return false;
185
- }
186
- return true;
187
- });
188
- }
189
- playbackSetDevice(device) {
190
- return this.setPlaybackStrand.enqueue(() => __awaiter(this, void 0, void 0, function* () {
191
- var _a;
192
- let deviceId = (_a = device === null || device === void 0 ? void 0 : device.device.deviceId) !== null && _a !== void 0 ? _a : '';
193
- if (deviceId === 'default') {
194
- deviceId = '';
195
- }
196
- let audioContext;
197
- if (this.playbackWorker === undefined) {
198
- audioContext = yield this.audioContexts.acquire(48000);
199
- console.log('playbackSetDevice: this.audioContexts.acquire(48000)');
200
- this.playbackWorker = yield createPlaybackWorker(audioContext);
201
- this.playbackWorker.setGlobalVolume(this.globalVolume);
202
- }
203
- else {
204
- console.log('this.playbackWorker.audioContext');
205
- audioContext = this.playbackWorker.audioContext;
206
- }
207
- console.log('playbackSetDevice audioContext: ', audioContext);
208
- if ((audioContext === null || audioContext === void 0 ? void 0 : audioContext.setSinkId) !== undefined &&
209
- device &&
210
- (audioContext === null || audioContext === void 0 ? void 0 : audioContext.state) === 'running') {
211
- console.log('Browser audioContext.setSinkId');
212
- yield audioContext.setSinkId(deviceId);
213
- }
214
- }));
215
- }
216
- playbackSetGlobalVolume(value) {
217
- var _a;
218
- this.globalVolume = value === 'muted' ? 0 : value;
219
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.setGlobalVolume(this.globalVolume);
220
- }
221
- playbackStart(uid, id) {
222
- var _a;
223
- const entry = this.playbacks.get(uid);
224
- if (entry === undefined)
225
- return;
226
- if (entry.id !== undefined) {
227
- this.playbackStop(uid, entry.id);
228
- }
229
- entry.id = id;
230
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.addMedias(id.connectionId, [id.mediaId]);
231
- this.playbackSetVolume(uid, entry.volume);
232
- }
233
- playbackStop(uid, id) {
234
- var _a;
235
- const entry = this.playbacks.get(uid);
236
- if (entry === undefined)
237
- return;
238
- if (entry.id !== id)
239
- console.error('playback mediaId out of sync!');
240
- entry.id = undefined;
241
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.removeMedias(id.connectionId, [id.mediaId]);
242
- }
243
- playbackGetActivity(uid) {
244
- var _a, _b;
245
- const entry = this.playbacks.get(uid);
246
- if ((entry === null || entry === void 0 ? void 0 : entry.id) === undefined)
247
- return Silence;
248
- return (_b = (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.getActivity(entry.id)) !== null && _b !== void 0 ? _b : Silence;
249
- }
250
- playbackSetVolume(uid, volume) {
251
- var _a;
252
- const entry = this.playbacks.get(uid);
253
- if (entry === undefined)
254
- return;
255
- entry.volume = volume === 'muted' ? 0 : volume;
256
- if (entry.id !== undefined) {
257
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.setVolume(entry.id, entry.volume);
258
- }
259
- }
260
- playbackGetVolume(uid) {
261
- var _a;
262
- const entry = this.playbacks.get(uid);
263
- return (_a = entry === null || entry === void 0 ? void 0 : entry.volume) !== null && _a !== void 0 ? _a : 1;
264
- }
265
- closePlayback(uid) {
266
- const entry = this.playbacks.get(uid);
267
- if (entry === undefined)
268
- return;
269
- if (entry.id !== undefined)
270
- this.playbackStop(uid, entry.id);
271
- this.playbacks.delete(uid);
272
- }
273
- close() {
274
- var _a;
275
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.close();
276
- this.audioContexts.close();
277
- }
278
- }
279
- export class Device {
280
- constructor(device, isDefault = false) {
281
- this.device = device;
282
- this.isDefault = isDefault;
283
- }
284
- get type() {
285
- switch (this.device.kind) {
286
- case 'audioinput':
287
- return 'AudioCapture';
288
- case 'audiooutput':
289
- return 'AudioPlayback';
290
- default:
291
- assert(false, 'not supported device');
292
- }
293
- }
294
- get name() {
295
- return this.device.label;
296
- }
297
- }
298
- export class AudioPlayback {
299
- get volume() {
300
- return this.controller.playbackGetVolume(this.uid);
301
- }
302
- get activity() {
303
- return this.controller.playbackGetActivity(this.uid);
304
- }
305
- constructor(controller, uid) {
306
- this.controller = controller;
307
- this.uid = uid;
308
- }
309
- start(id) {
310
- this.controller.playbackStart(this.uid, id);
311
- }
312
- stop(id) {
313
- this.controller.playbackStop(this.uid, id);
314
- }
315
- setVolume(value) {
316
- this.controller.playbackSetVolume(this.uid, value);
317
- }
318
- close() {
319
- this.controller.closePlayback(this.uid);
320
- }
321
- }
322
- export class AudioCapture {
323
- get vad() {
324
- return this._Vad;
325
- }
326
- get apm() {
327
- return this._Apm;
328
- }
329
- get volume() {
330
- return this._Volume;
331
- }
332
- get activity() {
333
- var _a, _b;
334
- return (_b = (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.activity) !== null && _b !== void 0 ? _b : Silence;
335
- }
336
- get speechProbability() {
337
- var _a, _b;
338
- return (_b = (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.speechProbability) !== null && _b !== void 0 ? _b : 0;
339
- }
340
- get deviceId() {
341
- var _a, _b;
342
- return (_b = (_a = this._deviceParameters) === null || _a === void 0 ? void 0 : _a.device) === null || _b === void 0 ? void 0 : _b.device.deviceId;
343
- }
344
- constructor(controller, parameters) {
345
- this.controller = controller;
346
- this._Medias = new Map();
347
- this._Vad = defaultVad;
348
- this._Apm = defaultApm;
349
- this._Volume = 1;
350
- if (parameters.apm) {
351
- this.setApm(parameters.apm);
352
- }
353
- if (parameters.vad) {
354
- this.setVad(parameters.vad);
355
- }
356
- if (parameters.volume) {
357
- this.setVolume(parameters.volume);
358
- }
359
- }
360
- start(room, media, uid) {
361
- var _a;
362
- let entry = this._Medias.get(media.connectionId);
363
- if (entry === undefined) {
364
- this._Medias.set(media.connectionId, (entry = new Set()));
365
- }
366
- if (entry.has(media.mediaId))
367
- return;
368
- entry.add(media.mediaId);
369
- if (((_a = room.mainConnection) === null || _a === void 0 ? void 0 : _a.id) === media.connectionId) {
370
- room
371
- .typedRequest('StartMedia', {
372
- media_id: media.mediaId,
373
- properties: { kind: 'audio', uid },
374
- })
375
- .catch((reason) => console.error(`StartMedia got rejected; ${reason}`));
376
- }
377
- this._UpdateMedias();
378
- }
379
- stop(room, media) {
380
- var _a;
381
- const entry = this._Medias.get(media.connectionId);
382
- if (!(entry === null || entry === void 0 ? void 0 : entry.delete(media.mediaId)))
383
- return;
384
- if (((_a = room.mainConnection) === null || _a === void 0 ? void 0 : _a.id) === media.connectionId) {
385
- room
386
- .typedRequest('StopMedia', { media_id: media.mediaId })
387
- .catch((reason) => console.error(`StopMedia got rejected; ${reason}`));
388
- }
389
- this._UpdateMedias();
390
- }
391
- setVad(value) {
392
- this._Vad = value;
393
- if (this._Capture) {
394
- this._Capture.worker.setVad(value);
395
- }
396
- }
397
- setApm(config) {
398
- return __awaiter(this, void 0, void 0, function* () {
399
- this._Apm = config;
400
- if (this._deviceParameters) {
401
- yield this.setDevice(this._deviceParameters);
402
- }
403
- });
404
- }
405
- setDevice(parameters) {
406
- var _a;
407
- return __awaiter(this, void 0, void 0, function* () {
408
- const rollbackParams = this._deviceParameters;
409
- this._deviceParameters = parameters;
410
- if (this._Volume === 'muted') {
411
- return;
412
- }
413
- const device = yield openCaptureDevice(this.controller, parameters, this._Apm);
414
- if (device.type !== 'Success') {
415
- console.error(`unable to open capture device; ${device.reason}`);
416
- this._deviceParameters = rollbackParams;
417
- return;
418
- }
419
- if (this._Capture !== undefined) {
420
- stopMediaStream(this._Capture.source.mediaStream);
421
- this._Capture.source.disconnect(this._Capture.worker.worklet);
422
- }
423
- if (this._Capture === undefined ||
424
- this._Capture.worker.worklet.context !== device.value.context) {
425
- (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.close();
426
- this._Capture = {
427
- worker: yield createCaptureWorker(device.value.context, this.controller.sink),
428
- source: device.value,
429
- };
430
- yield this._UpdateMedias();
431
- this.setVolume(this.volume);
432
- this.setVad(this.vad);
433
- }
434
- else {
435
- this._Capture.source = device.value;
436
- }
437
- this._Capture.source.connect(this._Capture.worker.worklet);
438
- return success();
439
- });
440
- }
441
- setVolume(value) {
442
- var _a;
443
- this._Volume = value;
444
- if (value === 'muted') {
445
- this.close();
446
- }
447
- else {
448
- if (!this._Capture && this._deviceParameters) {
449
- this.setDevice(this._deviceParameters);
450
- }
451
- (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.setVolume(value);
452
- }
453
- }
454
- close() {
455
- if (this._Capture !== undefined) {
456
- this._Capture.worker.close();
457
- stopMediaStream(this._Capture.source.mediaStream);
458
- this._Capture.source.disconnect(this._Capture.worker.worklet);
459
- this.controller.audioContexts.release(this._Capture.source.context);
460
- }
461
- this._Capture = undefined;
462
- }
463
- _UpdateMedias() {
464
- return __awaiter(this, void 0, void 0, function* () {
465
- if (this._Capture !== undefined) {
466
- const connections = Array.from(this._Medias).map(([key, value]) => {
467
- return { id: key, mediaIds: Array.from(value) };
468
- });
469
- this._Capture.worker.setMedias(connections);
470
- }
471
- });
472
- }
473
- }
474
- function stopMediaStream(ms) {
475
- ms.getTracks().forEach((track) => {
476
- track.stop();
477
- ms.removeTrack(track);
478
- });
479
- }
480
- function openCaptureDevice(controller, parameters, apm) {
481
- return __awaiter(this, void 0, void 0, function* () {
482
- const deviceId = parameters.device
483
- ? { exact: parameters.device.device.deviceId }
484
- : undefined;
485
- let stream;
486
- try {
487
- const result = yield Promise.race([
488
- navigator.mediaDevices.getUserMedia({
489
- audio: {
490
- echoCancellation: apm.echoCanceller,
491
- noiseSuppression: apm.noiseSuppression !== 'None',
492
- autoGainControl: apm.gainController,
493
- // @ts-ignore chrome fix
494
- echoCancellationType: 'system',
495
- // sampleRate: 48000,
496
- deviceId,
497
- },
498
- }),
499
- sleep(30000, 'timeout'),
500
- ]);
501
- if (result === 'timeout')
502
- return failure('getUserMedia; timeout');
503
- hasAudioPermissions = true;
504
- stream = result;
505
- }
506
- catch (reason) {
507
- return failure(`getUserMedia; ${reason}`);
508
- }
509
- const sampleRate = stream.getAudioTracks()[0].getSettings().sampleRate;
510
- const context = yield controller.audioContexts.acquire(sampleRate);
511
- return success(context.createMediaStreamSource(stream));
512
- });
513
- }