@whereby.com/media 2.6.2 → 2.6.4

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/dist/index.cjs CHANGED
@@ -2352,15 +2352,15 @@ class Session {
2352
2352
  return this.pc && this.pc.connectionState === "connected";
2353
2353
  }
2354
2354
  replaceTrack(oldTrack, newTrack) {
2355
+ var _a;
2355
2356
  const pc = this.pc;
2356
2357
  if (!pc)
2357
2358
  return false;
2358
2359
  const senders = pc.getSenders();
2359
- if (!oldTrack) {
2360
- oldTrack = (senders.find((s) => s.track && s.track.kind === newTrack.kind) || {}).track;
2361
- }
2360
+ const oldTrackFallback = (_a = senders.find((s) => { var _a; return ((_a = s.track) === null || _a === void 0 ? void 0 : _a.kind) === newTrack.kind; })) === null || _a === void 0 ? void 0 : _a.track;
2361
+ const oldTrackToReplace = oldTrack || oldTrackFallback;
2362
2362
  if (window.RTCRtpSender && window.RTCRtpSender.prototype.replaceTrack) {
2363
- if (oldTrack) {
2363
+ if (oldTrackToReplace) {
2364
2364
  const process = () => {
2365
2365
  for (let i = 0; i < senders.length; i++) {
2366
2366
  const sender = senders[i];
@@ -2368,39 +2368,52 @@ class Session {
2368
2368
  if ((track === null || track === void 0 ? void 0 : track.id) === newTrack.id) {
2369
2369
  return Promise.resolve(newTrack);
2370
2370
  }
2371
- if ((track === null || track === void 0 ? void 0 : track.id) === oldTrack.id) {
2371
+ if ((track === null || track === void 0 ? void 0 : track.id) === oldTrackToReplace.id) {
2372
2372
  return senders[i].replaceTrack(newTrack);
2373
2373
  }
2374
2374
  }
2375
2375
  return null;
2376
2376
  };
2377
- let result = process();
2377
+ const result = process();
2378
2378
  if (result) {
2379
2379
  return result;
2380
2380
  }
2381
- let resolve = null;
2382
- let reject = null;
2383
- result = new Promise((_resolve, _reject) => {
2384
- resolve = _resolve;
2385
- reject = _reject;
2386
- });
2387
- let retried = 0;
2388
- let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2389
- const trackReplacedPromise = process();
2390
- if (!trackReplacedPromise) {
2391
- if (3 < ++retried) {
2392
- clearInterval(timer);
2393
- timer = null;
2394
- reject("No sender track to replace");
2381
+ return new Promise((resolve, reject) => {
2382
+ let retried = 0;
2383
+ let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2384
+ const trackReplacedPromise = process();
2385
+ if (!trackReplacedPromise) {
2386
+ if (3 < ++retried) {
2387
+ clearInterval(timer);
2388
+ timer = null;
2389
+ const sendersAnalytics = senders.map((s) => {
2390
+ const track = s.track;
2391
+ if (track) {
2392
+ return {
2393
+ id: track.id,
2394
+ kind: track.kind,
2395
+ readyState: track.readyState,
2396
+ replaced: track.replaced,
2397
+ };
2398
+ }
2399
+ });
2400
+ rtcStats.sendEvent("P2PReplaceTrackFailed", {
2401
+ newTrackId: newTrack === null || newTrack === void 0 ? void 0 : newTrack.id,
2402
+ oldTrackId: oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id,
2403
+ oldTrackFallbackId: oldTrackFallback === null || oldTrackFallback === void 0 ? void 0 : oldTrackFallback.id,
2404
+ sendersCount: senders === null || senders === void 0 ? void 0 : senders.length,
2405
+ sendersAnalytics,
2406
+ });
2407
+ reject("No sender track to replace");
2408
+ }
2409
+ return;
2395
2410
  }
2396
- return;
2397
- }
2398
- clearInterval(timer);
2399
- timer = null;
2400
- const trackReplaced = yield trackReplacedPromise;
2401
- resolve(trackReplaced);
2402
- }), 1000);
2403
- return result;
2411
+ clearInterval(timer);
2412
+ timer = null;
2413
+ const trackReplaced = yield trackReplacedPromise;
2414
+ resolve(trackReplaced);
2415
+ }), 1000);
2416
+ });
2404
2417
  }
2405
2418
  const stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id)) || this.streams[0];
