livekit-client 2.18.8 → 2.18.10

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 (193) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +5609 -644
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +2898 -2431
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.pt.worker.js +2 -0
  8. package/dist/livekit-client.pt.worker.js.map +1 -0
  9. package/dist/livekit-client.pt.worker.mjs +5834 -0
  10. package/dist/livekit-client.pt.worker.mjs.map +1 -0
  11. package/dist/livekit-client.umd.js +1 -1
  12. package/dist/livekit-client.umd.js.map +1 -1
  13. package/dist/src/api/SignalClient.d.ts +2 -1
  14. package/dist/src/api/SignalClient.d.ts.map +1 -1
  15. package/dist/src/e2ee/E2eeManager.d.ts +8 -7
  16. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  17. package/dist/src/e2ee/types.d.ts +35 -8
  18. package/dist/src/e2ee/types.d.ts.map +1 -1
  19. package/dist/src/e2ee/utils.d.ts +5 -5
  20. package/dist/src/e2ee/utils.d.ts.map +1 -1
  21. package/dist/src/e2ee/worker/DataCryptor.d.ts +5 -5
  22. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  23. package/dist/src/e2ee/worker/FrameCryptor.d.ts +21 -4
  24. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  25. package/dist/src/e2ee/worker/naluUtils.d.ts +1 -1
  26. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
  27. package/dist/src/e2ee/worker/sifPayload.d.ts +7 -7
  28. package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
  29. package/dist/src/index.d.ts +4 -1
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/options.d.ts +7 -0
  32. package/dist/src/options.d.ts.map +1 -1
  33. package/dist/src/packetTrailer/PacketTrailerManager.d.ts +49 -0
  34. package/dist/src/packetTrailer/PacketTrailerManager.d.ts.map +1 -0
  35. package/dist/src/packetTrailer/packetTrailer.d.ts +32 -0
  36. package/dist/src/packetTrailer/packetTrailer.d.ts.map +1 -0
  37. package/dist/src/packetTrailer/types.d.ts +57 -0
  38. package/dist/src/packetTrailer/types.d.ts.map +1 -0
  39. package/dist/src/packetTrailer/utils.d.ts +9 -0
  40. package/dist/src/packetTrailer/utils.d.ts.map +1 -0
  41. package/dist/src/packetTrailer/worker/packetTrailer.worker.d.ts +2 -0
  42. package/dist/src/packetTrailer/worker/packetTrailer.worker.d.ts.map +1 -0
  43. package/dist/src/room/RTCEngine.d.ts +2 -1
  44. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  45. package/dist/src/room/Room.d.ts +3 -1
  46. package/dist/src/room/Room.d.ts.map +1 -1
  47. package/dist/src/room/data-track/LocalDataTrack.d.ts +2 -1
  48. package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
  49. package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -1
  50. package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
  51. package/dist/src/room/data-track/depacketizer.d.ts +12 -4
  52. package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
  53. package/dist/src/room/data-track/frame.d.ts +3 -3
  54. package/dist/src/room/data-track/frame.d.ts.map +1 -1
  55. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +3 -1
  56. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
  57. package/dist/src/room/data-track/incoming/pipeline.d.ts +4 -1
  58. package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
  59. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +2 -2
  60. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
  61. package/dist/src/room/data-track/outgoing/types.d.ts +4 -3
  62. package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
  63. package/dist/src/room/data-track/packet/extensions.d.ts +4 -4
  64. package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
  65. package/dist/src/room/data-track/packet/index.d.ts +5 -5
  66. package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
  67. package/dist/src/room/data-track/packet/serializable.d.ts +1 -1
  68. package/dist/src/room/data-track/packet/serializable.d.ts.map +1 -1
  69. package/dist/src/room/data-track/types.d.ts +7 -0
  70. package/dist/src/room/data-track/types.d.ts.map +1 -1
  71. package/dist/src/room/events.d.ts +2 -2
  72. package/dist/src/room/participant/LocalParticipant.d.ts +3 -1
  73. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  74. package/dist/src/room/participant/Participant.d.ts +1 -1
  75. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  76. package/dist/src/room/track/PacketTrailerExtractor.d.ts +19 -0
  77. package/dist/src/room/track/PacketTrailerExtractor.d.ts.map +1 -0
  78. package/dist/src/room/track/RemoteVideoTrack.d.ts +16 -0
  79. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  80. package/dist/src/room/track/Track.d.ts +1 -1
  81. package/dist/src/room/track/Track.d.ts.map +1 -1
  82. package/dist/src/room/track/create.d.ts.map +1 -1
  83. package/dist/src/room/track/options.d.ts +10 -0
  84. package/dist/src/room/track/options.d.ts.map +1 -1
  85. package/dist/src/room/track/utils.d.ts.map +1 -1
  86. package/dist/src/room/utils.d.ts +4 -3
  87. package/dist/src/room/utils.d.ts.map +1 -1
  88. package/dist/src/test/MockMediaStreamTrack.d.ts.map +1 -1
  89. package/dist/src/utils/dataPacketBuffer.d.ts +1 -1
  90. package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -1
  91. package/dist/src/version.d.ts +1 -1
  92. package/dist/ts4.2/api/SignalClient.d.ts +2 -1
  93. package/dist/ts4.2/e2ee/E2eeManager.d.ts +8 -7
  94. package/dist/ts4.2/e2ee/types.d.ts +35 -8
  95. package/dist/ts4.2/e2ee/utils.d.ts +5 -5
  96. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +5 -5
  97. package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +21 -4
  98. package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +1 -1
  99. package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +7 -7
  100. package/dist/ts4.2/index.d.ts +5 -1
  101. package/dist/ts4.2/options.d.ts +7 -0
  102. package/dist/ts4.2/packetTrailer/PacketTrailerManager.d.ts +49 -0
  103. package/dist/ts4.2/packetTrailer/packetTrailer.d.ts +32 -0
  104. package/dist/ts4.2/packetTrailer/types.d.ts +57 -0
  105. package/dist/ts4.2/packetTrailer/utils.d.ts +9 -0
  106. package/dist/ts4.2/packetTrailer/worker/packetTrailer.worker.d.ts +2 -0
  107. package/dist/ts4.2/room/RTCEngine.d.ts +2 -1
  108. package/dist/ts4.2/room/Room.d.ts +3 -1
  109. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +2 -1
  110. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +5 -1
  111. package/dist/ts4.2/room/data-track/depacketizer.d.ts +12 -4
  112. package/dist/ts4.2/room/data-track/frame.d.ts +3 -3
  113. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +3 -1
  114. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +4 -1
  115. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +2 -2
  116. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +4 -3
  117. package/dist/ts4.2/room/data-track/packet/extensions.d.ts +4 -4
  118. package/dist/ts4.2/room/data-track/packet/index.d.ts +5 -5
  119. package/dist/ts4.2/room/data-track/packet/serializable.d.ts +1 -1
  120. package/dist/ts4.2/room/data-track/types.d.ts +7 -0
  121. package/dist/ts4.2/room/events.d.ts +2 -2
  122. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +3 -1
  123. package/dist/ts4.2/room/participant/Participant.d.ts +1 -1
  124. package/dist/ts4.2/room/track/PacketTrailerExtractor.d.ts +19 -0
  125. package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +16 -0
  126. package/dist/ts4.2/room/track/Track.d.ts +1 -1
  127. package/dist/ts4.2/room/track/options.d.ts +10 -0
  128. package/dist/ts4.2/room/utils.d.ts +4 -3
  129. package/dist/ts4.2/utils/dataPacketBuffer.d.ts +1 -1
  130. package/dist/ts4.2/version.d.ts +1 -1
  131. package/package.json +24 -16
  132. package/src/api/SignalClient.test.ts +102 -10
  133. package/src/api/SignalClient.ts +4 -2
  134. package/src/api/WebSocketStream.test.ts +0 -1
  135. package/src/e2ee/E2eeManager.ts +82 -30
  136. package/src/e2ee/types.ts +37 -8
  137. package/src/e2ee/utils.ts +7 -6
  138. package/src/e2ee/worker/DataCryptor.ts +6 -6
  139. package/src/e2ee/worker/FrameCryptor.test.ts +177 -4
  140. package/src/e2ee/worker/FrameCryptor.ts +94 -14
  141. package/src/e2ee/worker/ParticipantKeyHandler.test.ts +4 -4
  142. package/src/e2ee/worker/e2ee.worker.ts +13 -5
  143. package/src/e2ee/worker/naluUtils.ts +4 -4
  144. package/src/e2ee/worker/sifPayload.ts +10 -8
  145. package/src/index.ts +7 -0
  146. package/src/options.ts +8 -0
  147. package/src/packetTrailer/PacketTrailerManager.test.ts +172 -0
  148. package/src/packetTrailer/PacketTrailerManager.ts +250 -0
  149. package/src/packetTrailer/packetTrailer.test.ts +174 -0
  150. package/src/packetTrailer/packetTrailer.ts +276 -0
  151. package/src/packetTrailer/types.ts +75 -0
  152. package/src/packetTrailer/utils.test.ts +105 -0
  153. package/src/packetTrailer/utils.ts +50 -0
  154. package/src/packetTrailer/worker/packetTrailer.worker.ts +155 -0
  155. package/src/packetTrailer/worker/tsconfig.json +14 -0
  156. package/src/room/BackOffStrategy.test.ts +1 -1
  157. package/src/room/RTCEngine.test.ts +219 -0
  158. package/src/room/RTCEngine.ts +86 -20
  159. package/src/room/Room.test.ts +62 -1
  160. package/src/room/Room.ts +28 -5
  161. package/src/room/data-track/LocalDataTrack.ts +15 -7
  162. package/src/room/data-track/RemoteDataTrack.ts +8 -1
  163. package/src/room/data-track/depacketizer.test.ts +433 -1
  164. package/src/room/data-track/depacketizer.ts +79 -61
  165. package/src/room/data-track/frame.ts +2 -2
  166. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +194 -0
  167. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +21 -1
  168. package/src/room/data-track/incoming/pipeline.ts +13 -2
  169. package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +350 -198
  170. package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +9 -3
  171. package/src/room/data-track/outgoing/types.ts +4 -3
  172. package/src/room/data-track/packet/extensions.ts +2 -2
  173. package/src/room/data-track/packet/index.ts +6 -6
  174. package/src/room/data-track/packet/serializable.ts +1 -1
  175. package/src/room/data-track/types.ts +8 -0
  176. package/src/room/events.ts +2 -2
  177. package/src/room/participant/LocalParticipant.test.ts +81 -0
  178. package/src/room/participant/LocalParticipant.ts +48 -7
  179. package/src/room/participant/Participant.ts +1 -1
  180. package/src/room/participant/publishUtils.ts +1 -1
  181. package/src/room/track/PacketTrailerExtractor.ts +43 -0
  182. package/src/room/track/RemoteVideoTrack.ts +23 -2
  183. package/src/room/track/Track.ts +1 -1
  184. package/src/room/track/create.ts +0 -4
  185. package/src/room/track/options.ts +11 -0
  186. package/src/room/track/record.ts +1 -1
  187. package/src/room/track/utils.ts +4 -1
  188. package/src/room/utils.test.ts +14 -1
  189. package/src/room/utils.ts +17 -3
  190. package/src/test/MockMediaStreamTrack.ts +0 -1
  191. package/src/type-polyfills/non-shared-typed-arrays.d.ts +6 -0
  192. package/src/utils/dataPacketBuffer.ts +1 -1
  193. package/src/version.ts +1 -1
