livekit-client 2.22.4 → 2.23.0

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 (116) 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 +69 -7
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +1133 -289
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.fm.worker.js.map +1 -1
  8. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  9. package/dist/livekit-client.umd.js +1 -1
  10. package/dist/livekit-client.umd.js.map +1 -1
  11. package/dist/src/api/SignalClient.d.ts.map +1 -1
  12. package/dist/src/api/WebSocketStream.d.ts +1 -0
  13. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  14. package/dist/src/api/utils.d.ts +1 -1
  15. package/dist/src/api/utils.d.ts.map +1 -1
  16. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  17. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  18. package/dist/src/e2ee/worker/naluUtils.d.ts +12 -1
  19. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
  20. package/dist/src/index.d.ts +2 -2
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/room/DefaultReconnectPolicy.d.ts.map +1 -1
  23. package/dist/src/room/PCTransport.d.ts +26 -4
  24. package/dist/src/room/PCTransport.d.ts.map +1 -1
  25. package/dist/src/room/RTCEngine.d.ts +8 -0
  26. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  27. package/dist/src/room/Room.d.ts +53 -0
  28. package/dist/src/room/Room.d.ts.map +1 -1
  29. package/dist/src/room/data-channel/DataChannelManager.d.ts +9 -8
  30. package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -1
  31. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +25 -12
  32. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -1
  33. package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -1
  34. package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -1
  35. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  36. package/dist/src/room/participant/LocalParticipant.d.ts +4 -0
  37. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  38. package/dist/src/room/participant/Participant.d.ts +1 -1
  39. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  40. package/dist/src/room/rpc/utils.d.ts.map +1 -1
  41. package/dist/src/room/track/LocalAudioTrack.d.ts +24 -1
  42. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  43. package/dist/src/room/track/LocalTrack.d.ts +18 -0
  44. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  45. package/dist/src/room/track/LocalVideoTrack.d.ts +7 -0
  46. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  47. package/dist/src/room/track/RemoteAudioTrack.d.ts +6 -0
  48. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  49. package/dist/src/room/track/processor/types.d.ts +11 -0
  50. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  51. package/dist/src/room/track/utils.d.ts.map +1 -1
  52. package/dist/src/room/utils.d.ts +28 -1
  53. package/dist/src/room/utils.d.ts.map +1 -1
  54. package/dist/ts4.2/api/WebSocketStream.d.ts +1 -0
  55. package/dist/ts4.2/api/utils.d.ts +1 -1
  56. package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +12 -1
  57. package/dist/ts4.2/index.d.ts +2 -2
  58. package/dist/ts4.2/room/PCTransport.d.ts +26 -4
  59. package/dist/ts4.2/room/RTCEngine.d.ts +8 -0
  60. package/dist/ts4.2/room/Room.d.ts +53 -0
  61. package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +9 -8
  62. package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +25 -12
  63. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +4 -0
  64. package/dist/ts4.2/room/participant/Participant.d.ts +1 -1
  65. package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +24 -1
  66. package/dist/ts4.2/room/track/LocalTrack.d.ts +18 -0
  67. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +7 -0
  68. package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +6 -0
  69. package/dist/ts4.2/room/track/processor/types.d.ts +11 -0
  70. package/dist/ts4.2/room/utils.d.ts +28 -1
  71. package/package.json +1 -1
  72. package/src/api/SignalClient.test.ts +234 -0
  73. package/src/api/SignalClient.ts +58 -23
  74. package/src/api/WebSocketStream.test.ts +11 -0
  75. package/src/api/WebSocketStream.ts +22 -1
  76. package/src/api/utils.ts +1 -1
  77. package/src/e2ee/E2eeManager.test.ts +43 -0
  78. package/src/e2ee/E2eeManager.ts +6 -2
  79. package/src/e2ee/worker/FrameCryptor.test.ts +212 -90
  80. package/src/e2ee/worker/FrameCryptor.ts +21 -4
  81. package/src/e2ee/worker/naluUtils.test.ts +123 -0
  82. package/src/e2ee/worker/naluUtils.ts +57 -2
  83. package/src/index.ts +2 -0
  84. package/src/room/DefaultReconnectPolicy.test.ts +58 -0
  85. package/src/room/DefaultReconnectPolicy.ts +14 -2
  86. package/src/room/PCTransport.test.ts +144 -2
  87. package/src/room/PCTransport.ts +151 -9
  88. package/src/room/RTCEngine.test.ts +234 -2
  89. package/src/room/RTCEngine.ts +102 -7
  90. package/src/room/Room.test.ts +116 -0
  91. package/src/room/Room.ts +286 -74
  92. package/src/room/data-channel/DataChannelManager.test.ts +24 -7
  93. package/src/room/data-channel/DataChannelManager.ts +43 -38
  94. package/src/room/data-channel/FlowControlledDataChannel.test.ts +60 -18
  95. package/src/room/data-channel/FlowControlledDataChannel.ts +40 -22
  96. package/src/room/data-channel/LossyDataChannel.test.ts +38 -0
  97. package/src/room/data-channel/LossyDataChannel.ts +10 -1
  98. package/src/room/data-channel/ReliableDataChannel.test.ts +72 -0
  99. package/src/room/data-channel/ReliableDataChannel.ts +16 -5
  100. package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +31 -0
  101. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +11 -0
  102. package/src/room/participant/LocalParticipant.test.ts +136 -1
  103. package/src/room/participant/LocalParticipant.ts +66 -17
  104. package/src/room/participant/Participant.ts +6 -3
  105. package/src/room/participant/restartOnDefaultAudioDevice.test.ts +177 -0
  106. package/src/room/rpc/utils.test.ts +32 -0
  107. package/src/room/rpc/utils.ts +7 -0
  108. package/src/room/track/LocalAudioTrack.ts +101 -10
  109. package/src/room/track/LocalTrack.ts +70 -13
  110. package/src/room/track/LocalVideoTrack.ts +7 -0
  111. package/src/room/track/RemoteAudioTrack.ts +56 -11
  112. package/src/room/track/processor/types.ts +11 -0
  113. package/src/room/track/utils.test.ts +5 -0
  114. package/src/room/track/utils.ts +29 -5
  115. package/src/room/utils.test.ts +126 -0
  116. package/src/room/utils.ts +84 -21
