livekit-client 2.15.3 → 2.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +2 -2
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +329 -124
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +868 -548
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/e2ee/worker/FrameCryptor.d.ts +0 -46
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  13. package/dist/src/e2ee/worker/naluUtils.d.ts +27 -0
  14. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -0
  15. package/dist/src/index.d.ts +2 -2
  16. package/dist/src/index.d.ts.map +1 -1
  17. package/dist/src/room/Room.d.ts +6 -10
  18. package/dist/src/room/Room.d.ts.map +1 -1
  19. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  20. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -0
  21. package/dist/src/room/{StreamReader.d.ts → data-stream/incoming/StreamReader.d.ts} +32 -6
  22. package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -0
  23. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  24. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -0
  25. package/dist/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  26. package/dist/src/room/data-stream/outgoing/StreamWriter.d.ts.map +1 -0
  27. package/dist/src/room/errors.d.ts +13 -0
  28. package/dist/src/room/errors.d.ts.map +1 -1
  29. package/dist/src/room/participant/LocalParticipant.d.ts +33 -20
  30. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  31. package/dist/src/room/track/LocalTrack.d.ts +8 -2
  32. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  33. package/dist/src/room/track/record.d.ts.map +1 -1
  34. package/dist/src/room/types.d.ts +17 -1
  35. package/dist/src/room/types.d.ts.map +1 -1
  36. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +0 -46
  37. package/dist/ts4.2/src/e2ee/worker/naluUtils.d.ts +27 -0
  38. package/dist/ts4.2/src/index.d.ts +2 -2
  39. package/dist/ts4.2/src/room/Room.d.ts +6 -10
  40. package/dist/ts4.2/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +20 -0
  41. package/dist/ts4.2/src/room/{StreamReader.d.ts → data-stream/incoming/StreamReader.d.ts} +32 -6
  42. package/dist/ts4.2/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +27 -0
  43. package/dist/ts4.2/src/room/{StreamWriter.d.ts → data-stream/outgoing/StreamWriter.d.ts} +1 -1
  44. package/dist/ts4.2/src/room/errors.d.ts +13 -0
  45. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +33 -20
  46. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +8 -2
  47. package/dist/ts4.2/src/room/types.d.ts +17 -1
  48. package/package.json +1 -1
  49. package/src/api/SignalClient.ts +6 -0
  50. package/src/e2ee/worker/FrameCryptor.ts +48 -139
  51. package/src/e2ee/worker/naluUtils.ts +328 -0
  52. package/src/index.ts +2 -2
  53. package/src/room/Room.ts +94 -207
  54. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +247 -0
  55. package/src/room/data-stream/incoming/StreamReader.ts +317 -0
  56. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +316 -0
  57. package/src/room/{StreamWriter.ts → data-stream/outgoing/StreamWriter.ts} +1 -1
  58. package/src/room/errors.ts +34 -0
  59. package/src/room/participant/LocalParticipant.ts +48 -299
  60. package/src/room/track/LocalAudioTrack.ts +2 -2
  61. package/src/room/track/LocalTrack.ts +75 -49
  62. package/src/room/track/record.ts +15 -2
  63. package/src/room/types.ts +22 -1
  64. package/src/room/utils.ts +3 -3
  65. package/dist/src/room/StreamReader.d.ts.map +0 -1
  66. package/dist/src/room/StreamWriter.d.ts.map +0 -1
  67. package/src/room/StreamReader.ts +0 -170
@@ -503,6 +503,22 @@ var ConnectionErrorReason;
503
503
  ConnectionErrorReason[ConnectionErrorReason["LeaveRequest"] = 4] = "LeaveRequest";
504
504
  ConnectionErrorReason[ConnectionErrorReason["Timeout"] = 5] = "Timeout";
505
505
  })(ConnectionErrorReason || (ConnectionErrorReason = {}));