@@ -54,9 +54,14 @@ import {
54
54
  toProtoSessionDescription,
55
55
  } from '../api/SignalClient';
56
56
  import type { BaseE2EEManager } from '../e2ee/E2eeManager';
57
- import { asEncryptablePacket } from '../e2ee/utils';
57
+ import { asEncryptablePacket, isInsertableStreamSupported } from '../e2ee/utils';
58
58
  import log, { LoggerNames, getLogger } from '../logger';
59
59
  import type { InternalRoomOptions } from '../options';
60
+ import {
61
+ hasPacketTrailerPublishOptions,
62
+ isPacketTrailerSupported,
63
+ shouldUsePacketTrailerScriptTransform,
64
+ } from '../packetTrailer/utils';
60
65
  import TypedPromise from '../utils/TypedPromise';
61
66
  import { DataPacketBuffer } from '../utils/dataPacketBuffer';
62
67
  import { TTLMap } from '../utils/ttlmap';
@@ -762,7 +767,14 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
762
767
  ): RTCConfiguration {
763
768
  const rtcConfig = { ...this.rtcConfig };
764
769
 
765
- if (this.signalOpts?.e2eeEnabled) {
770
+ // E2EE and packet trailer extraction both rely on encoded frame transforms.
771
+ // Only opt into the createEncodedStreams flavor when that path will be
772
+ // used; RTCRtpScriptTransform does not need the PeerConnection flag.
773
+ const needsInsertableStreams =
774
+ this.signalOpts?.e2eeEnabled ||
775
+ (this.options.packetTrailer?.worker && !shouldUsePacketTrailerScriptTransform());
776
+
777
+ if (needsInsertableStreams && isInsertableStreamSupported()) {
766
778
  this.log.debug('E2EE - setting up transports with insertable streams');
767
779
  // this makes sure that no data is sent before the transforms are ready
768
780
  // @ts-ignore
@@ -940,8 +952,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
940
952
  return;
941
953
  }
942
954
  const decryptedData = await this.e2eeManager?.handleEncryptedData(
943
- dp.value.value.encryptedValue,
944
- dp.value.value.iv,
955
+ dp.value.value.encryptedValue as NonSharedUint8Array,
956
+ dp.value.value.iv as NonSharedUint8Array,
945
957
  dp.participantIdentity,
946
958
  dp.value.value.keyIndex,
947
959
  );
@@ -1004,16 +1016,17 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1004
1016
  opts: TrackPublishOptions,
1005
1017
  encodings?: RTCRtpEncodingParameters[],
1006
1018
  ) {
1019
+ let sender: RTCRtpSender;
1007
1020
  if (supportsTransceiver()) {
1008
- const sender = await this.createTransceiverRTCRtpSender(track, opts, encodings);
1009
- return sender;
1010
- }
1011
- if (supportsAddTrack()) {
1021
+ sender = await this.createTransceiverRTCRtpSender(track, opts, encodings);
1022
+ } else if (supportsAddTrack()) {
1012
1023
  this.log.warn('using add-track fallback');
1013
- const sender = await this.createRTCRtpSender(track.mediaStreamTrack);
1014
- return sender;
1024
+ sender = await this.createRTCRtpSender(track.mediaStreamTrack);
1025
+ } else {
1026
+ throw new UnexpectedConnectionState('Required webRTC APIs not supported on this device');
1015
1027
  }
1016
- throw new UnexpectedConnectionState('Required webRTC APIs not supported on this device');
1028
+ this.setupPacketTrailerSender(sender, opts);
1029
+ return sender;
1017
1030
  }
1018
1031
 
1019
1032
  async createSimulcastSender(
@@ -1022,16 +1035,67 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1022
1035
  opts: TrackPublishOptions,
1023
1036
  encodings?: RTCRtpEncodingParameters[],
1024
1037
  ) {
1025
- // store RTCRtpSender
1038
+ let sender: RTCRtpSender | undefined;
1026
1039
  if (supportsTransceiver()) {
1027
- return this.createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings);
1028
- }
1029
- if (supportsAddTrack()) {
1040
+ sender = await this.createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings);
1041
+ } else if (supportsAddTrack()) {
1030
1042
  this.log.debug('using add-track fallback');
1031
- return this.createRTCRtpSender(track.mediaStreamTrack);
1043
+ sender = await this.createRTCRtpSender(track.mediaStreamTrack);
1044
+ } else {
1045
+ throw new UnexpectedConnectionState('Cannot stream on this device');
1032
1046
  }
