mediasoup-client 3.6.45 → 3.6.49

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 (67) hide show
  1. package/.vimrc +21 -0
  2. package/NO_GIT/Notes.txt +18 -0
  3. package/lib/DataConsumer.d.ts +2 -2
  4. package/lib/DataConsumer.d.ts.map +1 -1
  5. package/lib/Device.d.ts.map +1 -1
  6. package/lib/Device.js +7 -3
  7. package/lib/Logger.js +6 -6
  8. package/lib/Transport.d.ts +3 -0
  9. package/lib/Transport.d.ts.map +1 -1
  10. package/lib/Transport.js +111 -40
  11. package/lib/handlers/Chrome55.d.ts +1 -1
  12. package/lib/handlers/Chrome55.d.ts.map +1 -1
  13. package/lib/handlers/Chrome55.js +51 -30
  14. package/lib/handlers/Chrome67.d.ts +1 -1
  15. package/lib/handlers/Chrome67.d.ts.map +1 -1
  16. package/lib/handlers/Chrome67.js +55 -33
  17. package/lib/handlers/Chrome70.d.ts +1 -1
  18. package/lib/handlers/Chrome70.d.ts.map +1 -1
  19. package/lib/handlers/Chrome70.js +53 -33
  20. package/lib/handlers/Chrome74.d.ts +1 -1
  21. package/lib/handlers/Chrome74.d.ts.map +1 -1
  22. package/lib/handlers/Chrome74.js +56 -33
  23. package/lib/handlers/Edge11.d.ts +1 -1
  24. package/lib/handlers/Edge11.d.ts.map +1 -1
  25. package/lib/handlers/Edge11.js +31 -22
  26. package/lib/handlers/FakeHandler.d.ts +1 -1
  27. package/lib/handlers/FakeHandler.d.ts.map +1 -1
  28. package/lib/handlers/FakeHandler.js +14 -11
  29. package/lib/handlers/Firefox60.d.ts +1 -1
  30. package/lib/handlers/Firefox60.d.ts.map +1 -1
  31. package/lib/handlers/Firefox60.js +54 -33
  32. package/lib/handlers/HandlerInterface.d.ts +1 -1
  33. package/lib/handlers/HandlerInterface.d.ts.map +1 -1
  34. package/lib/handlers/ReactNative.d.ts +1 -1
  35. package/lib/handlers/ReactNative.d.ts.map +1 -1
  36. package/lib/handlers/ReactNative.js +59 -36
  37. package/lib/handlers/Safari11.d.ts +1 -1
  38. package/lib/handlers/Safari11.d.ts.map +1 -1
  39. package/lib/handlers/Safari11.js +52 -33
  40. package/lib/handlers/Safari12.d.ts +1 -1
  41. package/lib/handlers/Safari12.d.ts.map +1 -1
  42. package/lib/handlers/Safari12.js +51 -32
  43. package/lib/handlers/ortc/edgeUtils.js +1 -1
  44. package/lib/handlers/sdp/MediaSection.js +1 -1
  45. package/lib/handlers/sdp/RemoteSdp.js +1 -1
  46. package/lib/handlers/sdp/commonUtils.js +1 -1
  47. package/lib/index.d.ts +1 -1
  48. package/lib/index.js +3 -3
  49. package/lib/ortc.js +5 -7
  50. package/lib/types.js +1 -1
  51. package/package.json +11 -11
  52. package/src/DataConsumer.ts +2 -2
  53. package/src/Device.ts +7 -2
  54. package/src/Transport.ts +162 -58
  55. package/src/handlers/Chrome55.ts +53 -32
  56. package/src/handlers/Chrome67.ts +55 -34
  57. package/src/handlers/Chrome70.ts +52 -32
  58. package/src/handlers/Chrome74.ts +56 -32
  59. package/src/handlers/Edge11.ts +42 -26
  60. package/src/handlers/FakeHandler.ts +18 -10
  61. package/src/handlers/Firefox60.ts +54 -33
  62. package/src/handlers/HandlerInterface.ts +2 -2
  63. package/src/handlers/ReactNative.ts +63 -39
  64. package/src/handlers/Safari11.ts +55 -34
  65. package/src/handlers/Safari12.ts +52 -32
  66. package/src/ortc.ts +6 -8
  67. package/tsconfig.json +1 -1
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -121,7 +121,13 @@ class Safari12 extends HandlerInterface_1.HandlerInterface {
121
121
  ? 'client'
122
122
  : 'server';
123
123
  }