2406
2419
  if (!stream) {
@@ -2410,14 +2423,16 @@ class Session {
2410
2423
  }
2411
2424
  if (!this.canModifyPeerConnection()) {
2412
2425
  this.pending.push(() => {
2413
- this.replaceTrack(oldTrack, newTrack);
2426
+ this.replaceTrack(oldTrackToReplace, newTrack);
2414
2427
  });
2415
2428
  return;
2416
2429
  }
2417
2430
  this.isOperationPending = true;
2418
2431
  const onn = pc.onnegotiationneeded;
2419
2432
  pc.onnegotiationneeded = null;
2420
- this.removeTrack(oldTrack);
2433
+ if (oldTrackToReplace) {
2434
+ this.removeTrack(oldTrackToReplace);
2435
+ }
2421
2436
  this.addTrack(newTrack);
2422
2437
  setTimeout(() => {
2423
2438
  pc.onnegotiationneeded = onn;
@@ -4548,6 +4563,14 @@ class VegaRtcManager {
4548
4563
  });
4549
4564
  this._networkIsDetectedUpBySignal = false;
4550
4565
  this._cpuOveruseDetected = false;
4566
+ this.analytics = {
4567
+ vegaJoinFailed: 0,
4568
+ vegaJoinWithoutVegaConnection: 0,
4569
+ vegaCreateTransportWithoutVegaConnection: 0,
4570
+ vegaIceRestarts: 0,
4571
+ vegaIceRestartMissingTransport: 0,
4572
+ vegaIceRestartWrongTransportId: 0,
4573
+ };
4551
4574
  }
4552
4575
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4553
4576
  var _a, _b, _c;
@@ -4691,10 +4714,16 @@ class VegaRtcManager {
4691
4714
  _join() {
4692
4715
  return __awaiter(this, void 0, void 0, function* () {
4693
4716
  var _a, _b;
4694
- logger$2.info("join()");
4717
+ logger$2.info("_join()");
4695
4718
  this._emitToPWA(rtcManagerEvents.SFU_CONNECTION_OPEN);
4696
4719
  try {
4697
- const { routerRtpCapabilities } = yield this._vegaConnection.request("getCapabilities");
4720
+ if (!this._vegaConnection) {
4721
+ logger$2.error("_join() No VegaConnection found");
4722
+ this.analytics.vegaJoinWithoutVegaConnection++;
4723
+ rtcStats.sendEvent("JoinWithoutVegaConnection", {});
4724
+ throw new Error("No VegaConnection found");
4725
+ }
4726
+ const { routerRtpCapabilities } = (yield this._vegaConnection.request("getCapabilities"));
4698
4727
  if (!this._routerRtpCapabilities) {
4699
4728
  const modifiedCapabilities = modifyMediaCapabilities(routerRtpCapabilities, Object.assign(Object.assign({}, this._features), { vp9On: this._features.sfuVp9On }));
4700
4729
  this._routerRtpCapabilities = modifiedCapabilities;
@@ -4719,14 +4748,22 @@ class VegaRtcManager {
4719
4748
  }
4720
4749
  catch (error) {
4721
4750
  logger$2.error("_join() [error:%o]", error);
4751
+ this.analytics.vegaJoinFailed++;
4752
+ rtcStats.sendEvent("VegaJoinFailed", { error });
4722
4753
  }
4723
4754
  });
4724
4755
  }
4725
4756
  _createTransport(send) {
4726
4757
  return __awaiter(this, void 0, void 0, function* () {
4727
4758
  var _a;
4759
+ if (!this._vegaConnection) {
4760
+ logger$2.error("_createTransport() No VegaConnection found");
4761
+ this.analytics.vegaCreateTransportWithoutVegaConnection++;
4762
+ rtcStats.sendEvent("CreateTransportWithoutVegaConnection", {});
4763
+ throw new Error("No VegaConnection found");
4764
+ }
4728
4765
  const creator = send ? "createSendTransport" : "createRecvTransport";
4729
- const transportOptions = yield this._vegaConnection.request("createTransport", {
4766
+ const optionsFromSfu = (yield this._vegaConnection.request("createTransport", {
4730
4767
  producing: send,
4731
4768
  consuming: !send,
4732
4769
  enableTcp: true,
@@ -4741,7 +4778,8 @@ class VegaRtcManager {
4741
4778
  maxSctpMessageSize: 262144,
4742
4779
  sctpSendBufferSize: 262144,
4743
4780
  },
4744
- });
4781
+ }));
4782
+ const transportOptions = Object.assign({}, optionsFromSfu);
4745
4783
  transportOptions.iceServers = turnServerOverride(this._features.turnServersOn ? this._turnServers : this._iceServers, this._features.turnServerOverrideHost);
4746
4784
  maybeTurnOnly(transportOptions, this._features);
4747
4785
  const transport = (_a = (yield this._mediasoupDeviceInitializedAsync)) === null || _a === void 0 ? void 0 : _a[creator](transportOptions);
@@ -4751,18 +4789,22 @@ class VegaRtcManager {
4751
4789
  return;
4752
4790
  }
4753
4791
  if (send) {
4754
- if (this._sndTransportIceRestartPromise) {
4792
+ if (this._sndTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4755
4793
  return;
4756
4794
  }
4757
- this._sndTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4795
+ this._sndTransportIceRestartPromise = this._restartIce("send", transport.id)
4796
+ .catch((e) => logger$2.error(e))
4797
+ .finally(() => {
4758
4798
  this._sndTransportIceRestartPromise = null;
4759
4799
  });
4760
4800
  }
4761
4801
  else {
4762
- if (this._rcvTransportIceRestartPromise) {
4802
+ if (this._rcvTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4763
4803
  return;
4764
4804
  }
4765
- this._rcvTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4805
+ this._rcvTransportIceRestartPromise = this._restartIce("recv", transport.id)
4806
+ .catch((e) => logger$2.error(e))
4807
+ .finally(() => {
4766
4808
  this._rcvTransportIceRestartPromise = null;
4767
4809
  });
4768
4810
  }
@@ -4783,13 +4825,13 @@ class VegaRtcManager {
4783
4825
  var _b;
4784
4826
  try {
4785
4827
  const { paused } = appData;
4786
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4828
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4787
4829
  transportId: transport.id,
4788
4830
  kind,
4789
4831
  rtpParameters,
4790
4832
  paused,
4791
4833
  appData,
4792
- }));
4834
+ })));
4793
4835
  callback({ id });
4794
4836
  }
4795
4837
  catch (error) {
@@ -4799,11 +4841,11 @@ class VegaRtcManager {
4799
4841
  transport === null || transport === void 0 ? void 0 : transport.on("producedata", (_a, callback_1, errback_1) => __awaiter(this, [_a, callback_1, errback_1], void 0, function* ({ appData, sctpStreamParameters, }, callback, errback) {
4800
4842
  var _b;
4801
4843
  try {
4802
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4844
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4803
4845
  transportId: transport.id,
4804
4846
  sctpStreamParameters,
4805
4847
  appData,
4806
- }));
4848
+ })));
4807
4849
  callback({ id });
4808
4850
  }
4809
4851
  catch (error) {
@@ -4817,10 +4859,22 @@ class VegaRtcManager {
4817
4859
  }
4818
4860
  });
4819
4861
  }
4820
- _restartIce(transport_1) {
4821
- return __awaiter(this, arguments, void 0, function* (transport, retried = 0) {
4822
- if (!transport || !("closed" in transport) || !("connectionState" in transport)) {
4823
- logger$2.info("_restartIce: No transport or property closed or connectionState!");
4862
+ _restartIce(direction_1, transportId_1) {
4863
+ return __awaiter(this, arguments, void 0, function* (direction, transportId, retried = 0) {
4864
+ this.analytics.vegaIceRestarts++;
4865
+ const transport = direction === "send" ? this._sendTransport : this._receiveTransport;
4866
+ if (!transport) {
4867
+ logger$2.info(`_restartIce: No transport found with id ${transportId}`);
4868
+ this.analytics.vegaIceRestartMissingTransport++;
4869
+ return;
4870
+ }
4871
+ if (transport.id !== transportId) {
4872
+ logger$2.info(`_restartIce: Transport ids does not match [expected: ${transportId}, actual: ${transport.id}]`);
4873
+ this.analytics.vegaIceRestartWrongTransportId++;
4874
+ return;
4875
+ }
4876
+ if (!("closed" in transport) || !("connectionState" in transport)) {
4877
+ logger$2.info("_restartIce: Transport is missing closed or connectionState property");
4824
4878
  return;
4825
4879
  }
4826
4880
  if (transport.closed) {
@@ -4845,7 +4899,9 @@ class VegaRtcManager {
4845
4899
  logger$2.info(`_restartIce: Connection is undefined`);
4846
4900
  return;
4847
4901
  }
4848
- const { iceParameters } = yield this._vegaConnection.request("restartIce", { transportId: transport.id });
4902
+ const { iceParameters } = (yield this._vegaConnection.request("restartIce", {
4903
+ transportId: transport.id,
4904
+ }));
4849
4905
  logger$2.info("_restartIce: ICE restart iceParameters received from SFU: ", iceParameters);
4850
4906
  const error = yield transport
4851
4907
  .restartIce({ iceParameters })
@@ -4863,7 +4919,7 @@ class VegaRtcManager {
4863
4919
  resolve(undefined);
4864
4920
  }, Math.min(RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS * Math.pow(2, retried), 60000));
4865
4921
  });
4866
- yield this._restartIce(transport, retried + 1);
4922
+ yield this._restartIce(direction, transportId, retried + 1);
4867
4923
  break;
4868
4924
  }
4869
4925
  return;
@@ -4874,7 +4930,7 @@ class VegaRtcManager {
4874
4930
  }, 60000 * Math.min(8, retried + 1));
4875
4931
  });
4876
4932
  if (transport.connectionState === "failed" || transport.connectionState === "disconnected") {
4877
- yield this._restartIce(transport, retried + 1);
4933
+ yield this._restartIce(direction, transportId, retried + 1);
4878
4934
  return;
4879
4935
  }
4880
4936
  });