1047
+ if (sender) {
1048
+ this.setupPacketTrailerSender(sender, opts);
1049
+ }
1050
+ return sender;
1051
+ }
1033
1052
 
1034
- throw new UnexpectedConnectionState('Cannot stream on this device');
1053
+ private setupPacketTrailerSender(sender: RTCRtpSender, opts: TrackPublishOptions = {}) {
1054
+ if (!this.options.packetTrailer?.worker || this.signalOpts?.e2eeEnabled) {
1055
+ return;
1056
+ }
1057
+
1058
+ const packetTrailer = opts.packetTrailer;
1059
+ const hasPacketTrailer = hasPacketTrailerPublishOptions(packetTrailer);
1060
+
1061
+ if (shouldUsePacketTrailerScriptTransform()) {
1062
+ if (hasPacketTrailer) {
1063
+ // @ts-ignore
1064
+ sender.transform = new RTCRtpScriptTransform(this.options.packetTrailer.worker, {
1065
+ kind: 'encode',
1066
+ packetTrailer,
1067
+ });
1068
+ }
1069
+ return;
1070
+ }
1071
+
1072
+ if (
1073
+ !isPacketTrailerSupported(this.options.packetTrailer) ||
1074
+ !('createEncodedStreams' in sender)
1075
+ ) {
1076
+ if (hasPacketTrailer) {
1077
+ this.log.warn('packet trailer transform not supported; skipping write', this.logContext);
1078
+ }
1079
+ return;
1080
+ }
1081
+
1082
+ // @ts-ignore
1083
+ const { readable, writable } = sender.createEncodedStreams();
1084
+ if (hasPacketTrailer) {
1085
+ this.options.packetTrailer.worker.postMessage(
1086
+ {
1087
+ kind: 'encode',
1088
+ data: {
1089
+ readableStream: readable,
1090
+ writableStream: writable,
1091
+ packetTrailer,
1092
+ },
1093
+ },
1094
+ [readable, writable],
1095
+ );
1096
+ } else {
1097
+ readable.pipeTo(writable);
1098
+ }
1035
1099
  }