124
- this._pc = new RTCPeerConnection(Object.assign({ iceServers: iceServers || [], iceTransportPolicy: iceTransportPolicy || 'all', bundlePolicy: 'max-bundle', rtcpMuxPolicy: 'require' }, additionalSettings), proprietaryConstraints);
124
+ this._pc = new RTCPeerConnection({
125
+ iceServers: iceServers || [],
126
+ iceTransportPolicy: iceTransportPolicy || 'all',
127
+ bundlePolicy: 'max-bundle',
128
+ rtcpMuxPolicy: 'require',
129
+ ...additionalSettings
130
+ }, proprietaryConstraints);
125
131
  // Handle RTCPeerConnection connection status.
126
132
  this._pc.addEventListener('iceconnectionstatechange', () => {
127
133
  switch (this._pc.iceConnectionState) {
@@ -310,7 +316,7 @@ class Safari12 extends HandlerInterface_1.HandlerInterface {
310
316
  throw new Error('associated RTCRtpTransceiver not found');
311
317
  const parameters = transceiver.sender.getParameters();
312
318
  parameters.encodings.forEach((encoding, idx) => {
313
- parameters.encodings[idx] = Object.assign(Object.assign({}, encoding), params);
319
+ parameters.encodings[idx] = { ...encoding, ...params };
314
320
  });
315
321
  await transceiver.sender.setParameters(parameters);
316
322
  }
@@ -366,31 +372,39 @@ class Safari12 extends HandlerInterface_1.HandlerInterface {
366
372
  };
367
373
  return { dataChannel, sctpStreamParameters };
368
374
  }
369
- async receive({ trackId, kind, rtpParameters }) {
375
+ async receive(optionsList) {
370
376
  var _a;
371
377
  this._assertRecvDirection();
372
- logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind);
373
- const localId = rtpParameters.mid || String(this._mapMidTransceiver.size);
374
- this._remoteSdp.receive({
375
- mid: localId,
376
- kind,
377
- offerRtpParameters: rtpParameters,
378
- streamId: rtpParameters.rtcp.cname,
379
- trackId
380
- });
378
+ const results = [];
379
+ for (const options of optionsList) {
380
+ const { trackId, kind, rtpParameters } = options;
381
+ logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind);
382
+ const localId = rtpParameters.mid || String(this._mapMidTransceiver.size);
383
+ this._remoteSdp.receive({
384
+ mid: localId,
385
+ kind,
386
+ offerRtpParameters: rtpParameters,
387
+ streamId: rtpParameters.rtcp.cname,
388
+ trackId
389
+ });
390
+ }
381
391
  const offer = { type: 'offer', sdp: this._remoteSdp.getSdp() };
382
392
  logger.debug('receive() | calling pc.setRemoteDescription() [offer:%o]', offer);
383
393
  await this._pc.setRemoteDescription(offer);
384
394
  let answer = await this._pc.createAnswer();
385
395
  const localSdpObject = sdpTransform.parse(answer.sdp);
386
- const answerMediaObject = localSdpObject.media
387
- .find((m) => String(m.mid) === localId);
388
- // May need to modify codec parameters in the answer based on codec
389
- // parameters in the offer.
390
- sdpCommonUtils.applyCodecParameters({
391
- offerRtpParameters: rtpParameters,
392
- answerMediaObject
393
- });
396
+ for (const options of optionsList) {
397
+ const { rtpParameters } = options;
398
+ const localId = rtpParameters.mid || String(this._mapMidTransceiver.size);
399
+ const answerMediaObject = localSdpObject.media
400
+ .find((m) => String(m.mid) === localId);
401
+ // May need to modify codec parameters in the answer based on codec
402
+ // parameters in the offer.
403
+ sdpCommonUtils.applyCodecParameters({
404
+ offerRtpParameters: rtpParameters,
405
+ answerMediaObject
406
+ });
407
+ }
394
408
  answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) };
