livekit-client 2.15.9 → 2.15.11

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.
@@ -202,6 +202,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
202
202
 
203
203
  private reliableReceivedState: TTLMap<string, number> = new TTLMap(reliabeReceiveStateTTL);
204
204
 
205
+ private midToTrackId: { [key: string]: string } = {};
206
+
205
207
  constructor(private options: InternalRoomOptions) {
206
208
  super();
207
209
  this.log = getLogger(options.loggerName ?? LoggerNames.Engine);
@@ -499,11 +501,17 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
499
501
 
500
502
  private setupSignalClientCallbacks() {
501
503
  // configure signaling client
502
- this.client.onAnswer = async (sd, offerId) => {
504
+ this.client.onAnswer = async (sd, offerId, midToTrackId) => {
503
505
  if (!this.pcManager) {
504
506
  return;
505
507
  }
506
- this.log.debug('received server answer', { ...this.logContext, RTCSdpType: sd.type });
508
+ this.log.debug('received server answer', {
509
+ ...this.logContext,
510
+ RTCSdpType: sd.type,
511
+ sdp: sd.sdp,
512
+ midToTrackId,
513
+ });
514
+ this.midToTrackId = midToTrackId;
507
515
  await this.pcManager.setPublisherAnswer(sd, offerId);
508
516
  };
509
517
 
@@ -517,11 +525,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
517
525
  };
518
526
 
519
527
  // when server creates an offer for the client
520
- this.client.onOffer = async (sd, offerId) => {
528
+ this.client.onOffer = async (sd, offerId, midToTrackId) => {
521
529
  this.latestRemoteOfferId = offerId;
522
530
  if (!this.pcManager) {
523
531
  return;
524
532
  }
533
+ this.midToTrackId = midToTrackId;
525
534
  const answer = await this.pcManager.createSubscriberAnswerFromOffer(sd, offerId);
526
535
  if (answer) {
527
536
  this.client.sendAnswer(answer, offerId);
@@ -1625,6 +1634,16 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1625
1634
  window.removeEventListener('online', this.handleBrowserOnLine);
1626
1635
  }
1627
1636
  }
1637
+
1638
+ getTrackIdForReceiver(receiver: RTCRtpReceiver): string | undefined {
1639
+ const mid = this.pcManager?.getMidForReceiver(receiver);
1640
+ if (mid) {
1641
+ const match = Object.entries(this.midToTrackId).find(([key]) => key === mid);
1642
+ if (match) {
1643
+ return match[1];
1644
+ }
1645
+ }
1646
+ }
1628
1647
  }
1629
1648
 
1630
1649
  class SignalReconnectError extends Error {}
package/src/room/Room.ts CHANGED
@@ -1370,6 +1370,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1370
1370
  // We'll defer these events until when the room is connected or eventually disconnected.
1371
1371
  if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
1372
1372
  const reconnectedHandler = () => {
1373
+ this.log.debug('deferring on track for later', {
1374
+ mediaTrackId: mediaTrack.id,
1375
+ mediaStreamId: stream.id,
1376
+ tracksInStream: stream.getTracks().map((track) => track.id),
1377
+ });
1373
1378
  this.onTrackAdded(mediaTrack, stream, receiver);
1374
1379
  cleanup();
1375
1380
  };
@@ -1416,6 +1421,28 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1416
1421
  return;
1417
1422
  }
1418
1423
 
1424
+ // in single peer connection case, the trackID is locally generated,
1425
+ // not the TR_ prefixed one generated by the server,
1426
+ // use `mid` to find the appropriate track.
1427
+ if (!trackId.startsWith('TR')) {
1428
+ const id = this.engine.getTrackIdForReceiver(receiver);
1429
+ if (!id) {
1430
+ this.log.error(
1431
+ `Tried to add a track whose 'sid' could not be found for a participant, that's not present. Sid: ${participantSid}`,
1432
+ this.logContext,
1433
+ );
1434
+ return;
1435
+ }
1436
+
1437
+ trackId = id;
1438
+ }
1439
+ if (!trackId.startsWith('TR')) {
1440
+ this.log.warn(
1441
+ `Tried to add a track whose 'sid' could not be determined for a participant, that's not present. Sid: ${participantSid}, streamId: ${streamId}, trackId: ${trackId}`,
1442
+ { ...this.logContext, rpID: participantSid, streamId, trackId },
1443
+ );
1444
+ }
1445
+
1419
1446
  let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
1420
1447
  if (this.options.adaptiveStream) {
1421
1448
  if (typeof this.options.adaptiveStream === 'object') {
@@ -2541,6 +2568,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
2541
2568
  if (event !== RoomEvent.ActiveSpeakersChanged && event !== RoomEvent.TranscriptionReceived) {
2542
2569
  // only extract logContext from arguments in order to avoid logging the whole object tree
2543
2570
  const minimizedArgs = mapArgs(args).filter((arg: unknown) => arg !== undefined);
2571
+ if (event === RoomEvent.TrackSubscribed || event === RoomEvent.TrackUnsubscribed) {
2572
+ this.log.trace(`subscribe trace: ${event}`, {
2573
+ ...this.logContext,
2574
+ event,
2575
+ args: minimizedArgs,
2576
+ });
2577
+ }
2544
2578
  this.log.debug(`room event ${event}`, { ...this.logContext, event, args: minimizedArgs });
2545
2579
  }
2546
2580
  return super.emit(event, ...args);
@@ -1771,9 +1771,10 @@ export default class LocalParticipant extends Participant {
1771
1771
  destinationIdentity,
1772
1772
  method,
1773
1773
  payload,
1774
- responseTimeout = 10000,
1774
+ responseTimeout = 15000,
1775
1775
  }: PerformRpcParams): Promise<string> {
1776
- const maxRoundTripLatency = 2000;
1776
+ const maxRoundTripLatency = 7000;
1777
+ const minEffectiveTimeout = maxRoundTripLatency + 1000;
1777
1778
 
1778
1779
  return new Promise(async (resolve, reject) => {
1779
1780
  if (byteLength(payload) > MAX_PAYLOAD_BYTES) {
@@ -1789,14 +1790,9 @@ export default class LocalParticipant extends Participant {
1789
1790
  return;
1790
1791
  }
1791
1792
 
1793
+ const effectiveTimeout = Math.max(responseTimeout, minEffectiveTimeout);
1792
1794
  const id = crypto.randomUUID();
1793
- await this.publishRpcRequest(
1794
- destinationIdentity,
1795
- id,
1796
- method,
1797
- payload,
1798
- responseTimeout - maxRoundTripLatency,
1799
- );
1795
+ await this.publishRpcRequest(destinationIdentity, id, method, payload, effectiveTimeout);
1800
1796
 
1801
1797
  const ackTimeoutId = setTimeout(() => {
1802
1798
  this.pendingAcks.delete(id);
package/src/room/rpc.ts CHANGED
@@ -11,7 +11,12 @@ export interface PerformRpcParams {
11
11
  method: string;
12
12
  /** The method payload */
13
13
  payload: string;
14
- /** Timeout for receiving a response after initial connection (milliseconds). Default: 10000 */
14
+ /**
15
+ * Timeout for receiving a response after the initial connection (milliseconds).
16
+ * If a value less than 8000ms is provided, it will be automatically clamped to 8000ms
17
+ * to ensure sufficient time for round-trip latency buffering.
18
+ * Default: 15000ms.
19
+ */
15
20
  responseTimeout?: number;
16
21
  }
17
22