506
+ // NOTE: matches with https://github.com/livekit/client-sdk-swift/blob/f37bbd260d61e165084962db822c79f995f1a113/Sources/LiveKit/DataStream/StreamError.swift#L17
507
+ var DataStreamErrorReason;
508
+ (function (DataStreamErrorReason) {
509
+ // Unable to open a stream with the same ID more than once.
510
+ DataStreamErrorReason[DataStreamErrorReason["AlreadyOpened"] = 0] = "AlreadyOpened";
511
+ // Stream closed abnormally by remote participant.
512
+ DataStreamErrorReason[DataStreamErrorReason["AbnormalEnd"] = 1] = "AbnormalEnd";
513
+ // Incoming chunk data could not be decoded.
514
+ DataStreamErrorReason[DataStreamErrorReason["DecodeFailed"] = 2] = "DecodeFailed";
515
+ // Read length exceeded total length specified in stream header.
516
+ DataStreamErrorReason[DataStreamErrorReason["LengthExceeded"] = 3] = "LengthExceeded";
517
+ // Read length less than total length specified in stream header.
518
+ DataStreamErrorReason[DataStreamErrorReason["Incomplete"] = 4] = "Incomplete";
519
+ // Unable to register a stream handler more than once.
520
+ DataStreamErrorReason[DataStreamErrorReason["HandlerAlreadyRegistered"] = 7] = "HandlerAlreadyRegistered";
521
+ })(DataStreamErrorReason || (DataStreamErrorReason = {}));
506
522
  var MediaDeviceFailure;