package/dist/index.d.cts CHANGED
@@ -447,6 +447,7 @@ type GetDeviceDataResult = {
447
447
  };
448
448
  interface CustomMediaStreamTrack extends MediaStreamTrack {
449
449
  effectTrack?: boolean;
450
+ replaced?: boolean;
450
451
  }
451
452
 
452
453
  declare function getMediaConstraints({ disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }: GetMediaConstraintsOptions): any;
@@ -1335,7 +1336,7 @@ declare class Session {
1335
1336
  canModifyPeerConnection(): boolean;
1336
1337
  close(): void;
1337
1338
  hasConnectedPeerConnection(): any;
1338
- replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack): any;
1339
+ replaceTrack(oldTrack: CustomMediaStreamTrack | undefined | null, newTrack: MediaStreamTrack): any;
1339
1340
  changeBandwidth(bandwidth: any): void;
1340
1341
  setAudioOnly(enable: boolean, excludedTrackIds?: any[]): void;
1341
1342
  }
@@ -1483,19 +1484,30 @@ declare function createVegaConnectionManager(config: {
1483
1484
  networkIsPossiblyDown: () => void;
1484
1485
  };
1485
1486
 
1486
- type MediaSteamWhichMayHaveInboundId = MediaStream & {
1487
- inboundId?: string;
1487
+ type VegaTransportDirection = "send" | "recv";
1488
+
1489
+ type VegaAnalytics = {
1490
+ vegaJoinFailed: number;
1491
+ vegaJoinWithoutVegaConnection: number;
1492
+ vegaCreateTransportWithoutVegaConnection: number;
1493
+ vegaIceRestarts: number;
1494
+ vegaIceRestartMissingTransport: number;
1495
+ vegaIceRestartWrongTransportId: number;
1488
1496
  };
1497
+
1498
+ type MediaStreamWhichMayHaveInboundId = MediaStream & { inboundId?: string };
1499
+
1489
1500
  type ClientState = {
1490
1501
  hasAcceptedWebcamStream: Boolean;
1491
1502
  hasAcceptedScreenStream: Boolean;
1492
1503
  hasEmittedWebcamStream: Boolean;
1493
1504
  hasEmittedScreenStream: Boolean;
1494
- webcamStream?: MediaSteamWhichMayHaveInboundId;
1495
- screenStream?: MediaSteamWhichMayHaveInboundId;
1505
+ webcamStream?: MediaStreamWhichMayHaveInboundId;
1506
+ screenStream?: MediaStreamWhichMayHaveInboundId;
1496
1507
  screenShareStreamId?: string;
1497
1508
  camStreamId?: string;
1498
1509
  };
1510
+
1499
1511
  declare class VegaRtcManager implements RtcManager {
1500
1512
  _selfId: any;
1501
1513
  _room: any;
@@ -1505,7 +1517,7 @@ declare class VegaRtcManager implements RtcManager {
1505
1517
  _webrtcProvider: any;
1506
1518
  _features: any;
1507
1519
  _eventClaim?: any;
1508
- _vegaConnection: any;
1520
+ _vegaConnection: VegaConnection | null;
1509
1521
  _micAnalyser: any;
1510
1522
  _micAnalyserDebugger: any;
1511
1523
  _mediasoupDeviceInitializedAsync: Promise<Device | null>;
@@ -1559,6 +1571,7 @@ declare class VegaRtcManager implements RtcManager {
1559
1571
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1560
1572
  _networkIsDetectedUpBySignal: boolean;
1561
1573
  _cpuOveruseDetected: boolean;
1574
+ analytics: VegaAnalytics;
1562
1575
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1563
1576
  selfId: any;
1564
1577
  room: any;
@@ -1584,7 +1597,7 @@ declare class VegaRtcManager implements RtcManager {
1584
1597
  _onClose(): void;
1585
1598
  _join(): Promise<void>;
1586
1599
  _createTransport(send: any): Promise<void>;
1587
- _restartIce(transport: any, retried?: number): Promise<void>;
1600
+ _restartIce(direction: VegaTransportDirection, transportId: string, retried?: number): Promise<void>;
1588
1601
  _internalSendMic(): Promise<void>;
1589
1602
  _internalSetupMicScore(): Promise<void>;
1590
1603
  _stopMicScoreProducer(): void;
package/dist/index.d.mts CHANGED
@@ -447,6 +447,7 @@ type GetDeviceDataResult = {
447
447
  };
448
448
  interface CustomMediaStreamTrack extends MediaStreamTrack {
449
449
  effectTrack?: boolean;
450
+ replaced?: boolean;
450
451
  }
451
452
 
452
453
  declare function getMediaConstraints({ disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }: GetMediaConstraintsOptions): any;
@@ -1335,7 +1336,7 @@ declare class Session {
1335
1336
  canModifyPeerConnection(): boolean;
1336
1337
  close(): void;
1337
1338
  hasConnectedPeerConnection(): any;
1338
- replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack): any;
1339
+ replaceTrack(oldTrack: CustomMediaStreamTrack | undefined | null, newTrack: MediaStreamTrack): any;
1339
1340
  changeBandwidth(bandwidth: any): void;
1340
1341
  setAudioOnly(enable: boolean, excludedTrackIds?: any[]): void;
1341
1342
  }
@@ -1483,19 +1484,30 @@ declare function createVegaConnectionManager(config: {
1483
1484
  networkIsPossiblyDown: () => void;
1484
1485
  };
1485
1486
 
1486
- type MediaSteamWhichMayHaveInboundId = MediaStream & {
1487
- inboundId?: string;
1487
+ type VegaTransportDirection = "send" | "recv";
1488
+
1489
+ type VegaAnalytics = {
1490
+ vegaJoinFailed: number;
1491
+ vegaJoinWithoutVegaConnection: number;
1492
+ vegaCreateTransportWithoutVegaConnection: number;
1493
+ vegaIceRestarts: number;
1494
+ vegaIceRestartMissingTransport: number;
1495
+ vegaIceRestartWrongTransportId: number;
1488
1496
  };
1497
+
1498
+ type MediaStreamWhichMayHaveInboundId = MediaStream & { inboundId?: string };
1499
+
1489
1500
  type ClientState = {
1490
1501
  hasAcceptedWebcamStream: Boolean;
1491
1502
  hasAcceptedScreenStream: Boolean;
1492
1503
  hasEmittedWebcamStream: Boolean;
1493
1504
  hasEmittedScreenStream: Boolean;
1494
- webcamStream?: MediaSteamWhichMayHaveInboundId;
1495
- screenStream?: MediaSteamWhichMayHaveInboundId;
1505
+ webcamStream?: MediaStreamWhichMayHaveInboundId;
1506
+ screenStream?: MediaStreamWhichMayHaveInboundId;
1496
1507
  screenShareStreamId?: string;
1497
1508
  camStreamId?: string;
1498
1509
  };
1510
+
1499
1511
  declare class VegaRtcManager implements RtcManager {
1500
1512
  _selfId: any;
1501
1513
  _room: any;
@@ -1505,7 +1517,7 @@ declare class VegaRtcManager implements RtcManager {
1505
1517
  _webrtcProvider: any;
1506
1518
  _features: any;
1507
1519
  _eventClaim?: any;
1508
- _vegaConnection: any;
1520
+ _vegaConnection: VegaConnection | null;
1509
1521
  _micAnalyser: any;
1510
1522
  _micAnalyserDebugger: any;
1511
1523
  _mediasoupDeviceInitializedAsync: Promise<Device | null>;
@@ -1559,6 +1571,7 @@ declare class VegaRtcManager implements RtcManager {
1559
1571
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1560
1572
  _networkIsDetectedUpBySignal: boolean;
1561
1573
  _cpuOveruseDetected: boolean;
1574
+ analytics: VegaAnalytics;
1562
1575
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1563
1576
  selfId: any;
1564
1577
  room: any;
@@ -1584,7 +1597,7 @@ declare class VegaRtcManager implements RtcManager {
1584
1597
  _onClose(): void;
1585
1598
  _join(): Promise<void>;
1586
1599
  _createTransport(send: any): Promise<void>;
1587
- _restartIce(transport: any, retried?: number): Promise<void>;
1600
+ _restartIce(direction: VegaTransportDirection, transportId: string, retried?: number): Promise<void>;
1588
1601
  _internalSendMic(): Promise<void>;
1589
1602
  _internalSetupMicScore(): Promise<void>;
1590
1603
  _stopMicScoreProducer(): void;
package/dist/index.d.ts CHANGED
@@ -447,6 +447,7 @@ type GetDeviceDataResult = {
447
447
  };
448
448
  interface CustomMediaStreamTrack extends MediaStreamTrack {
449
449
  effectTrack?: boolean;
450
+ replaced?: boolean;
450
451
  }
451
452
 
452
453
  declare function getMediaConstraints({ disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }: GetMediaConstraintsOptions): any;
@@ -1335,7 +1336,7 @@ declare class Session {
1335
1336
  canModifyPeerConnection(): boolean;
1336
1337
  close(): void;
1337
1338
  hasConnectedPeerConnection(): any;
1338
- replaceTrack(oldTrack: MediaStreamTrack, newTrack: MediaStreamTrack): any;
1339
+ replaceTrack(oldTrack: CustomMediaStreamTrack | undefined | null, newTrack: MediaStreamTrack): any;
1339
1340
  changeBandwidth(bandwidth: any): void;
1340
1341
  setAudioOnly(enable: boolean, excludedTrackIds?: any[]): void;
1341
1342
  }
@@ -1483,19 +1484,30 @@ declare function createVegaConnectionManager(config: {
1483
1484
  networkIsPossiblyDown: () => void;
1484
1485
  };
1485
1486
 
1486
- type MediaSteamWhichMayHaveInboundId = MediaStream & {
1487
- inboundId?: string;
1487
+ type VegaTransportDirection = "send" | "recv";
1488
+
1489
+ type VegaAnalytics = {
1490
+ vegaJoinFailed: number;
1491
+ vegaJoinWithoutVegaConnection: number;
1492
+ vegaCreateTransportWithoutVegaConnection: number;
1493
+ vegaIceRestarts: number;
1494
+ vegaIceRestartMissingTransport: number;
1495
+ vegaIceRestartWrongTransportId: number;
1488
1496
  };
1497
+
1498
+ type MediaStreamWhichMayHaveInboundId = MediaStream & { inboundId?: string };
1499
+
1489
1500
  type ClientState = {
1490
1501
  hasAcceptedWebcamStream: Boolean;
1491
1502
  hasAcceptedScreenStream: Boolean;
1492
1503
  hasEmittedWebcamStream: Boolean;
1493
1504
  hasEmittedScreenStream: Boolean;
1494
- webcamStream?: MediaSteamWhichMayHaveInboundId;
1495
- screenStream?: MediaSteamWhichMayHaveInboundId;
1505
+ webcamStream?: MediaStreamWhichMayHaveInboundId;
1506
+ screenStream?: MediaStreamWhichMayHaveInboundId;
1496
1507
  screenShareStreamId?: string;
1497
1508
  camStreamId?: string;
1498
1509
  };
1510
+
1499
1511
  declare class VegaRtcManager implements RtcManager {
1500
1512
  _selfId: any;
1501
1513
  _room: any;
@@ -1505,7 +1517,7 @@ declare class VegaRtcManager implements RtcManager {
1505
1517
  _webrtcProvider: any;
1506
1518
  _features: any;
1507
1519
  _eventClaim?: any;
1508
- _vegaConnection: any;
1520
+ _vegaConnection: VegaConnection | null;
1509
1521
  _micAnalyser: any;
1510
1522
  _micAnalyserDebugger: any;
1511
1523
  _mediasoupDeviceInitializedAsync: Promise<Device | null>;
@@ -1559,6 +1571,7 @@ declare class VegaRtcManager implements RtcManager {
1559
1571
  _vegaConnectionManager?: ReturnType<typeof createVegaConnectionManager>;
1560
1572
  _networkIsDetectedUpBySignal: boolean;
1561
1573
  _cpuOveruseDetected: boolean;
1574
+ analytics: VegaAnalytics;
1562
1575
  constructor({ selfId, room, emitter, serverSocket, webrtcProvider, features, eventClaim, }: {
1563
1576
  selfId: any;
1564
1577
  room: any;
@@ -1584,7 +1597,7 @@ declare class VegaRtcManager implements RtcManager {
1584
1597
  _onClose(): void;
1585
1598
  _join(): Promise<void>;
1586
1599
  _createTransport(send: any): Promise<void>;
1587
- _restartIce(transport: any, retried?: number): Promise<void>;
1600
+ _restartIce(direction: VegaTransportDirection, transportId: string, retried?: number): Promise<void>;
1588
1601
  _internalSendMic(): Promise<void>;
1589
1602
  _internalSetupMicScore(): Promise<void>;
1590
1603
  _stopMicScoreProducer(): void;
package/dist/index.mjs CHANGED
@@ -2331,15 +2331,15 @@ class Session {
2331
2331
  return this.pc && this.pc.connectionState === "connected";
2332
2332
  }
2333
2333
  replaceTrack(oldTrack, newTrack) {
2334
+ var _a;
2334
2335
  const pc = this.pc;
2335
2336
  if (!pc)
2336
2337
  return false;
2337
2338
  const senders = pc.getSenders();
2338
- if (!oldTrack) {
2339
- oldTrack = (senders.find((s) => s.track && s.track.kind === newTrack.kind) || {}).track;
2340
- }
2339
+ const oldTrackFallback = (_a = senders.find((s) => { var _a; return ((_a = s.track) === null || _a === void 0 ? void 0 : _a.kind) === newTrack.kind; })) === null || _a === void 0 ? void 0 : _a.track;
2340
+ const oldTrackToReplace = oldTrack || oldTrackFallback;
2341
2341
  if (window.RTCRtpSender && window.RTCRtpSender.prototype.replaceTrack) {
2342
- if (oldTrack) {
2342
+ if (oldTrackToReplace) {
2343
2343
  const process = () => {
2344
2344
  for (let i = 0; i < senders.length; i++) {
2345
2345
  const sender = senders[i];
@@ -2347,39 +2347,52 @@ class Session {
2347
2347
  if ((track === null || track === void 0 ? void 0 : track.id) === newTrack.id) {
2348
2348
  return Promise.resolve(newTrack);
2349
2349
  }
2350
- if ((track === null || track === void 0 ? void 0 : track.id) === oldTrack.id) {
2350
+ if ((track === null || track === void 0 ? void 0 : track.id) === oldTrackToReplace.id) {
2351
2351
  return senders[i].replaceTrack(newTrack);
2352
2352
  }
2353
2353
  }
2354
2354
  return null;
2355
2355
  };
2356
- let result = process();
2356
+ const result = process();
2357
2357
  if (result) {
2358
2358
  return result;
2359
2359
  }
2360
- let resolve = null;
2361
- let reject = null;
2362
- result = new Promise((_resolve, _reject) => {
2363
- resolve = _resolve;
2364
- reject = _reject;
2365
- });
2366
- let retried = 0;
2367
- let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2368
- const trackReplacedPromise = process();
2369
- if (!trackReplacedPromise) {
2370
- if (3 < ++retried) {
2371
- clearInterval(timer);
2372
- timer = null;
2373
- reject("No sender track to replace");
2360
+ return new Promise((resolve, reject) => {
2361
+ let retried = 0;
2362
+ let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2363
+ const trackReplacedPromise = process();
2364
+ if (!trackReplacedPromise) {
2365
+ if (3 < ++retried) {
2366
+ clearInterval(timer);
2367
+ timer = null;
2368
+ const sendersAnalytics = senders.map((s) => {
2369
+ const track = s.track;
2370
+ if (track) {
2371
+ return {
2372
+ id: track.id,
2373
+ kind: track.kind,
2374
+ readyState: track.readyState,
2375
+ replaced: track.replaced,
2376
+ };
2377
+ }
2378
+ });
2379
+ rtcStats.sendEvent("P2PReplaceTrackFailed", {
2380
+ newTrackId: newTrack === null || newTrack === void 0 ? void 0 : newTrack.id,
2381
+ oldTrackId: oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id,
2382
+ oldTrackFallbackId: oldTrackFallback === null || oldTrackFallback === void 0 ? void 0 : oldTrackFallback.id,
2383
+ sendersCount: senders === null || senders === void 0 ? void 0 : senders.length,
2384
+ sendersAnalytics,
2385
+ });
2386
+ reject("No sender track to replace");
2387
+ }
2388
+ return;
2374
2389
  }
2375
- return;
2376
- }
2377
- clearInterval(timer);
2378
- timer = null;
2379
- const trackReplaced = yield trackReplacedPromise;
2380
- resolve(trackReplaced);
2381
- }), 1000);
2382
- return result;
2390
+ clearInterval(timer);
2391
+ timer = null;
2392
+ const trackReplaced = yield trackReplacedPromise;
2393
+ resolve(trackReplaced);
2394
+ }), 1000);
2395
+ });
2383
2396
  }
2384
2397
  const stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id)) || this.streams[0];
2385
2398
  if (!stream) {
@@ -2389,14 +2402,16 @@ class Session {
2389
2402
  }
2390
2403
  if (!this.canModifyPeerConnection()) {
2391
2404
  this.pending.push(() => {
2392
- this.replaceTrack(oldTrack, newTrack);
2405
+ this.replaceTrack(oldTrackToReplace, newTrack);
2393
2406
  });
2394
2407
  return;
2395
2408
  }
2396
2409
  this.isOperationPending = true;
2397
2410
  const onn = pc.onnegotiationneeded;
2398
2411
  pc.onnegotiationneeded = null;
2399
- this.removeTrack(oldTrack);
2412
+ if (oldTrackToReplace) {
2413
+ this.removeTrack(oldTrackToReplace);
2414
+ }
2400
2415
  this.addTrack(newTrack);
2401
2416
  setTimeout(() => {
2402
2417
  pc.onnegotiationneeded = onn;
@@ -4527,6 +4542,14 @@ class VegaRtcManager {
4527
4542
  });
4528
4543
  this._networkIsDetectedUpBySignal = false;
4529
4544
  this._cpuOveruseDetected = false;
4545
+ this.analytics = {
4546
+ vegaJoinFailed: 0,
4547
+ vegaJoinWithoutVegaConnection: 0,
4548
+ vegaCreateTransportWithoutVegaConnection: 0,
4549
+ vegaIceRestarts: 0,
4550
+ vegaIceRestartMissingTransport: 0,
4551
+ vegaIceRestartWrongTransportId: 0,
4552
+ };
4530
4553
  }
4531
4554
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4532
4555
  var _a, _b, _c;
@@ -4670,10 +4693,16 @@ class VegaRtcManager {
4670
4693
  _join() {
4671
4694
  return __awaiter(this, void 0, void 0, function* () {
4672
4695
  var _a, _b;
4673
- logger$2.info("join()");
4696
+ logger$2.info("_join()");
4674
4697
  this._emitToPWA(rtcManagerEvents.SFU_CONNECTION_OPEN);
4675
4698
  try {
4676
- const { routerRtpCapabilities } = yield this._vegaConnection.request("getCapabilities");
4699
+ if (!this._vegaConnection) {
4700
+ logger$2.error("_join() No VegaConnection found");
4701
+ this.analytics.vegaJoinWithoutVegaConnection++;
4702
+ rtcStats.sendEvent("JoinWithoutVegaConnection", {});
4703
+ throw new Error("No VegaConnection found");
4704
+ }
4705
+ const { routerRtpCapabilities } = (yield this._vegaConnection.request("getCapabilities"));
4677
4706
  if (!this._routerRtpCapabilities) {
4678
4707
  const modifiedCapabilities = modifyMediaCapabilities(routerRtpCapabilities, Object.assign(Object.assign({}, this._features), { vp9On: this._features.sfuVp9On }));
4679
4708
  this._routerRtpCapabilities = modifiedCapabilities;
@@ -4698,14 +4727,22 @@ class VegaRtcManager {
4698
4727
  }
4699
4728
  catch (error) {
4700
4729
  logger$2.error("_join() [error:%o]", error);
4730
+ this.analytics.vegaJoinFailed++;
4731
+ rtcStats.sendEvent("VegaJoinFailed", { error });
4701
4732
  }
4702
4733
  });
4703
4734
  }
4704
4735
  _createTransport(send) {
4705
4736
  return __awaiter(this, void 0, void 0, function* () {
4706
4737
  var _a;
4738
+ if (!this._vegaConnection) {
4739
+ logger$2.error("_createTransport() No VegaConnection found");
4740
+ this.analytics.vegaCreateTransportWithoutVegaConnection++;
4741
+ rtcStats.sendEvent("CreateTransportWithoutVegaConnection", {});
4742
+ throw new Error("No VegaConnection found");
4743
+ }
4707
4744
  const creator = send ? "createSendTransport" : "createRecvTransport";
4708
- const transportOptions = yield this._vegaConnection.request("createTransport", {
4745
+ const optionsFromSfu = (yield this._vegaConnection.request("createTransport", {
4709
4746
  producing: send,
4710
4747
  consuming: !send,
4711
4748
  enableTcp: true,
@@ -4720,7 +4757,8 @@ class VegaRtcManager {
4720
4757
  maxSctpMessageSize: 262144,
4721
4758
  sctpSendBufferSize: 262144,
4722
4759
  },
4723
- });
4760
+ }));
4761
+ const transportOptions = Object.assign({}, optionsFromSfu);
4724
4762
  transportOptions.iceServers = turnServerOverride(this._features.turnServersOn ? this._turnServers : this._iceServers, this._features.turnServerOverrideHost);
4725
4763
  maybeTurnOnly(transportOptions, this._features);
4726
4764
  const transport = (_a = (yield this._mediasoupDeviceInitializedAsync)) === null || _a === void 0 ? void 0 : _a[creator](transportOptions);
@@ -4730,18 +4768,22 @@ class VegaRtcManager {
4730
4768
  return;
4731
4769
  }
4732
4770
  if (send) {
4733
- if (this._sndTransportIceRestartPromise) {
4771
+ if (this._sndTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4734
4772
  return;
4735
4773
  }
4736
- this._sndTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4774
+ this._sndTransportIceRestartPromise = this._restartIce("send", transport.id)
4775
+ .catch((e) => logger$2.error(e))
4776
+ .finally(() => {
4737
4777
  this._sndTransportIceRestartPromise = null;
4738
4778
  });
4739
4779
  }
4740
4780
  else {
4741
- if (this._rcvTransportIceRestartPromise) {
4781
+ if (this._rcvTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4742
4782
  return;
4743
4783
  }
4744
- this._rcvTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4784
+ this._rcvTransportIceRestartPromise = this._restartIce("recv", transport.id)
4785
+ .catch((e) => logger$2.error(e))
4786
+ .finally(() => {
4745
4787
  this._rcvTransportIceRestartPromise = null;
4746
4788
  });
4747
4789
  }
@@ -4762,13 +4804,13 @@ class VegaRtcManager {
4762
4804
  var _b;
4763
4805
  try {
4764
4806
  const { paused } = appData;
4765
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4807
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4766
4808
  transportId: transport.id,
4767
4809
  kind,
4768
4810
  rtpParameters,
4769
4811
  paused,
4770
4812
  appData,
4771
- }));
4813
+ })));
4772
4814
  callback({ id });
4773
4815
  }
4774
4816
  catch (error) {
@@ -4778,11 +4820,11 @@ class VegaRtcManager {
4778
4820
  transport === null || transport === void 0 ? void 0 : transport.on("producedata", (_a, callback_1, errback_1) => __awaiter(this, [_a, callback_1, errback_1], void 0, function* ({ appData, sctpStreamParameters, }, callback, errback) {
4779
4821
  var _b;
4780
4822
  try {
4781
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4823
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4782
4824
  transportId: transport.id,
4783
4825
  sctpStreamParameters,
4784
4826
  appData,
4785
- }));
4827
+ })));
4786
4828
  callback({ id });
4787
4829
  }
4788
4830
  catch (error) {
@@ -4796,10 +4838,22 @@ class VegaRtcManager {
4796
4838
  }
4797
4839
  });
4798
4840
  }
4799
- _restartIce(transport_1) {
4800
- return __awaiter(this, arguments, void 0, function* (transport, retried = 0) {
4801
- if (!transport || !("closed" in transport) || !("connectionState" in transport)) {
4802
- logger$2.info("_restartIce: No transport or property closed or connectionState!");
4841
+ _restartIce(direction_1, transportId_1) {
4842
+ return __awaiter(this, arguments, void 0, function* (direction, transportId, retried = 0) {
4843
+ this.analytics.vegaIceRestarts++;
4844
+ const transport = direction === "send" ? this._sendTransport : this._receiveTransport;
4845
+ if (!transport) {
4846
+ logger$2.info(`_restartIce: No transport found with id ${transportId}`);
4847
+ this.analytics.vegaIceRestartMissingTransport++;
4848
+ return;
4849
+ }
4850
+ if (transport.id !== transportId) {
4851
+ logger$2.info(`_restartIce: Transport ids does not match [expected: ${transportId}, actual: ${transport.id}]`);
4852
+ this.analytics.vegaIceRestartWrongTransportId++;
4853
+ return;
4854
+ }
4855
+ if (!("closed" in transport) || !("connectionState" in transport)) {
4856
+ logger$2.info("_restartIce: Transport is missing closed or connectionState property");
4803
4857
  return;
4804
4858
  }
4805
4859
  if (transport.closed) {
@@ -4824,7 +4878,9 @@ class VegaRtcManager {
4824
4878
  logger$2.info(`_restartIce: Connection is undefined`);
4825
4879
  return;
4826
4880
  }
4827
- const { iceParameters } = yield this._vegaConnection.request("restartIce", { transportId: transport.id });
4881
+ const { iceParameters } = (yield this._vegaConnection.request("restartIce", {
4882
+ transportId: transport.id,
4883
+ }));
4828
4884
  logger$2.info("_restartIce: ICE restart iceParameters received from SFU: ", iceParameters);
4829
4885
  const error = yield transport
4830
4886
  .restartIce({ iceParameters })
@@ -4842,7 +4898,7 @@ class VegaRtcManager {
4842
4898
  resolve(undefined);
4843
4899
  }, Math.min(RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS * Math.pow(2, retried), 60000));
4844
4900
  });
4845
- yield this._restartIce(transport, retried + 1);
4901
+ yield this._restartIce(direction, transportId, retried + 1);
4846
4902
  break;
4847
4903
  }
4848
4904
  return;
@@ -4853,7 +4909,7 @@ class VegaRtcManager {
4853
4909
  }, 60000 * Math.min(8, retried + 1));
4854
4910
  });
4855
4911
  if (transport.connectionState === "failed" || transport.connectionState === "disconnected") {
4856
- yield this._restartIce(transport, retried + 1);
4912
+ yield this._restartIce(direction, transportId, retried + 1);
4857
4913
  return;
4858
4914
  }
4859
4915
  });
@@ -2331,15 +2331,15 @@ class Session {
2331
2331
  return this.pc && this.pc.connectionState === "connected";
2332
2332
  }
2333
2333
  replaceTrack(oldTrack, newTrack) {
2334
+ var _a;
2334
2335
  const pc = this.pc;
2335
2336
  if (!pc)
2336
2337
  return false;
2337
2338
  const senders = pc.getSenders();
2338
- if (!oldTrack) {
2339
- oldTrack = (senders.find((s) => s.track && s.track.kind === newTrack.kind) || {}).track;
2340
- }
2339
+ const oldTrackFallback = (_a = senders.find((s) => { var _a; return ((_a = s.track) === null || _a === void 0 ? void 0 : _a.kind) === newTrack.kind; })) === null || _a === void 0 ? void 0 : _a.track;
2340
+ const oldTrackToReplace = oldTrack || oldTrackFallback;
2341
2341
  if (window.RTCRtpSender && window.RTCRtpSender.prototype.replaceTrack) {
2342
- if (oldTrack) {
2342
+ if (oldTrackToReplace) {
2343
2343
  const process = () => {
2344
2344
  for (let i = 0; i < senders.length; i++) {
2345
2345
  const sender = senders[i];
@@ -2347,39 +2347,52 @@ class Session {
2347
2347
  if ((track === null || track === void 0 ? void 0 : track.id) === newTrack.id) {
2348
2348
  return Promise.resolve(newTrack);
2349
2349
  }
2350
- if ((track === null || track === void 0 ? void 0 : track.id) === oldTrack.id) {
2350
+ if ((track === null || track === void 0 ? void 0 : track.id) === oldTrackToReplace.id) {
2351
2351
  return senders[i].replaceTrack(newTrack);
2352
2352
  }
2353
2353
  }
2354
2354
  return null;
2355
2355
  };
2356
- let result = process();
2356
+ const result = process();
2357
2357
  if (result) {
2358
2358
  return result;
2359
2359
  }
2360
- let resolve = null;
2361
- let reject = null;
2362
- result = new Promise((_resolve, _reject) => {
2363
- resolve = _resolve;
2364
- reject = _reject;
2365
- });
2366
- let retried = 0;
2367
- let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2368
- const trackReplacedPromise = process();
2369
- if (!trackReplacedPromise) {
2370
- if (3 < ++retried) {
2371
- clearInterval(timer);
2372
- timer = null;
2373
- reject("No sender track to replace");
2360
+ return new Promise((resolve, reject) => {
2361
+ let retried = 0;
2362
+ let timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
2363
+ const trackReplacedPromise = process();
2364
+ if (!trackReplacedPromise) {
2365
+ if (3 < ++retried) {
2366
+ clearInterval(timer);
2367
+ timer = null;
2368
+ const sendersAnalytics = senders.map((s) => {
2369
+ const track = s.track;
2370
+ if (track) {
2371
+ return {
2372
+ id: track.id,
2373
+ kind: track.kind,
2374
+ readyState: track.readyState,
2375
+ replaced: track.replaced,
2376
+ };
2377
+ }
2378
+ });
2379
+ rtcStats.sendEvent("P2PReplaceTrackFailed", {
2380
+ newTrackId: newTrack === null || newTrack === void 0 ? void 0 : newTrack.id,
2381
+ oldTrackId: oldTrack === null || oldTrack === void 0 ? void 0 : oldTrack.id,
2382
+ oldTrackFallbackId: oldTrackFallback === null || oldTrackFallback === void 0 ? void 0 : oldTrackFallback.id,
2383
+ sendersCount: senders === null || senders === void 0 ? void 0 : senders.length,
2384
+ sendersAnalytics,
2385
+ });
2386
+ reject("No sender track to replace");
2387
+ }
2388
+ return;
2374
2389
  }
2375
- return;
2376
- }
2377
- clearInterval(timer);
2378
- timer = null;
2379
- const trackReplaced = yield trackReplacedPromise;
2380
- resolve(trackReplaced);
2381
- }), 1000);
2382
- return result;
2390
+ clearInterval(timer);
2391
+ timer = null;
2392
+ const trackReplaced = yield trackReplacedPromise;
2393
+ resolve(trackReplaced);
2394
+ }), 1000);
2395
+ });
2383
2396
  }
2384
2397
  const stream = this.streams.find((s) => s.getTracks().find((t) => t.id === newTrack.id)) || this.streams[0];
2385
2398
  if (!stream) {
@@ -2389,14 +2402,16 @@ class Session {
2389
2402
  }
2390
2403
  if (!this.canModifyPeerConnection()) {
2391
2404
  this.pending.push(() => {
2392
- this.replaceTrack(oldTrack, newTrack);
2405
+ this.replaceTrack(oldTrackToReplace, newTrack);
2393
2406
  });
2394
2407
  return;
2395
2408
  }
2396
2409
  this.isOperationPending = true;
2397
2410
  const onn = pc.onnegotiationneeded;
2398
2411
  pc.onnegotiationneeded = null;
2399
- this.removeTrack(oldTrack);
2412
+ if (oldTrackToReplace) {
2413
+ this.removeTrack(oldTrackToReplace);
2414
+ }
2400
2415
  this.addTrack(newTrack);
2401
2416
  setTimeout(() => {
2402
2417
  pc.onnegotiationneeded = onn;
@@ -4527,6 +4542,14 @@ class VegaRtcManager {
4527
4542
  });
4528
4543
  this._networkIsDetectedUpBySignal = false;
4529
4544
  this._cpuOveruseDetected = false;
4545
+ this.analytics = {
4546
+ vegaJoinFailed: 0,
4547
+ vegaJoinWithoutVegaConnection: 0,
4548
+ vegaCreateTransportWithoutVegaConnection: 0,
4549
+ vegaIceRestarts: 0,
4550
+ vegaIceRestartMissingTransport: 0,
4551
+ vegaIceRestartWrongTransportId: 0,
4552
+ };
4530
4553
  }
4531
4554
  _updateAndScheduleMediaServersRefresh({ iceServers, turnServers, sfuServer, sfuServers, mediaserverConfigTtlSeconds, }) {
4532
4555
  var _a, _b, _c;
@@ -4670,10 +4693,16 @@ class VegaRtcManager {
4670
4693
  _join() {
4671
4694
  return __awaiter(this, void 0, void 0, function* () {
4672
4695
  var _a, _b;
4673
- logger$2.info("join()");
4696
+ logger$2.info("_join()");
4674
4697
  this._emitToPWA(rtcManagerEvents.SFU_CONNECTION_OPEN);
4675
4698
  try {
4676
- const { routerRtpCapabilities } = yield this._vegaConnection.request("getCapabilities");
4699
+ if (!this._vegaConnection) {
4700
+ logger$2.error("_join() No VegaConnection found");
4701
+ this.analytics.vegaJoinWithoutVegaConnection++;
4702
+ rtcStats.sendEvent("JoinWithoutVegaConnection", {});
4703
+ throw new Error("No VegaConnection found");
4704
+ }
4705
+ const { routerRtpCapabilities } = (yield this._vegaConnection.request("getCapabilities"));
4677
4706
  if (!this._routerRtpCapabilities) {
4678
4707
  const modifiedCapabilities = modifyMediaCapabilities(routerRtpCapabilities, Object.assign(Object.assign({}, this._features), { vp9On: this._features.sfuVp9On }));
4679
4708
  this._routerRtpCapabilities = modifiedCapabilities;
@@ -4698,14 +4727,22 @@ class VegaRtcManager {
4698
4727
  }
4699
4728
  catch (error) {
4700
4729
  logger$2.error("_join() [error:%o]", error);
4730
+ this.analytics.vegaJoinFailed++;
4731
+ rtcStats.sendEvent("VegaJoinFailed", { error });
4701
4732
  }
4702
4733
  });
4703
4734
  }
4704
4735
  _createTransport(send) {
4705
4736
  return __awaiter(this, void 0, void 0, function* () {
4706
4737
  var _a;
4738
+ if (!this._vegaConnection) {
4739
+ logger$2.error("_createTransport() No VegaConnection found");
4740
+ this.analytics.vegaCreateTransportWithoutVegaConnection++;
4741
+ rtcStats.sendEvent("CreateTransportWithoutVegaConnection", {});
4742
+ throw new Error("No VegaConnection found");
4743
+ }
4707
4744
  const creator = send ? "createSendTransport" : "createRecvTransport";
4708
- const transportOptions = yield this._vegaConnection.request("createTransport", {
4745
+ const optionsFromSfu = (yield this._vegaConnection.request("createTransport", {
4709
4746
  producing: send,
4710
4747
  consuming: !send,
4711
4748
  enableTcp: true,
@@ -4720,7 +4757,8 @@ class VegaRtcManager {
4720
4757
  maxSctpMessageSize: 262144,
4721
4758
  sctpSendBufferSize: 262144,
4722
4759
  },
4723
- });
4760
+ }));
4761
+ const transportOptions = Object.assign({}, optionsFromSfu);
4724
4762
  transportOptions.iceServers = turnServerOverride(this._features.turnServersOn ? this._turnServers : this._iceServers, this._features.turnServerOverrideHost);
4725
4763
  maybeTurnOnly(transportOptions, this._features);
4726
4764
  const transport = (_a = (yield this._mediasoupDeviceInitializedAsync)) === null || _a === void 0 ? void 0 : _a[creator](transportOptions);
@@ -4730,18 +4768,22 @@ class VegaRtcManager {
4730
4768
  return;
4731
4769
  }
4732
4770
  if (send) {
4733
- if (this._sndTransportIceRestartPromise) {
4771
+ if (this._sndTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4734
4772
  return;
4735
4773
  }
4736
- this._sndTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4774
+ this._sndTransportIceRestartPromise = this._restartIce("send", transport.id)
4775
+ .catch((e) => logger$2.error(e))
4776
+ .finally(() => {
4737
4777
  this._sndTransportIceRestartPromise = null;
4738
4778
  });
4739
4779
  }
4740
4780
  else {
4741
- if (this._rcvTransportIceRestartPromise) {
4781
+ if (this._rcvTransportIceRestartPromise || !(transport === null || transport === void 0 ? void 0 : transport.id)) {
4742
4782
  return;
4743
4783
  }
4744
- this._rcvTransportIceRestartPromise = this._restartIce(transport).finally(() => {
4784
+ this._rcvTransportIceRestartPromise = this._restartIce("recv", transport.id)
4785
+ .catch((e) => logger$2.error(e))
4786
+ .finally(() => {
4745
4787
  this._rcvTransportIceRestartPromise = null;
4746
4788
  });
4747
4789
  }
@@ -4762,13 +4804,13 @@ class VegaRtcManager {
4762
4804
  var _b;
4763
4805
  try {
4764
4806
  const { paused } = appData;
4765
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4807
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produce", {
4766
4808
  transportId: transport.id,
4767
4809
  kind,
4768
4810
  rtpParameters,
4769
4811
  paused,
4770
4812
  appData,
4771
- }));
4813
+ })));
4772
4814
  callback({ id });
4773
4815
  }
4774
4816
  catch (error) {
@@ -4778,11 +4820,11 @@ class VegaRtcManager {
4778
4820
  transport === null || transport === void 0 ? void 0 : transport.on("producedata", (_a, callback_1, errback_1) => __awaiter(this, [_a, callback_1, errback_1], void 0, function* ({ appData, sctpStreamParameters, }, callback, errback) {
4779
4821
  var _b;
4780
4822
  try {
4781
- const { id } = yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4823
+ const { id } = (yield ((_b = this._vegaConnection) === null || _b === void 0 ? void 0 : _b.request("produceData", {
4782
4824
  transportId: transport.id,
4783
4825
  sctpStreamParameters,
4784
4826
  appData,
4785
- }));
4827
+ })));
4786
4828
  callback({ id });
4787
4829
  }
4788
4830
  catch (error) {
@@ -4796,10 +4838,22 @@ class VegaRtcManager {
4796
4838
  }
4797
4839
  });
4798
4840
  }
4799
- _restartIce(transport_1) {
4800
- return __awaiter(this, arguments, void 0, function* (transport, retried = 0) {
4801
- if (!transport || !("closed" in transport) || !("connectionState" in transport)) {
4802
- logger$2.info("_restartIce: No transport or property closed or connectionState!");
4841
+ _restartIce(direction_1, transportId_1) {
4842
+ return __awaiter(this, arguments, void 0, function* (direction, transportId, retried = 0) {
4843
+ this.analytics.vegaIceRestarts++;
4844
+ const transport = direction === "send" ? this._sendTransport : this._receiveTransport;
4845
+ if (!transport) {
4846
+ logger$2.info(`_restartIce: No transport found with id ${transportId}`);
4847
+ this.analytics.vegaIceRestartMissingTransport++;
4848
+ return;
4849
+ }
4850
+ if (transport.id !== transportId) {
4851
+ logger$2.info(`_restartIce: Transport ids does not match [expected: ${transportId}, actual: ${transport.id}]`);
4852
+ this.analytics.vegaIceRestartWrongTransportId++;
4853
+ return;
4854
+ }
4855
+ if (!("closed" in transport) || !("connectionState" in transport)) {
4856
+ logger$2.info("_restartIce: Transport is missing closed or connectionState property");
4803
4857
  return;
4804
4858
  }
4805
4859
  if (transport.closed) {
@@ -4824,7 +4878,9 @@ class VegaRtcManager {
4824
4878
  logger$2.info(`_restartIce: Connection is undefined`);
4825
4879
  return;
4826
4880
  }
4827
- const { iceParameters } = yield this._vegaConnection.request("restartIce", { transportId: transport.id });
4881
+ const { iceParameters } = (yield this._vegaConnection.request("restartIce", {
4882
+ transportId: transport.id,
4883
+ }));
4828
4884
  logger$2.info("_restartIce: ICE restart iceParameters received from SFU: ", iceParameters);
4829
4885
  const error = yield transport
4830
4886
  .restartIce({ iceParameters })
@@ -4842,7 +4898,7 @@ class VegaRtcManager {
4842
4898
  resolve(undefined);
4843
4899
  }, Math.min(RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS * Math.pow(2, retried), 60000));
4844
4900
  });
4845
- yield this._restartIce(transport, retried + 1);
4901
+ yield this._restartIce(direction, transportId, retried + 1);
4846
4902
  break;
4847
4903
  }
4848
4904
  return;
@@ -4853,7 +4909,7 @@ class VegaRtcManager {
4853
4909
  }, 60000 * Math.min(8, retried + 1));
4854
4910
  });
4855
4911
  if (transport.connectionState === "failed" || transport.connectionState === "disconnected") {
4856
- yield this._restartIce(transport, retried + 1);
4912
+ yield this._restartIce(direction, transportId, retried + 1);
4857
4913
  return;
4858
4914
  }
4859
4915
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@whereby.com/media",
3
3
  "description": "Media library for Whereby",
4
- "version": "2.6.2",
4
+ "version": "2.6.4",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/whereby/sdk",
7
7
  "repository": {