395
409
  if (!this._transportReady) {
396
410
  await this._setupTransport({
@@ -400,17 +414,22 @@ class Safari12 extends HandlerInterface_1.HandlerInterface {
400
414
  }
401
415
  logger.debug('receive() | calling pc.setLocalDescription() [answer:%o]', answer);
402
416
  await this._pc.setLocalDescription(answer);
403
- const transceiver = this._pc.getTransceivers()
404
- .find((t) => t.mid === localId);
405
- if (!transceiver)
406
- throw new Error('new RTCRtpTransceiver not found');
407
- // Store in the map.
408
- this._mapMidTransceiver.set(localId, transceiver);
409
- return {
410
- localId,
411
- track: transceiver.receiver.track,
412
- rtpReceiver: transceiver.receiver
413
- };
417
+ for (const options of optionsList) {
418
+ const { rtpParameters } = options;
419
+ const localId = rtpParameters.mid || String(this._mapMidTransceiver.size);
420
+ const transceiver = this._pc.getTransceivers()
421
+ .find((t) => t.mid === localId);
422
+ if (!transceiver)
423
+ throw new Error('new RTCRtpTransceiver not found');
424
+ // Store in the map.
425
+ this._mapMidTransceiver.set(localId, transceiver);
426
+ results.push({
427
+ localId,
428
+ track: transceiver.receiver.track,
429
+ rtpReceiver: transceiver.receiver
430
+ });
431
+ }
432
+ return results;
414
433
  }
415
434
  async stopReceiving(localId) {
416
435
  this._assertRecvDirection();
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
package/lib/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { types };
8
8
  /**
9
9
  * Expose mediasoup-client version.
10
10
  */
11
- export declare const version = "3.6.45";
11
+ export declare const version = "3.6.49";
12
12
  /**
13
13
  * Expose Device class and detectDevice() helper.
14
14
  */
package/lib/index.js CHANGED
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.debug = exports.detectDevice = exports.Device = exports.version = exports.types = void 0;
25
+ exports.debug = exports.parseScalabilityMode = exports.detectDevice = exports.Device = exports.version = exports.types = void 0;
26
26
  const debug_1 = __importDefault(require("debug"));
27
27
  exports.debug = debug_1.default;
28
28
  const Device_1 = require("./Device");
@@ -33,7 +33,7 @@ exports.types = types;
33
33
  /**
34
34
  * Expose mediasoup-client version.
35
35
  */
36
- exports.version = '3.6.45';
36
+ exports.version = '3.6.49';
37
37
  /**
38
38
  * Expose parseScalabilityMode() function.
39
39
  */
package/lib/ortc.js CHANGED
@@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
14
  var __importStar = (this && this.__importStar) || function (mod) {
15
15
  if (mod && mod.__esModule) return mod;
16
16
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
@@ -780,12 +780,11 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
780
780
  switch (aMimeType) {
781
781
  case 'video/h264':
782
782
  {
783
- const aPacketizationMode = aCodec.parameters['packetization-mode'] || 0;
784
- const bPacketizationMode = bCodec.parameters['packetization-mode'] || 0;
785
- if (aPacketizationMode !== bPacketizationMode)
786
- return false;
787
- // If strict matching check profile-level-id.
788
783
  if (strict) {
784
+ const aPacketizationMode = aCodec.parameters['packetization-mode'] || 0;
785
+ const bPacketizationMode = bCodec.parameters['packetization-mode'] || 0;
786
+ if (aPacketizationMode !== bPacketizationMode)
787
+ return false;
789
788
  if (!h264.isSameProfile(aCodec.parameters, bCodec.parameters))
790
789
  return false;
791
790
  let selectedProfileLevelId;
@@ -811,7 +810,6 @@ function matchCodecs(aCodec, bCodec, { strict = false, modify = false } = {}) {
811
810
  }
812
811
  case 'video/vp9':
813
812
  {
814
- // If strict matching check profile-id.
815
813
  if (strict) {
816
814
  const aProfileId = aCodec.parameters['profile-id'] || 0;
817
815
  const bProfileId = bCodec.parameters['profile-id'] || 0;
package/lib/types.js CHANGED
@@ -7,7 +7,7 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
7
7
  o[k2] = m[k];
8
8
  }));
9
9
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./Device"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasoup-client",
3
- "version": "3.6.45",
3
+ "version": "3.6.49",
4
4
  "description": "mediasoup client side JavaScript library",
5
5
  "contributors": [
6
6
  "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
@@ -39,25 +39,25 @@
39
39
  "@types/events": "^3.0.0",
40
40
  "awaitqueue": "^2.3.3",
41
41
  "bowser": "^2.11.0",
42
- "debug": "^4.3.2",
42
+ "debug": "^4.3.3",
43
43
  "events": "^3.3.0",
44
44
  "fake-mediastreamtrack": "^1.1.6",
45
45
  "h264-profile-level-id": "^1.0.1",
46
46
  "sdp-transform": "^2.14.1",
47
- "supports-color": "^8.1.1"
47
+ "supports-color": "^9.2.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/sdp-transform": "^2.4.5",
51
51
  "@types/uuid": "^3.4.9",
52
- "@typescript-eslint/eslint-plugin": "^4.33.0",
53
- "@typescript-eslint/parser": "^4.33.0",
54
- "eslint": "^7.32.0",
55
- "eslint-plugin-jest": "^24.5.2",
56
- "jest": "^27.3.1",
52
+ "@typescript-eslint/eslint-plugin": "^5.9.1",
53
+ "@typescript-eslint/parser": "^5.9.1",
54
+ "eslint": "^8.6.0",
55
+ "eslint-plugin-jest": "^25.3.4",
56
+ "jest": "^27.4.7",
57
57
  "jest-tobetype": "^1.2.3",
58
- "open-cli": "^6.0.1",
59
- "tsc-watch": "^4.5.0",
60
- "typescript": "^3.8",
58
+ "open-cli": "^7.0.1",
59
+ "tsc-watch": "^4.6.0",
60
+ "typescript": "^4.5.4",
61
61
  "uuid": "^3.4.0"
62
62
  }
63
63
  }
@@ -128,7 +128,7 @@ export class DataConsumer extends EnhancedEventEmitter
128
128
  /**
129
129
  * DataChannel binaryType.
130
130
  */
131
- get binaryType(): string
131
+ get binaryType(): BinaryType
132
132
  {
133
133
  return this._dataChannel.binaryType;
134
134
  }
@@ -136,7 +136,7 @@ export class DataConsumer extends EnhancedEventEmitter
136
136
  /**
137
137
  * Set DataChannel binaryType.
138
138
  */
139
- set binaryType(binaryType: string)
139
+ set binaryType(binaryType: BinaryType)
140
140
  {
141
141
  this._dataChannel.binaryType = binaryType;
142
142
  }
package/src/Device.ts CHANGED
@@ -80,8 +80,8 @@ export function detectDevice(): BuiltinHandlerName | undefined
80
80
  const browser = Bowser.getParser(ua);
81
81
  const engine = browser.getEngine();
82
82
 
83
- // Chrome and Chromium.
84
- if (browser.satisfies({ chrome: '>=74', chromium: '>=74' }))
83
+ // Chrome, Chromium, and Edge.
84
+ if (browser.satisfies({ chrome: '>=74', chromium: '>=74', 'microsoft edge': '>=88' }))
85
85
  {
86
86
  return 'Chrome74';
87
87
  }
@@ -102,6 +102,11 @@ export function detectDevice(): BuiltinHandlerName | undefined
102
102
  {
103
103
  return 'Firefox60';
104
104
  }
105
+ // Firefox on iOS.
106
+ else if (browser.satisfies({ ios: { OS: '>=14.3', firefox: '>=30.0' } }))
107
+ {
108
+ return 'Safari12';
109
+ }
105
110
  // Safari with Unified-Plan support enabled.
106
111
  else if (
107
112
  browser.satisfies({ safari: '>=12.0' }) &&
package/src/Transport.ts CHANGED
@@ -4,7 +4,7 @@ import { EnhancedEventEmitter } from './EnhancedEventEmitter';
4
4
  import { UnsupportedError, InvalidStateError } from './errors';
5
5
  import * as utils from './utils';
6
6
  import * as ortc from './ortc';
7
- import { HandlerFactory, HandlerInterface } from './handlers/HandlerInterface';
7
+ import { HandlerFactory, HandlerInterface, HandlerReceiveOptions } from './handlers/HandlerInterface';
8
8
  import { Producer, ProducerOptions } from './Producer';
9
9
  import { Consumer, ConsumerOptions } from './Consumer';
10
10
  import { DataProducer, DataProducerOptions } from './DataProducer';
@@ -19,6 +19,24 @@ interface InternalTransportOptions extends TransportOptions
19
19
  canProduceByKind: CanProduceByKind;
20
20
  }
21
21
 
22
+ class ConsumerCreationTask
23
+ {
24
+ consumerOptions: ConsumerOptions;
25
+ promise: Promise<Consumer>;
26
+ resolve?: (consumer: Consumer) => void;
27
+ reject?: (error: Error) => void;
28
+
29
+ constructor(consumerOptions: ConsumerOptions)
30
+ {
31
+ this.consumerOptions = consumerOptions;
32
+ this.promise = new Promise((resolve, reject) =>
33
+ {
34
+ this.resolve = resolve;
35
+ this.reject = reject;
36
+ });
37
+ }
38
+ }
39
+
22
40
  export type TransportOptions =
23
41
  {
24
42
  id: string;
@@ -165,6 +183,10 @@ export class Transport extends EnhancedEventEmitter
165
183
  private _probatorConsumerCreated = false;
166
184
  // AwaitQueue instance to make async tasks happen sequentially.
167
185
  private readonly _awaitQueue = new AwaitQueue({ ClosedErrorClass: InvalidStateError });
186
+ // Consumer creation tasks awaiting to be processed.
187
+ private _pendingConsumerTasks: ConsumerCreationTask[] = [];
188
+ // Consumer creation in progress flag.
189
+ private _consumerCreationInProgress = false;
168
190
  // Observer instance.
169
191
  protected readonly _observer = new EnhancedEventEmitter();
170
192
 
@@ -583,68 +605,33 @@ export class Transport extends EnhancedEventEmitter
583
605
  else if (appData && typeof appData !== 'object')
584
606
  throw new TypeError('if given, appData must be an object');
585
607
 
586
- // Enqueue command.
587
- return this._awaitQueue.push(
588
- async () =>
589
- {
590
- // Ensure the device can consume it.
591
- const canConsume = ortc.canReceive(
592
- rtpParameters, this._extendedRtpCapabilities);
593
-
594
- if (!canConsume)
595
- throw new UnsupportedError('cannot consume this Producer');
596
-
597
- const { localId, rtpReceiver, track } =
598
- await this._handler.receive({ trackId: id, kind, rtpParameters });
599
-
600
- const consumer = new Consumer(
601
- {
602
- id,
603
- localId,
604
- producerId,
605
- rtpReceiver,
606
- track,
607
- rtpParameters,
608
- appData
609
- });
610
-
611
- this._consumers.set(consumer.id, consumer);
612
- this._handleConsumer(consumer);
613
-
614
- // If this is the first video Consumer and the Consumer for RTP probation
615
- // has not yet been created, create it now.
616
- if (!this._probatorConsumerCreated && kind === 'video')
617
- {
618
- try
619
- {
620
- const probatorRtpParameters =
621
- ortc.generateProbatorRtpParameters(consumer.rtpParameters);
608
+ // Ensure the device can consume it.
609
+ const canConsume = ortc.canReceive(
610
+ rtpParameters, this._extendedRtpCapabilities);
622
611
 
623
- await this._handler.receive(
624
- {
625
- trackId : 'probator',
626
- kind : 'video',
627
- rtpParameters : probatorRtpParameters
628
- });
612
+ if (!canConsume)
613
+ throw new UnsupportedError('cannot consume this Producer');
629
614
 
630
- logger.debug('consume() | Consumer for RTP probation created');
615
+ const consumerCreationTask = new ConsumerCreationTask(
616
+ {
617
+ id,
618
+ producerId,
619
+ kind,
620
+ rtpParameters,
621
+ appData
622
+ }
623
+ );
631
624
 
632
- this._probatorConsumerCreated = true;
633
- }
634
- catch (error)
635
- {
636
- logger.error(
637
- 'consume() | failed to create Consumer for RTP probation:%o',
638
- error);
639
- }
640
- }
625
+ // Store the Consumer creation task.
626
+ this._pendingConsumerTasks.push(consumerCreationTask);
641
627
 
642
- // Emit observer event.
643
- this._observer.safeEmit('newconsumer', consumer);
628
+ // There is no Consumer creation in progress, create it now.
629
+ if (this._consumerCreationInProgress === false)
630
+ {
631
+ this._createPendingConsumers();
632
+ }
644
633
 
645
- return consumer;
646
- },
647
- 'transport.consume()');
634
+ return consumerCreationTask.promise;
648
635
  }
649
636
 
650
637
  /**
@@ -788,6 +775,123 @@ export class Transport extends EnhancedEventEmitter
788
775
  'transport.consumeData()');
789
776
  }
790
777
 
778
+ // This method is guaranteed to never throw.
779
+ async _createPendingConsumers(): Promise<void>
780
+ {
781
+ this._awaitQueue.push(
782
+ async () =>
783
+ {
784
+ this._consumerCreationInProgress = true;
785
+
786
+ const pendingConsumerTasks = [ ...this._pendingConsumerTasks ];
787
+
788
+ // Clear pending Consumer tasks.
789
+ this._pendingConsumerTasks = [];
790
+
791
+ // Video Consumer in order to create the probator.
792
+ let videoConsumerForProbator: Consumer | undefined = undefined;
793
+
794
+ // Fill options list.
795
+ const optionsList: HandlerReceiveOptions[] = [];
796
+
797
+ for (const task of pendingConsumerTasks)
798
+ {
799
+ const { id, kind, rtpParameters } = task.consumerOptions;
800
+
801
+ optionsList.push({
802
+ trackId : id!,
803
+ kind : kind as 'audio' | 'video',
804
+ rtpParameters
805
+ });
806
+ }
807
+
808
+ try
809
+ {
810
+ const results = await this._handler.receive(optionsList);
811
+
812
+ for (let idx=0; idx < results.length; idx++)
813
+ {
814
+ const task = pendingConsumerTasks[idx];
815
+ const result = results[idx];
816
+ const { id, producerId, kind, rtpParameters, appData } = task.consumerOptions;
817
+ const { localId, rtpReceiver, track } = result;
818
+ const consumer = new Consumer(
819
+ {
820
+ id : id!,
821
+ localId,
822
+ producerId : producerId!,
823
+ rtpReceiver,
824
+ track,
825
+ rtpParameters,
826
+ appData
827
+ });
828
+
829
+ this._consumers.set(consumer.id, consumer);
830
+ this._handleConsumer(consumer);
831
+
832
+ // If this is the first video Consumer and the Consumer for RTP probation
833
+ // has not yet been created, it's time to create it.
834
+ if (!this._probatorConsumerCreated && !videoConsumerForProbator && kind === 'video')
835
+ {
836
+ videoConsumerForProbator = consumer;
837
+ }
838
+
839
+ // Emit observer event.
840
+ this._observer.safeEmit('newconsumer', consumer);
841
+
842
+ task.resolve!(consumer);
843
+ }
844
+ }
845
+ catch (error)
846
+ {
847
+ for (const task of pendingConsumerTasks)
848
+ {
849
+ task.reject!(error as Error);
850
+ }
851
+ }
852
+
853
+ // If RTP probation must be handled, do it now.
854
+ if (videoConsumerForProbator)
855
+ {
856
+ try
857
+ {
858
+ const probatorRtpParameters =
859
+ ortc.generateProbatorRtpParameters(videoConsumerForProbator!.rtpParameters);
860
+
861
+ await this._handler.receive(
862
+ [ {
863
+ trackId : 'probator',
864
+ kind : 'video',
865
+ rtpParameters : probatorRtpParameters
866
+ } ]);
867
+
868
+ logger.debug('_createPendingConsumers() | Consumer for RTP probation created');
869
+
870
+ this._probatorConsumerCreated = true;
871
+ }
872
+ catch (error)
873
+ {
874
+ logger.error(
875
+ '_createPendingConsumers() | failed to create Consumer for RTP probation:%o',
876
+ error);
877
+ }
878
+ }
879
+
880
+ this._consumerCreationInProgress = false;
881
+ },
882
+ 'transport._createPendingConsumers()')
883
+ .then(() =>
884
+ {
885
+ // There are pending Consumer tasks, enqueue their creation.
886
+ if (this._pendingConsumerTasks.length > 0)
887
+ {
888
+ this._createPendingConsumers();
889
+ }
890
+ })
891
+ // NOTE: We only get here when the await queue is closed.
892
+ .catch(() => {});
893
+ }
894
+
791
895
  _handleHandler(): void
792
896
  {
793
897
  const handler = this._handler;