507
523
  (function (MediaDeviceFailure) {
508
524
  // user rejected permissions
@@ -1113,6 +1129,266 @@ class SifGuard {
1113
1129
  }
1114
1130
  }
1115
1131
 
1132
+ /**
1133
+ * NALU (Network Abstraction Layer Unit) utilities for H.264 and H.265 video processing
1134
+ * Contains functions for parsing and working with NALUs in video frames
1135
+ */
1136
+ /**
1137
+ * Mask for extracting NALU type from H.264 header byte
1138
+ */
1139
+ const kH264NaluTypeMask = 0x1f;
1140
+ /**
1141
+ * H.264 NALU types according to RFC 6184
1142
+ */
1143
+ var H264NALUType;
1144
+ (function (H264NALUType) {
1145
+ /** Coded slice of a non-IDR picture */
1146
+ H264NALUType[H264NALUType["SLICE_NON_IDR"] = 1] = "SLICE_NON_IDR";
1147
+ /** Coded slice data partition A */
1148
+ H264NALUType[H264NALUType["SLICE_PARTITION_A"] = 2] = "SLICE_PARTITION_A";
1149
+ /** Coded slice data partition B */
1150
+ H264NALUType[H264NALUType["SLICE_PARTITION_B"] = 3] = "SLICE_PARTITION_B";
1151
+ /** Coded slice data partition C */
1152
+ H264NALUType[H264NALUType["SLICE_PARTITION_C"] = 4] = "SLICE_PARTITION_C";
1153
+ /** Coded slice of an IDR picture */
1154
+ H264NALUType[H264NALUType["SLICE_IDR"] = 5] = "SLICE_IDR";
1155
+ /** Supplemental enhancement information */
1156
+ H264NALUType[H264NALUType["SEI"] = 6] = "SEI";
1157
+ /** Sequence parameter set */
1158
+ H264NALUType[H264NALUType["SPS"] = 7] = "SPS";
1159
+ /** Picture parameter set */
1160
+ H264NALUType[H264NALUType["PPS"] = 8] = "PPS";
1161
+ /** Access unit delimiter */
1162
+ H264NALUType[H264NALUType["AUD"] = 9] = "AUD";
1163
+ /** End of sequence */
1164
+ H264NALUType[H264NALUType["END_SEQ"] = 10] = "END_SEQ";
1165
+ /** End of stream */
1166
+ H264NALUType[H264NALUType["END_STREAM"] = 11] = "END_STREAM";
1167
+ /** Filler data */
1168
+ H264NALUType[H264NALUType["FILLER_DATA"] = 12] = "FILLER_DATA";
1169
+ /** Sequence parameter set extension */
1170
+ H264NALUType[H264NALUType["SPS_EXT"] = 13] = "SPS_EXT";
1171
+ /** Prefix NAL unit */
1172
+ H264NALUType[H264NALUType["PREFIX_NALU"] = 14] = "PREFIX_NALU";
1173
+ /** Subset sequence parameter set */
1174
+ H264NALUType[H264NALUType["SUBSET_SPS"] = 15] = "SUBSET_SPS";
1175
+ /** Depth parameter set */
1176
+ H264NALUType[H264NALUType["DPS"] = 16] = "DPS";
1177
+ // 17, 18 reserved
1178
+ /** Coded slice of an auxiliary coded picture without partitioning */
1179
+ H264NALUType[H264NALUType["SLICE_AUX"] = 19] = "SLICE_AUX";
1180
+ /** Coded slice extension */
1181
+ H264NALUType[H264NALUType["SLICE_EXT"] = 20] = "SLICE_EXT";
1182
+ /** Coded slice extension for a depth view component or a 3D-AVC texture view component */
1183
+ H264NALUType[H264NALUType["SLICE_LAYER_EXT"] = 21] = "SLICE_LAYER_EXT";
1184
+ // 22, 23 reserved
1185
+ })(H264NALUType || (H264NALUType = {}));
1186
+ /**
1187
+ * H.265/HEVC NALU types according to ITU-T H.265
1188
+ */
1189
+ var H265NALUType;
1190
+ (function (H265NALUType) {
1191
+ /** Coded slice segment of a non-TSA, non-STSA trailing picture */
1192
+ H265NALUType[H265NALUType["TRAIL_N"] = 0] = "TRAIL_N";
1193
+ /** Coded slice segment of a non-TSA, non-STSA trailing picture */
1194
+ H265NALUType[H265NALUType["TRAIL_R"] = 1] = "TRAIL_R";
1195
+ /** Coded slice segment of a TSA picture */
1196
+ H265NALUType[H265NALUType["TSA_N"] = 2] = "TSA_N";
1197
+ /** Coded slice segment of a TSA picture */
1198
+ H265NALUType[H265NALUType["TSA_R"] = 3] = "TSA_R";
1199
+ /** Coded slice segment of an STSA picture */
1200
+ H265NALUType[H265NALUType["STSA_N"] = 4] = "STSA_N";
1201
+ /** Coded slice segment of an STSA picture */
1202
+ H265NALUType[H265NALUType["STSA_R"] = 5] = "STSA_R";
1203
+ /** Coded slice segment of a RADL picture */
1204
+ H265NALUType[H265NALUType["RADL_N"] = 6] = "RADL_N";
1205
+ /** Coded slice segment of a RADL picture */
1206
+ H265NALUType[H265NALUType["RADL_R"] = 7] = "RADL_R";
1207
+ /** Coded slice segment of a RASL picture */
1208
+ H265NALUType[H265NALUType["RASL_N"] = 8] = "RASL_N";
1209
+ /** Coded slice segment of a RASL picture */
1210
+ H265NALUType[H265NALUType["RASL_R"] = 9] = "RASL_R";
1211
+ // 10-15 reserved
1212
+ /** Coded slice segment of a BLA picture */
1213
+ H265NALUType[H265NALUType["BLA_W_LP"] = 16] = "BLA_W_LP";
1214
+ /** Coded slice segment of a BLA picture */
1215
+ H265NALUType[H265NALUType["BLA_W_RADL"] = 17] = "BLA_W_RADL";
1216
+ /** Coded slice segment of a BLA picture */
1217
+ H265NALUType[H265NALUType["BLA_N_LP"] = 18] = "BLA_N_LP";
1218
+ /** Coded slice segment of an IDR picture */
1219
+ H265NALUType[H265NALUType["IDR_W_RADL"] = 19] = "IDR_W_RADL";
1220
+ /** Coded slice segment of an IDR picture */
1221
+ H265NALUType[H265NALUType["IDR_N_LP"] = 20] = "IDR_N_LP";
1222
+ /** Coded slice segment of a CRA picture */
1223
+ H265NALUType[H265NALUType["CRA_NUT"] = 21] = "CRA_NUT";
1224
+ // 22-31 reserved
1225
+ /** Video parameter set */
1226
+ H265NALUType[H265NALUType["VPS_NUT"] = 32] = "VPS_NUT";
1227
+ /** Sequence parameter set */
1228
+ H265NALUType[H265NALUType["SPS_NUT"] = 33] = "SPS_NUT";
1229
+ /** Picture parameter set */
1230
+ H265NALUType[H265NALUType["PPS_NUT"] = 34] = "PPS_NUT";
1231
+ /** Access unit delimiter */
1232
+ H265NALUType[H265NALUType["AUD_NUT"] = 35] = "AUD_NUT";
1233
+ /** End of sequence */
1234
+ H265NALUType[H265NALUType["EOS_NUT"] = 36] = "EOS_NUT";
1235
+ /** End of bitstream */
1236
+ H265NALUType[H265NALUType["EOB_NUT"] = 37] = "EOB_NUT";
1237
+ /** Filler data */
1238
+ H265NALUType[H265NALUType["FD_NUT"] = 38] = "FD_NUT";
1239
+ /** Supplemental enhancement information */
1240
+ H265NALUType[H265NALUType["PREFIX_SEI_NUT"] = 39] = "PREFIX_SEI_NUT";
1241
+ /** Supplemental enhancement information */
1242
+ H265NALUType[H265NALUType["SUFFIX_SEI_NUT"] = 40] = "SUFFIX_SEI_NUT";
1243
+ // 41-47 reserved
1244
+ // 48-63 unspecified
1245
+ })(H265NALUType || (H265NALUType = {}));
1246
+ /**
1247
+ * Parse H.264 NALU type from the first byte of a NALU
1248
+ * @param startByte First byte of the NALU
1249
+ * @returns H.264 NALU type
1250
+ */
1251
+ function parseH264NALUType(startByte) {
1252
+ return startByte & kH264NaluTypeMask;
1253
+ }
1254
+ /**
1255
+ * Parse H.265 NALU type from the first byte of a NALU
1256
+ * @param firstByte First byte of the NALU
1257
+ * @returns H.265 NALU type
1258
+ */
1259
+ function parseH265NALUType(firstByte) {
1260
+ // In H.265, NALU type is in bits 1-6 (shifted right by 1)
1261
+ return firstByte >> 1 & 0x3f;
1262
+ }
1263
+ /**
1264
+ * Check if H.264 NALU type is a slice (IDR or non-IDR)
1265
+ * @param naluType H.264 NALU type
1266
+ * @returns True if the NALU is a slice
1267
+ */
1268
+ function isH264SliceNALU(naluType) {
1269
+ return naluType === H264NALUType.SLICE_IDR || naluType === H264NALUType.SLICE_NON_IDR;
1270
+ }
1271
+ /**
1272
+ * Check if H.265 NALU type is a slice
1273
+ * @param naluType H.265 NALU type
1274
+ * @returns True if the NALU is a slice
1275
+ */
1276
+ function isH265SliceNALU(naluType) {
1277
+ return (
1278
+ // VCL NALUs (Video Coding Layer) - slice segments
1279
+ naluType === H265NALUType.TRAIL_N || naluType === H265NALUType.TRAIL_R || naluType === H265NALUType.TSA_N || naluType === H265NALUType.TSA_R || naluType === H265NALUType.STSA_N || naluType === H265NALUType.STSA_R || naluType === H265NALUType.RADL_N || naluType === H265NALUType.RADL_R || naluType === H265NALUType.RASL_N || naluType === H265NALUType.RASL_R || naluType === H265NALUType.BLA_W_LP || naluType === H265NALUType.BLA_W_RADL || naluType === H265NALUType.BLA_N_LP || naluType === H265NALUType.IDR_W_RADL || naluType === H265NALUType.IDR_N_LP || naluType === H265NALUType.CRA_NUT
1280
+ );
1281
+ }
1282
+ /**
1283
+ * Detect codec type by examining NALU types in the data
1284
+ * @param data Frame data
1285
+ * @param naluIndices Indices where NALUs start
1286
+ * @returns Detected codec type
1287
+ */
1288
+ function detectCodecFromNALUs(data, naluIndices) {
1289
+ for (const naluIndex of naluIndices) {
1290
+ if (isH264SliceNALU(parseH264NALUType(data[naluIndex]))) return 'h264';
1291
+ if (isH265SliceNALU(parseH265NALUType(data[naluIndex]))) return 'h265';
1292
+ }
1293
+ return 'unknown';
1294
+ }
1295
+ /**
1296
+ * Find the first slice NALU and return the number of unencrypted bytes
1297
+ * @param data Frame data
1298
+ * @param naluIndices Indices where NALUs start
1299
+ * @param codec Codec type to use for parsing
1300
+ * @returns Number of unencrypted bytes (index + 2) or null if no slice found
1301
+ */
1302
+ function findSliceNALUUnencryptedBytes(data, naluIndices, codec) {
1303
+ for (const index of naluIndices) {
1304
+ if (codec === 'h265') {
1305
+ const type = parseH265NALUType(data[index]);
1306
+ if (isH265SliceNALU(type)) {
1307
+ return index + 2;
1308
+ }
1309
+ } else {
1310
+ const type = parseH264NALUType(data[index]);
1311
+ if (isH264SliceNALU(type)) {
1312
+ return index + 2;
1313
+ }
1314
+ }
1315
+ }
1316
+ return null;
1317
+ }
1318
+ /**
1319
+ * Find all NALU start indices in a byte stream
1320
+ * Supports both H.264 and H.265 with 3-byte and 4-byte start codes
1321
+ *
1322
+ * This function slices the NALUs present in the supplied buffer, assuming it is already byte-aligned.
1323
+ * Code adapted from https://github.com/medooze/h264-frame-parser/blob/main/lib/NalUnits.ts to return indices only
1324
+ *
1325
+ * @param stream Byte stream containing NALUs
1326
+ * @returns Array of indices where NALUs start (after the start code)
1327
+ */
1328
+ function findNALUIndices(stream) {
1329
+ const result = [];
1330
+ let start = 0,
1331
+ pos = 0,
1332
+ searchLength = stream.length - 3; // Changed to -3 to handle 4-byte start codes
1333
+ while (pos < searchLength) {
1334
+ // skip until end of current NALU - check for both 3-byte and 4-byte start codes
1335
+ while (pos < searchLength) {
1336
+ // Check for 4-byte start code: 0x00 0x00 0x00 0x01
1337
+ if (pos < searchLength - 1 && stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 0 && stream[pos + 3] === 1) {
1338
+ break;
1339
+ }
1340
+ // Check for 3-byte start code: 0x00 0x00 0x01
1341
+ if (stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 1) {
1342
+ break;
1343
+ }
1344
+ pos++;
1345
+ }
1346
+ if (pos >= searchLength) pos = stream.length;
1347
+ // remove trailing zeros from current NALU
1348
+ let end = pos;
1349
+ while (end > start && stream[end - 1] === 0) end--;
1350
+ // save current NALU
1351
+ if (start === 0) {
1352
+ if (end !== start) throw TypeError('byte stream contains leading data');
1353
+ } else {
1354
+ result.push(start);
1355
+ }
1356
+ // begin new NALU - determine start code length
1357
+ let startCodeLength = 3;
1358
+ if (pos < stream.length - 3 && stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 0 && stream[pos + 3] === 1) {
1359
+ startCodeLength = 4;
1360
+ }
1361
+ start = pos = pos + startCodeLength;
1362
+ }
1363
+ return result;
1364
+ }
1365
+ /**
1366
+ * Process NALU data for frame encryption, detecting codec and finding unencrypted bytes
1367
+ * @param data Frame data
1368
+ * @param knownCodec Known codec from other sources (optional)
1369
+ * @returns NALU processing result
1370
+ */
1371
+ function processNALUsForEncryption(data, knownCodec) {
1372
+ const naluIndices = findNALUIndices(data);
1373
+ const detectedCodec = knownCodec !== null && knownCodec !== void 0 ? knownCodec : detectCodecFromNALUs(data, naluIndices);
1374
+ if (detectedCodec === 'unknown') {
1375
+ return {
1376
+ unencryptedBytes: 0,
1377
+ detectedCodec,
1378
+ requiresNALUProcessing: false
1379
+ };
1380
+ }
1381
+ const unencryptedBytes = findSliceNALUUnencryptedBytes(data, naluIndices, detectedCodec);
1382
+ if (unencryptedBytes === null) {
1383
+ throw new TypeError('Could not find NALU');
1384
+ }
1385
+ return {
1386
+ unencryptedBytes,
1387
+ detectedCodec,
1388
+ requiresNALUProcessing: true
1389
+ };
1390
+ }
1391
+
1116
1392
  const encryptionEnabledMap = new Map();
1117
1393
  class BaseFrameCryptor extends eventsExports.EventEmitter {
1118
1394
  encodeFunction(encodedFrame, controller) {
@@ -1294,7 +1570,7 @@ class FrameCryptor extends BaseFrameCryptor {
1294
1570
  newDataWithoutHeader.set(new Uint8Array(cipherText)); // add ciphertext.
1295
1571
  newDataWithoutHeader.set(new Uint8Array(iv), cipherText.byteLength); // append IV.
1296
1572
  newDataWithoutHeader.set(frameTrailer, cipherText.byteLength + iv.byteLength); // append frame trailer.
1297
- if (frameInfo.isH264) {
1573
+ if (frameInfo.requiresNALUProcessing) {
1298
1574
  newDataWithoutHeader = writeRbsp(newDataWithoutHeader);
1299
1575
  }
1300
1576
  var newData = new Uint8Array(frameHeader.byteLength + newDataWithoutHeader.byteLength);
@@ -1402,7 +1678,7 @@ class FrameCryptor extends BaseFrameCryptor {
1402
1678
  try {
1403
1679
  const frameHeader = new Uint8Array(encodedFrame.data, 0, frameInfo.unencryptedBytes);
1404
1680
  var encryptedData = new Uint8Array(encodedFrame.data, frameHeader.length, encodedFrame.data.byteLength - frameHeader.length);
1405
- if (frameInfo.isH264 && needsRbspUnescaping(encryptedData)) {
1681
+ if (frameInfo.requiresNALUProcessing && needsRbspUnescaping(encryptedData)) {
1406
1682
  encryptedData = parseRbsp(encryptedData);
1407
1683
  const newUint8 = new Uint8Array(frameHeader.byteLength + encryptedData.byteLength);
1408
1684
  newUint8.set(frameHeader);
@@ -1504,56 +1780,59 @@ class FrameCryptor extends BaseFrameCryptor {
1504
1780
  }
1505
1781
  getUnencryptedBytes(frame) {
1506
1782
  var _a;
1507
- var frameInfo = {
1508
- unencryptedBytes: 0,
1509
- isH264: false
1510
- };
1511
- if (isVideoFrame(frame)) {
1512
- let detectedCodec = (_a = this.getVideoCodec(frame)) !== null && _a !== void 0 ? _a : this.videoCodec;
1513
- if (detectedCodec !== this.detectedCodec) {
1514
- workerLogger.debug('detected different codec', Object.assign({
1515
- detectedCodec,
1516
- oldCodec: this.detectedCodec
1517
- }, this.logContext));
1518
- this.detectedCodec = detectedCodec;
1519
- }
1520
- if (detectedCodec === 'av1') {
1521
- throw new Error("".concat(detectedCodec, " is not yet supported for end to end encryption"));
1522
- }
1523
- if (detectedCodec === 'vp8') {
1524
- frameInfo.unencryptedBytes = UNENCRYPTED_BYTES[frame.type];
1525
- } else if (detectedCodec === 'vp9') {
1526
- frameInfo.unencryptedBytes = 0;
1527
- return frameInfo;
1528
- }
1529
- const data = new Uint8Array(frame.data);
1530
- try {
1531
- const naluIndices = findNALUIndices(data);
1532
- // if the detected codec is undefined we test whether it _looks_ like a h264 frame as a best guess
1533
- frameInfo.isH264 = detectedCodec === 'h264' || naluIndices.some(naluIndex => [NALUType.SLICE_IDR, NALUType.SLICE_NON_IDR].includes(parseNALUType(data[naluIndex])));
1534
- if (frameInfo.isH264) {
1535
- for (const index of naluIndices) {
1536
- let type = parseNALUType(data[index]);
1537
- switch (type) {
1538
- case NALUType.SLICE_IDR:
1539
- case NALUType.SLICE_NON_IDR:
1540
- frameInfo.unencryptedBytes = index + 2;
1541
- return frameInfo;
1542
- default:
1543
- break;
1544
- }
1545
- }
1546
- throw new TypeError('Could not find NALU');
1547
- }
1548
- } catch (e) {
1549
- // no op, we just continue and fallback to vp8
1783
+ // Handle audio frames
1784
+ if (!isVideoFrame(frame)) {
1785
+ return {
1786
+ unencryptedBytes: UNENCRYPTED_BYTES.audio,
1787
+ requiresNALUProcessing: false
1788
+ };
1789
+ }
1790
+ // Detect and track codec changes
1791
+ const detectedCodec = (_a = this.getVideoCodec(frame)) !== null && _a !== void 0 ? _a : this.videoCodec;
1792
+ if (detectedCodec !== this.detectedCodec) {
1793
+ workerLogger.debug('detected different codec', Object.assign({
1794
+ detectedCodec,
1795
+ oldCodec: this.detectedCodec
1796
+ }, this.logContext));
1797
+ this.detectedCodec = detectedCodec;
1798
+ }
1799
+ // Check for unsupported codecs
1800
+ if (detectedCodec === 'av1') {
1801
+ throw new Error("".concat(detectedCodec, " is not yet supported for end to end encryption"));
1802
+ }
1803
+ // Handle VP8/VP9 codecs (no NALU processing needed)
1804
+ if (detectedCodec === 'vp8') {
1805
+ return {
1806
+ unencryptedBytes: UNENCRYPTED_BYTES[frame.type],
1807
+ requiresNALUProcessing: false
1808
+ };
1809
+ }
1810
+ if (detectedCodec === 'vp9') {
1811
+ return {
1812
+ unencryptedBytes: 0,
1813
+ requiresNALUProcessing: false
1814
+ };
1815
+ }
1816
+ // Try NALU processing for H.264/H.265 codecs
1817
+ try {
1818
+ const knownCodec = detectedCodec === 'h264' || detectedCodec === 'h265' ? detectedCodec : undefined;
1819
+ const naluResult = processNALUsForEncryption(new Uint8Array(frame.data), knownCodec);
1820
+ if (naluResult.requiresNALUProcessing) {
1821
+ return {
1822
+ unencryptedBytes: naluResult.unencryptedBytes,
1823
+ requiresNALUProcessing: true
1824
+ };
1550
1825
  }
1551
- frameInfo.unencryptedBytes = UNENCRYPTED_BYTES[frame.type];
1552
- return frameInfo;
1553
- } else {
1554
- frameInfo.unencryptedBytes = UNENCRYPTED_BYTES.audio;
1555
- return frameInfo;
1826
+ } catch (e) {
1827
+ workerLogger.debug('NALU processing failed, falling back to VP8 handling', Object.assign({
1828
+ error: e
1829
+ }, this.logContext));
1556
1830
  }
1831
+ // Fallback to VP8 handling
1832
+ return {
1833
+ unencryptedBytes: UNENCRYPTED_BYTES[frame.type],
1834
+ requiresNALUProcessing: false
1835
+ };
1557
1836
  }
1558
1837
  /**
1559
1838
  * inspects frame payloadtype if available and maps it to the codec specified in rtpMap
@@ -1567,80 +1846,6 @@ class FrameCryptor extends BaseFrameCryptor {
1567
1846
  return codec;
1568
1847
  }
1569
1848
  }
1570
- /**
1571
- * Slice the NALUs present in the supplied buffer, assuming it is already byte-aligned
1572
- * code adapted from https://github.com/medooze/h264-frame-parser/blob/main/lib/NalUnits.ts to return indices only
1573
- */
1574
- function findNALUIndices(stream) {
1575
- const result = [];
1576
- let start = 0,
1577
- pos = 0,
1578
- searchLength = stream.length - 2;
1579
- while (pos < searchLength) {
1580
- // skip until end of current NALU
1581
- while (pos < searchLength && !(stream[pos] === 0 && stream[pos + 1] === 0 && stream[pos + 2] === 1)) pos++;
1582
- if (pos >= searchLength) pos = stream.length;
1583
- // remove trailing zeros from current NALU
1584
- let end = pos;
1585
- while (end > start && stream[end - 1] === 0) end--;
1586
- // save current NALU
1587
- if (start === 0) {
1588
- if (end !== start) throw TypeError('byte stream contains leading data');
1589
- } else {
1590
- result.push(start);
1591
- }
1592
- // begin new NALU
1593
- start = pos = pos + 3;
1594
- }
1595
- return result;
1596
- }
1597
- function parseNALUType(startByte) {
1598
- return startByte & kNaluTypeMask;
1599
- }
1600
- const kNaluTypeMask = 0x1f;
1601
- var NALUType;
1602
- (function (NALUType) {
1603
- /** Coded slice of a non-IDR picture */
1604
- NALUType[NALUType["SLICE_NON_IDR"] = 1] = "SLICE_NON_IDR";
1605
- /** Coded slice data partition A */
1606
- NALUType[NALUType["SLICE_PARTITION_A"] = 2] = "SLICE_PARTITION_A";
1607
- /** Coded slice data partition B */
1608
- NALUType[NALUType["SLICE_PARTITION_B"] = 3] = "SLICE_PARTITION_B";
1609
- /** Coded slice data partition C */
1610
- NALUType[NALUType["SLICE_PARTITION_C"] = 4] = "SLICE_PARTITION_C";
1611
- /** Coded slice of an IDR picture */
1612
- NALUType[NALUType["SLICE_IDR"] = 5] = "SLICE_IDR";
1613
- /** Supplemental enhancement information */
1614
- NALUType[NALUType["SEI"] = 6] = "SEI";
1615
- /** Sequence parameter set */
1616
- NALUType[NALUType["SPS"] = 7] = "SPS";
1617
- /** Picture parameter set */
1618
- NALUType[NALUType["PPS"] = 8] = "PPS";
1619
- /** Access unit delimiter */
1620
- NALUType[NALUType["AUD"] = 9] = "AUD";
1621
- /** End of sequence */
1622
- NALUType[NALUType["END_SEQ"] = 10] = "END_SEQ";
1623
- /** End of stream */
1624
- NALUType[NALUType["END_STREAM"] = 11] = "END_STREAM";
1625
- /** Filler data */
1626
- NALUType[NALUType["FILLER_DATA"] = 12] = "FILLER_DATA";
1627
- /** Sequence parameter set extension */
1628
- NALUType[NALUType["SPS_EXT"] = 13] = "SPS_EXT";
1629
- /** Prefix NAL unit */
1630
- NALUType[NALUType["PREFIX_NALU"] = 14] = "PREFIX_NALU";
1631
- /** Subset sequence parameter set */
1632
- NALUType[NALUType["SUBSET_SPS"] = 15] = "SUBSET_SPS";
1633
- /** Depth parameter set */
1634
- NALUType[NALUType["DPS"] = 16] = "DPS";
1635
- // 17, 18 reserved
1636
- /** Coded slice of an auxiliary coded picture without partitioning */
1637
- NALUType[NALUType["SLICE_AUX"] = 19] = "SLICE_AUX";
1638
- /** Coded slice extension */
1639
- NALUType[NALUType["SLICE_EXT"] = 20] = "SLICE_EXT";
1640
- /** Coded slice extension for a depth view component or a 3D-AVC texture view component */
1641
- NALUType[NALUType["SLICE_LAYER_EXT"] = 21] = "SLICE_LAYER_EXT";
1642
- // 22, 23 reserved
1643
- })(NALUType || (NALUType = {}));
1644
1849
  /**
1645
1850
  * we use a magic frame trailer to detect whether a frame is injected
1646
1851
  * by the livekit server and thus to be treated as unencrypted