@@ -6586,8 +6586,14 @@ function isH265SliceNALU(naluType) {
6586
6586
  * @returns Detected codec type
6587
6587
  */
6588
6588
  function detectCodecFromNALUs(data, naluIndices) {
6589
+ // h264 runs to completion first. Several non-slice h264 NALUs read as h265 slice types
6590
+ // (AUD 0x09 -> STSA_N, SEI 0x06 -> TSA_R), so interleaving the two checks lets one of
6591
+ // those outrank the real h264 slice that follows it. The reverse cannot happen: h265
6592
+ // header bytes are even at nuh_layer_id 0, so they never mask to an h264 slice type.
6589
6593
  for (const naluIndex of naluIndices) {
6590
6594
  if (isH264SliceNALU(parseH264NALUType(data[naluIndex]))) return 'h264';
6595
+ }
6596
+ for (const naluIndex of naluIndices) {
6591
6597
  if (isH265SliceNALU(parseH265NALUType(data[naluIndex]))) return 'h265';
6592
6598
  }
6593
6599
  return 'unknown';
@@ -6665,12 +6671,21 @@ function findNALUIndices(stream) {
6665
6671
  /**
6666
6672
  * Process NALU data for frame encryption, detecting codec and finding unencrypted bytes
6667
6673
  * @param data Frame data
6668
- * @param knownCodec Known codec from other sources (optional)
6674
+ * @param knownCodec Codec from other sources, used only when the bytes are inconclusive (optional)
6669
6675
  * @returns NALU processing result
6670
6676
  */
6671
6677
  function processNALUsForEncryption(data, knownCodec) {
6672
6678
  const naluIndices = findNALUIndices(data);
6673
- const detectedCodec = knownCodec !== null && knownCodec !== void 0 ? knownCodec : detectCodecFromNALUs(data, naluIndices);
6679
+ // The bytes outrank knownCodec, which can be the codec the client asked for rather than the
6680
+ // one that was negotiated. Parsing the wrong one mis-slices without raising anything: the
6681
+ // offset lands before the real slice header, so the header itself gets encrypted.
6682
+ // That holds in one direction only. An h264 access unit carrying no slice NALU but carrying
6683
+ // an AUD or SEI that reads as an h265 slice now slices there instead of throwing. No real
6684
+ // WebRTC access unit can reach it, because every one of them carries a type 1 or 5 slice.
6685
+ let detectedCodec = detectCodecFromNALUs(data, naluIndices);
6686
+ if (detectedCodec === 'unknown') {
6687
+ detectedCodec = knownCodec !== null && knownCodec !== void 0 ? knownCodec : 'unknown';
6688
+ }
6674
6689
  if (detectedCodec === 'unknown') {
6675
6690
  return {
6676
6691
  unencryptedBytes: 0,
@@ -6688,6 +6703,44 @@ function processNALUsForEncryption(data, knownCodec) {
6688
6703
  requiresNALUProcessing: true
6689
6704
  };
6690
6705
  }
6706
+ /**
6707
+ * Rewrite every 3-byte Annex B start code (00 00 01) in `data` to the 4-byte form (00 00 00 01).
6708
+ *
6709
+ * WebRTC's H.264/H.265 depacketizer always reconstructs NALUs with 4-byte start codes, regardless
6710
+ * of what the encoder emitted. Some encoders (e.g. Intel Quick Sync) emit 3-byte start codes, so
6711
+ * the unencrypted header a receiver sees would differ from what the sender authenticated as
6712
+ * AES-GCM additionalData. Normalizing on the sender makes both sides agree.
6713
+ *
6714
+ * Returns `data` unchanged (same instance) when it contains no 3-byte start codes.
6715
+ */
6716
+ function normalizeAnnexBStartCodes(data) {
6717
+ const shortStartCodes = [];
6718
+ for (let i = 0; i + 2 < data.length;) {
6719
+ if (data[i + 2] === 0) {
6720
+ i += 1;
6721
+ continue;
6722
+ }
6723
+ if (data[i + 2] === 1 && data[i] === 0 && data[i + 1] === 0 && (i === 0 || data[i - 1] !== 0)) {
6724
+ shortStartCodes.push(i);
6725
+ }
6726
+ // data[i + 2] is non-zero, so no start code can begin at i + 1 or i + 2 either.
6727
+ i += 3;
6728
+ }
6729
+ if (shortStartCodes.length === 0) {
6730
+ return data;
6731
+ }
6732
+ // The new array is zero-filled, so the one-byte gap left before each short start code becomes
6733
+ // the inserted leading 00.
6734
+ const result = new Uint8Array(data.length + shortStartCodes.length);
6735
+ let from = 0;
6736
+ for (let inserted = 0; inserted < shortStartCodes.length; inserted++) {
6737
+ const to = shortStartCodes[inserted];
6738
+ result.set(data.subarray(from, to), from + inserted);
6739
+ from = to;
6740
+ }
6741
+ result.set(data.subarray(from), from + shortStartCodes.length);
6742
+ return result;
6743
+ }
6691
6744
 
6692
6745
  /**
6693
6746
  * Create a crypto hash using Web Crypto API for secure comparison operations
@@ -7061,7 +7114,12 @@ class FrameCryptor extends BaseFrameCryptor {
7061
7114
  const iv = this.makeIV((_a = encodedFrame.getMetadata().synchronizationSource) !== null && _a !== void 0 ? _a : -1, encodedFrame.timestamp);
7062
7115
  let frameInfo = this.getUnencryptedBytes(encodedFrame);
7063
7116
  // Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.
7064
- const frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
7117
+ let frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
7118
+ if (frameInfo.requiresNALUProcessing) {
7119
+ // The receiver's depacketizer rebuilds NALUs with 4-byte start codes, so authenticate (and
7120
+ // send) the header in that form or decryption fails for encoders emitting 3-byte ones.
7121
+ frameHeader = normalizeAnnexBStartCodes(frameHeader);
7122
+ }
7065
7123
  // Frame trailer contains the R|IV_LENGTH and key index
7066
7124
  const frameTrailer = new Uint8Array(2);
7067
7125
  frameTrailer[0] = IV_LENGTH;
@@ -7077,7 +7135,7 @@ class FrameCryptor extends BaseFrameCryptor {
7077
7135
  const cipherText = yield crypto.subtle.encrypt({
7078
7136
  name: ENCRYPTION_ALGORITHM,
7079
7137
  iv,
7080
- additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength)
7138
+ additionalData: frameHeader
7081
7139
  }, encryptionKey, new Uint8Array(encodedFrame.data, frameInfo.unencryptedBytes));
7082
7140
  let newDataWithoutHeader = new Uint8Array(cipherText.byteLength + iv.byteLength + frameTrailer.byteLength);
7083
7141
  newDataWithoutHeader.set(new Uint8Array(cipherText)); // add ciphertext.
@@ -7386,7 +7444,7 @@ class FrameCryptor extends BaseFrameCryptor {
7386
7444
  };
7387
7445
  }
7388
7446
  } catch (e) {
7389
- this.logNALUFallbackOnce(fallbackKey, payloadType, e);
7447
+ this.logNALUFallbackOnce(fallbackKey, frame, payloadType, detectedCodec, e);
7390
7448
  }
7391
7449
  // Fallback to VP8 handling
7392
7450
  return {
@@ -7398,14 +7456,18 @@ class FrameCryptor extends BaseFrameCryptor {
7398
7456
  * Logs a NALU processing fallback at most once per (participant, trackId, payloadType) tuple,
7399
7457
  * so a persistent bad state doesn't flood the console (Firefox doesn't filter debug).
7400
7458
  */
7401
- logNALUFallbackOnce(fallbackKey, payloadType, error) {
7459
+ logNALUFallbackOnce(fallbackKey, frame, payloadType, detectedCodec, error) {
7402
7460
  if (this.loggedNALUFallbacks.has(fallbackKey)) {
7403
7461
  return;
7404
7462
  }
7405
7463
  this.loggedNALUFallbacks.add(fallbackKey);
7406
7464
  workerLogger.warn('NALU processing failed, falling back to VP8 handling', Object.assign({
7407
7465
  error,
7408
- payloadType
7466
+ payloadType,
7467
+ detectedCodec,
7468
+ frameType: frame.type,
7469
+ byteLength: frame.data.byteLength,
7470
+ firstBytes: Array.from(new Uint8Array(frame.data, 0, Math.min(16, frame.data.byteLength))).map(b => b.toString(16).padStart(2, '0')).join(' ')
7409
7471
  }, this.logContext));
7410
7472
  }
7411
7473
  /**