1036
1100
 
1037
1101
  private async createTransceiverRTCRtpSender(
@@ -1447,7 +1511,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1447
1511
  if (this.e2eeManager && this.e2eeManager.isDataChannelEncryptionEnabled) {
1448
1512
  const encryptablePacket = asEncryptablePacket(packet);
1449
1513
  if (encryptablePacket) {
1450
- const encryptedData = await this.e2eeManager.encryptData(encryptablePacket.toBinary());
1514
+ const encryptedData = await this.e2eeManager.encryptData(
1515
+ encryptablePacket.toBinary() as NonSharedUint8Array,
1516
+ );
1451
1517
  packet.value = {
1452
1518
  case: 'encryptedPacket',
1453
1519
  value: new EncryptedPacket({
@@ -1464,7 +1530,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1464
1530
  this.reliableDataSequence += 1;
1465
1531
  }
1466
1532
 
1467
- const msg = packet.toBinary();
1533
+ const msg = packet.toBinary() as Uint8Array<ArrayBuffer>;
1468
1534
 
1469
1535
  switch (kind) {
1470
1536
  case DataChannelKind.LOSSY:
@@ -1491,7 +1557,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1491
1557
 
1492
1558
  /* @internal */
1493
1559
  async sendLossyBytes(
1494
- bytes: Uint8Array,
1560
+ bytes: NonSharedUint8Array,
1495
1561
  kind: Exclude<DataChannelKind, DataChannelKind.RELIABLE>,
1496
1562
  bufferStatusLowBehavior: 'drop' | 'wait' = 'drop',
1497
1563
  ) {
@@ -1,5 +1,7 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { ClientInfo_Capability, JoinResponse } from '@livekit/protocol';
2
+ import { describe, expect, it, vi } from 'vitest';
2
3
  import Room from './Room';
4
+ import { roomConnectOptionDefaults, roomOptionDefaults } from './defaults';
3
5
  import { RoomEvent } from './events';
4
6
 
5
7
  describe('Active device switch', () => {
@@ -28,3 +30,62 @@ describe('Active device switch', () => {
28
30
  expect(kind).toBe('audioinput');
29
31
  });
30
32
  });
33
+
34
+ describe('Room signaling options', () => {
35
+ it('advertises packet trailer capability when E2EE can handle trailers', async () => {
36
+ const room = new Room();
37
+ const join = vi.fn().mockResolvedValue({
38
+ joinResponse: new JoinResponse({
39
+ room: { name: 'test-room', sid: 'room-sid' },
40
+ participant: { sid: 'participant-sid', identity: 'test-user' },
41
+ }),
42
+ serverInfo: { version: '1.0.0' },
43
+ });
44
+ const engine = { join };
45
+
46
+ (
47
+ room as unknown as {
48
+ e2eeManager: unknown;
49
+ connectSignal: (
50
+ url: string,
51
+ token: string,
52
+ engine: unknown,
53
+ connectOptions: typeof roomConnectOptionDefaults,
54
+ roomOptions: typeof roomOptionDefaults,
55
+ abortController: AbortController,
56
+ ) => Promise<JoinResponse>;
57
+ }
58
+ ).e2eeManager = {};
59
+
60
+ await (
61
+ room as unknown as {
62
+ connectSignal: (
63
+ url: string,
64
+ token: string,
65
+ engine: unknown,
66
+ connectOptions: typeof roomConnectOptionDefaults,
67
+ roomOptions: typeof roomOptionDefaults,
68
+ abortController: AbortController,
69
+ ) => Promise<JoinResponse>;
70
+ }
71
+ ).connectSignal(
72
+ 'wss://test.livekit.io',
73
+ 'test-token',
74
+ engine,
75
+ roomConnectOptionDefaults,
76
+ roomOptionDefaults,
77
+ new AbortController(),
78
+ );
79
+
80
+ expect(join).toHaveBeenCalledWith(
81
+ 'wss://test.livekit.io',
82
+ 'test-token',
83
+ expect.objectContaining({
84
+ clientInfoCapabilities: [ClientInfo_Capability.CAP_PACKET_TRAILER],
85
+ e2eeEnabled: true,
86
+ }),
87
+ expect.any(AbortSignal),
88
+ false,
89
+ );
90
+ });
91
+ });
package/src/room/Room.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Mutex } from '@livekit/mutex';
2
2
  import {
3
3
  ChatMessage as ChatMessageModel,
4
+ ClientInfo_Capability,
4
5
  ConnectionQualityUpdate,
5
6
  type DataPacket,
6
7
  DataPacket_Kind,
@@ -43,6 +44,8 @@ import type {
43
44
  RoomConnectOptions,
44
45
  RoomOptions,
45
46
  } from '../options';
47
+ import { PacketTrailerManager } from '../packetTrailer/PacketTrailerManager';
48
+ import { isPacketTrailerSupported } from '../packetTrailer/utils';
46
49
  import TypedPromise from '../utils/TypedPromise';
47
50
  import { getBrowser } from '../utils/browserParser';
48
51
  import { BackOffStrategy } from './BackOffStrategy';
@@ -187,6 +190,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
187
190
 
188
191
  private e2eeManager: BaseE2EEManager | undefined;
189
192
 
193
+ private packetTrailerManager: PacketTrailerManager | undefined;
194
+
190
195
  private e2eeStateMutex: Mutex = new Mutex();
191
196
 
192
197
  private connectionReconcileInterval?: ReturnType<typeof setInterval>;
@@ -307,6 +312,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
307
312
  this.outgoingDataTrackManager,
308
313
  );
309
314
 
315
+ this.setupPacketTrailer();
316
+
310
317
  if (this.options.e2ee || this.options.encryption) {
311
318
  this.setupE2EE();
312
319
  }
@@ -465,6 +472,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
465
472
  }
466
473
  }
467
474
 
475
+ private setupPacketTrailer() {
476
+ // The manager is always created so tracks that advertise packet trailer
477
+ // features can be wired up when the app passes a packet trailer worker.
478
+ this.packetTrailerManager = new PacketTrailerManager(this.options.packetTrailer);
479
+ this.packetTrailerManager.setup(this);
480
+ }
481
+
468
482
  private get logContext() {
469
483
  return {
470
484
  room: this.name,
@@ -914,6 +928,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
914
928
  autoSubscribe: connectOptions.autoSubscribe,
915
929
  adaptiveStream:
916
930
  typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
931
+ clientInfoCapabilities:
932
+ isPacketTrailerSupported(roomOptions.packetTrailer) || !!this.e2eeManager
933
+ ? [ClientInfo_Capability.CAP_PACKET_TRAILER]
934
+ : undefined,
917
935
  maxRetries: connectOptions.maxRetries,
918
936
  e2eeEnabled: !!this.e2eeManager,
919
937
  websocketTimeout: connectOptions.websocketTimeout,
@@ -946,7 +964,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
946
964
 
947
965
  if (this.e2eeManager) {
948
966
  try {
949
- this.e2eeManager.setSifTrailer(joinResponse.sifTrailer);
967
+ this.e2eeManager.setSifTrailer(joinResponse.sifTrailer as NonSharedUint8Array);
950
968
  } catch (e: any) {
951
969
  this.log.error(e instanceof Error ? e.message : 'Could not set SifTrailer', {
952
970
  error: e,
@@ -1825,7 +1843,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1825
1843
  this.handleParticipantDisconnected(info.identity, remoteParticipant);
1826
1844
  } else {
1827
1845
  // create participant if doesn't exist
1828
- remoteParticipant = this.getOrCreateParticipant(info.identity, info);
1846
+ this.getOrCreateParticipant(info.identity, info);
1829
1847
  }
1830
1848
  }
1831
1849
 
@@ -2018,7 +2036,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
2018
2036
  ) => {
2019
2037
  this.emit(
2020
2038
  RoomEvent.DataReceived,
2021
- userPacket.payload,
2039
+ userPacket.payload as NonSharedUint8Array,
2022
2040
  participant,
2023
2041
  kind,
2024
2042
  userPacket.topic,
@@ -2026,7 +2044,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
2026
2044
  );
2027
2045
 
2028
2046
  // also emit on the participant
2029
- participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind, encryptionType);
2047
+ participant?.emit(
2048
+ ParticipantEvent.DataReceived,
2049
+ userPacket.payload as NonSharedUint8Array,
2050
+ kind,
2051
+ encryptionType,
2052
+ );
2030
2053
  };
2031
2054
 
2032
2055
  private handleSipDtmf = (participant: RemoteParticipant | undefined, dtmf: SipDTMF) => {
@@ -2874,7 +2897,7 @@ export type RoomEventCallbacks = {
2874
2897
  activeSpeakersChanged: (speakers: Array<Participant>) => void;
2875
2898
  roomMetadataChanged: (metadata: string) => void;
2876
2899
  dataReceived: (
2877
- payload: Uint8Array,
2900
+ payload: NonSharedUint8Array,
2878
2901
  participant?: RemoteParticipant,
2879
2902
  kind?: DataPacket_Kind,
2880
2903
  topic?: string,
@@ -4,7 +4,7 @@ import { type DataTrackFrame, DataTrackFrameInternal } from './frame';
4
4
  import type { DataTrackHandle } from './handle';
5
5
  import type OutgoingDataTrackManager from './outgoing/OutgoingDataTrackManager';
6
6
  import { DataTrackPushFrameError } from './outgoing/errors';
7
- import type { DataTrackOptions } from './outgoing/types';
7
+ import type { DataTrackOptions, EventPacketsFlushedChange } from './outgoing/types';
8
8
  import {
9
9
  DataTrackSymbol,
10
10
  type IDataTrack,
@@ -32,6 +32,8 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
32
32
  /** Resolves once the data track has sent all pending packets the rtc data channel buffer. */
33
33
  protected flushedFuture = new Future<void, never>();
34
34
 
35
+ protected isFlushed = true;
36
+
35
37
  /** @internal */
36
38
  constructor(options: DataTrackOptions, manager: OutgoingDataTrackManager) {
37
39
  this.options = options;
@@ -39,7 +41,7 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
39
41
 
40
42
  this.log = getLogger(LoggerNames.DataTracks);
41
43
 
42
- this.manager.on('packetsFlushed', this.handleManagerPacketsFlushed);
44
+ this.manager.on('packetsFlushedChange', this.handleManagerPacketsFlushedChange);
43
45
  this.manager.on('reset', this.handleManagerReset);
44
46
  }
45
47
 
@@ -47,15 +49,18 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
47
49
  // When the associated manager resets, mark any in flight flushes as complete
48
50
  // There's nothing actionable a user can do to get these to complete so no
49
51
  // error is being thrown.
50
- this.handleManagerPacketsFlushed();
52
+ this.flushedFuture.resolve?.();
51
53
 
52
- this.manager.off('packetsFlushed', this.handleManagerReset);
54
+ this.manager.off('packetsFlushedChange', this.handleManagerPacketsFlushedChange);
53
55
  this.manager.off('reset', this.handleManagerReset);
54
56
  };
55
57
 
56
- private handleManagerPacketsFlushed = () => {
57
- this.flushedFuture.resolve?.();
58
- this.flushedFuture = new Future();
58
+ private handleManagerPacketsFlushedChange = (event: EventPacketsFlushedChange) => {
59
+ this.isFlushed = event.isFlushed;
60
+ if (event.isFlushed) {
61
+ this.flushedFuture.resolve?.();
62
+ this.flushedFuture = new Future();
63
+ }
59
64
  };
60
65
 
61
66
  /** @internal */
@@ -152,6 +157,9 @@ export default class LocalDataTrack implements ILocalTrack, IDataTrack {
152
157
  * ```
153
158
  **/
154
159
  async flush(): Promise<void> {
160
+ if (this.isFlushed) {
161
+ return;
162
+ }
155
163
  return this.flushedFuture.promise;
156
164
  }
157
165
 
@@ -7,7 +7,7 @@ import {
7
7
  type IRemoteTrack,
8
8
  TrackSymbol,
9
9
  } from './track-interfaces';
10
- import { type DataTrackInfo } from './types';
10
+ import { type DataTrackInfo, type RemoteDataTrackPipelineOptions } from './types';
11
11
 
12
12
  type RemoteDataTrackOptions = {
13
13
  publisherIdentity: Participant['identity'];
@@ -80,4 +80,11 @@ export default class RemoteDataTrack implements IRemoteTrack, IDataTrack {
80
80
  throw err;
81
81
  }
82
82
  }
83
+
84
+ /** Configure how incoming frames for this track are processed before they are handed out to
85
+ * subscribers (the "pipeline"). These options apply to all current and future subscriptions
86
+ * of this track, and may be set at any time. */
87
+ setPipelineOptions(options: RemoteDataTrackPipelineOptions): void {
88
+ this.manager.setPipelineOptions(this.info.sid, options);
89
+ }
83
90
  }