@camstack/addon-pipeline 1.2.371 → 1.2.373

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 (32) hide show
  1. package/dist/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-n9v8MUL6.js → custom-model-catalog-M-ECFeNJ.js} +2 -2
  4. package/dist/{custom-model-catalog-C6C2cSwd.mjs → custom-model-catalog-UaaDgBow.mjs} +2 -2
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-8AEiKOFO.mjs → dist-BT3uSeR7.mjs} +25 -5
  8. package/dist/{dist-DmuX7IFJ.js → dist-QBVFmPYQ.js} +25 -5
  9. package/dist/{model-catalogs-DO4aA1qo.js → model-catalogs-C42Mo0jo.js} +1 -1
  10. package/dist/{model-catalogs-BSWAHZJh.mjs → model-catalogs-ChW0i2P6.mjs} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-DqA6nD1z.js → node-B6ev-Hqa.js} +1 -1
  14. package/dist/{node-Bd4va7c-.mjs → node-BVRFl1hZ.mjs} +1 -1
  15. package/dist/pipeline-runner/index.js +3 -3
  16. package/dist/pipeline-runner/index.mjs +3 -3
  17. package/dist/{process-memory-2OQ2oxrz.js → process-memory-Cdf2A2qg.js} +1 -1
  18. package/dist/{process-memory-BvS2Z88w.mjs → process-memory-DdeedEgU.mjs} +1 -1
  19. package/dist/recorder/index.js +26 -5
  20. package/dist/recorder/index.mjs +26 -5
  21. package/dist/{segment-demux-js-By9MLn_g.mjs → segment-demux-js-8CfbEFeD.mjs} +1 -1
  22. package/dist/{segment-demux-js-BkGK1rhu.js → segment-demux-js-Dp0OI-2n.js} +1 -1
  23. package/dist/stream-broker/_stub.js +1 -1
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DjoAY42S.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Dl3wrJnM.mjs} +2 -2
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-nua-KuDg.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BFRtKBOe.mjs} +1 -1
  26. package/dist/stream-broker/demux-worker-child.js +1 -1
  27. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  28. package/dist/stream-broker/{hostInit-DPiGNc5u.mjs → hostInit-Bq36nWMP.mjs} +2 -2
  29. package/dist/stream-broker/index.js +407 -72
  30. package/dist/stream-broker/index.mjs +407 -72
  31. package/dist/stream-broker/remoteEntry.js +1 -1
  32. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
2
- import { $n as _enum, An as errMsg, At as gifFromMp4Args, Bt as memoizePinnedFfmpeg, D as G711_SCALE_CORRECTION_DB, Dn as isSoftwareDecode, Dt as ffmpegRate, En as invocationFromEncodeProfile, Ft as loadContributionCapability, Gn as makeProfileBrokerId, I as RATE_CONTROL_RELAXED, It as logChannelsCapability, J as addonWidgetsSourceCapability, Jn as nodePin, Kn as makeSourceBrokerId, L as RATE_CONTROL_TIGHT, Mn as BaseAddon, Nn as CAM_PROFILE_ORDER, On as DeviceFeature, Pn as CamProfileSchema, Qt as rankStreams, Rn as createEvent, Vn as expandAudioChunkToF32le, W as RingBuffer, Wn as isEvent, Yn as parseProfileBrokerId, an as resolveEgressDecodeHwAccel, ar as number, bn as webrtcSessionCapability, bt as egressTransportFromRequest, c as CLIP_STILL_REASON_CODE_HEADER, cr as record, dr as EventCategory, dt as declareLogChannel, er as array, gn as streamBrokerCapability, ir as literal, it as cameraStreamsCapability, jn as BOOT_RECOVERY_BACKOFF_MS, kn as DeviceType, kt as gifDeriveSpeed, lr as string, lt as createHwAccelCache, nr as discriminatedUnion, o as BatteryStatusSchema, or as object, ot as clipExportFailureOf, pt as deriveBatteryPresence, qn as maskUrlCredentials, s as CLIP_BROKER_PACED_RATES, tr as boolean, tt as batteryCapability, ur as union, ut as createLogChannelsProvider, w as EncodeProfileSchema, wn as buildFfmpegArgs, xn as AUDIO_PRESETS, yt as egressTranscodeSharingKey } from "../dist-8AEiKOFO.mjs";
3
- import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-Bd4va7c-.mjs";
2
+ import { $n as _enum, An as errMsg, At as gifFromMp4Args, Bt as memoizePinnedFfmpeg, D as G711_SCALE_CORRECTION_DB, Dn as isSoftwareDecode, Dt as ffmpegRate, En as invocationFromEncodeProfile, Ft as loadContributionCapability, Gn as makeProfileBrokerId, I as RATE_CONTROL_RELAXED, It as logChannelsCapability, J as addonWidgetsSourceCapability, Jn as nodePin, Kn as makeSourceBrokerId, L as RATE_CONTROL_TIGHT, Mn as BaseAddon, Nn as CAM_PROFILE_ORDER, On as DeviceFeature, Pn as CamProfileSchema, Qt as rankStreams, Rn as createEvent, Vn as expandAudioChunkToF32le, W as RingBuffer, Wn as isEvent, Yn as parseProfileBrokerId, an as resolveEgressDecodeHwAccel, ar as number, bn as webrtcSessionCapability, bt as egressTransportFromRequest, c as CLIP_STILL_REASON_CODE_HEADER, cr as record, dr as EventCategory, dt as declareLogChannel, er as array, gn as streamBrokerCapability, ir as literal, it as cameraStreamsCapability, jn as BOOT_RECOVERY_BACKOFF_MS, kn as DeviceType, kt as gifDeriveSpeed, lr as string, lt as createHwAccelCache, nr as discriminatedUnion, o as BatteryStatusSchema, or as object, ot as clipExportFailureOf, pt as deriveBatteryPresence, qn as maskUrlCredentials, s as CLIP_BROKER_PACED_RATES, tr as boolean, tt as batteryCapability, ur as union, ut as createLogChannelsProvider, w as EncodeProfileSchema, wn as buildFfmpegArgs, xn as AUDIO_PRESETS, yt as egressTranscodeSharingKey } from "../dist-BT3uSeR7.mjs";
3
+ import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-BVRFl1hZ.mjs";
4
4
  import { a as parseRestreamPath, d as PREBUFFER_MAX_MS, f as capPreRollRequestMs, i as isLiveEdgeOnlyDial, m as resolvePrebuffer, n as NO_DECLARATION, p as prebufferRetentionMs, s as sdpVariantFor, u as withSentryIntent } from "../restream-intent-DyGmLAU9.mjs";
5
5
  import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
6
6
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
7
- import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-By9MLn_g.mjs";
7
+ import { a as require_mp4box_all, c as parseNalLengthSize, i as runFfmpegOnce, n as demuxSegmentPreferJs, o as avccSampleToAnnexB, r as FfmpegFallbackRefusedError, s as buildKeyframeAnnexB, t as demuxSegmentFullJs } from "../segment-demux-js-8CfbEFeD.mjs";
8
8
  import { createRequire } from "node:module";
9
9
  import * as os$1 from "node:os";
10
10
  import { networkInterfaces, tmpdir } from "node:os";
@@ -3085,6 +3085,223 @@ var RtpPacketizer = class {
3085
3085
  };
3086
3086
  }
3087
3087
  };
3088
+ function isTransferable(buf) {
3089
+ return typeof Reflect.get(buf, "transfer") === "function";
3090
+ }
3091
+ var Slab = class {
3092
+ now;
3093
+ buffer;
3094
+ offset = 0;
3095
+ holds = 0;
3096
+ state = "active";
3097
+ releasedAt = 0;
3098
+ constructor(buffer, now) {
3099
+ this.now = now;
3100
+ this.buffer = buffer;
3101
+ }
3102
+ };
3103
+ /** ArrayBuffer -> slab, for every slab ArrayBuffer currently in service. */
3104
+ var slabOf = /* @__PURE__ */ new WeakMap();
3105
+ var slabsLive = 0;
3106
+ var slabsFree = 0;
3107
+ var slabAllocs = 0;
3108
+ var slabRecycles = 0;
3109
+ function readSlabTotals() {
3110
+ return {
3111
+ live: slabsLive,
3112
+ free: slabsFree,
3113
+ allocs: slabAllocs,
3114
+ recycles: slabRecycles
3115
+ };
3116
+ }
3117
+ /** True when `buf` is a view of a pool slab. */
3118
+ function isSlabView(buf) {
3119
+ return slabOf.has(buf.buffer);
3120
+ }
3121
+ /** A retaining structure keeps `buf`: its slab cannot be recycled until released. */
3122
+ function holdPacket(buf) {
3123
+ const slab = slabOf.get(buf.buffer);
3124
+ if (slab === void 0) return;
3125
+ slab.holds += 1;
3126
+ if (slab.state === "quarantined") slab.state = "held";
3127
+ }
3128
+ /** The matching drop. Safe on a non-slab buffer and on a stale (recycled) view. */
3129
+ function releasePacket(buf) {
3130
+ const slab = slabOf.get(buf.buffer);
3131
+ if (slab === void 0 || slab.holds === 0) return;
3132
+ slab.holds -= 1;
3133
+ if (slab.holds === 0 && slab.state === "held") {
3134
+ slab.state = "quarantined";
3135
+ slab.releasedAt = slab.now();
3136
+ }
3137
+ }
3138
+ function releasePackets(bufs) {
3139
+ for (const b of bufs) releasePacket(b);
3140
+ }
3141
+ /**
3142
+ * A consumer's own hold on the last `limit` packets it forwarded.
3143
+ *
3144
+ * werift keeps the last RTP_HISTORY_SIZE (4096) packets of each sender for NACK
3145
+ * retransmission, and those packets' payloads are views of the broker's slabs.
3146
+ * The session mirrors that window here: each forwarded packet is held, and
3147
+ * the one falling out of the window is released, so a retransmit asked for
3148
+ * within the history is served from bytes nobody has recycled.
3149
+ */
3150
+ var HeldPacketWindow = class {
3151
+ limit;
3152
+ slots;
3153
+ next = 0;
3154
+ constructor(limit) {
3155
+ this.limit = limit;
3156
+ this.slots = new Array(limit).fill(void 0);
3157
+ }
3158
+ hold(buf) {
3159
+ holdPacket(buf);
3160
+ const evicted = this.slots[this.next];
3161
+ if (evicted !== void 0) releasePacket(evicted);
3162
+ this.slots[this.next] = buf;
3163
+ this.next = (this.next + 1) % this.limit;
3164
+ }
3165
+ releaseAll() {
3166
+ for (let i = 0; i < this.slots.length; i++) {
3167
+ const held = this.slots[i];
3168
+ if (held !== void 0) releasePacket(held);
3169
+ this.slots[i] = void 0;
3170
+ }
3171
+ this.next = 0;
3172
+ }
3173
+ };
3174
+ var PacketSlabPool = class {
3175
+ slabBytes;
3176
+ maxPacket;
3177
+ quarantineMs;
3178
+ maxFree;
3179
+ now;
3180
+ active = null;
3181
+ /** Sealed slabs, oldest first; entries may have been re-held since. */
3182
+ sealed = [];
3183
+ free = [];
3184
+ allocs = 0;
3185
+ recycles = 0;
3186
+ ownBuffers = 0;
3187
+ constructor(options = {}) {
3188
+ this.slabBytes = options.slabBytes ?? 65536;
3189
+ this.maxPacket = this.slabBytes >> 2;
3190
+ this.quarantineMs = options.quarantineMs ?? 2e3;
3191
+ this.maxFree = options.maxFree ?? 2;
3192
+ this.now = options.now ?? Date.now;
3193
+ }
3194
+ /**
3195
+ * A copy of `src[start, end)` in this pool's active slab. An oversized
3196
+ * packet gets its own buffer (it owns its ArrayBuffer, like the old
3197
+ * `copyOwned`).
3198
+ */
3199
+ copy(src, start, end) {
3200
+ const length = end - start;
3201
+ recordCopyOwned(length);
3202
+ if (length > this.maxPacket) {
3203
+ this.ownBuffers += 1;
3204
+ const own = Buffer.allocUnsafeSlow(length);
3205
+ src.copy(own, 0, start, end);
3206
+ return own;
3207
+ }
3208
+ let slab = this.active;
3209
+ if (slab === null || slab.offset + length > this.slabBytes) slab = this.rotate();
3210
+ const view = Buffer.from(slab.buffer, slab.offset, length);
3211
+ src.copy(view, 0, start, end);
3212
+ slab.offset += length;
3213
+ return view;
3214
+ }
3215
+ /**
3216
+ * `buf` itself when it is already owned (a slab view or a whole-ArrayBuffer
3217
+ * buffer), else a copy in the active slab. Used for packets that arrive from
3218
+ * paths that do not cut through {@link copy} (push-rtp).
3219
+ */
3220
+ adopt(buf) {
3221
+ if (isSlabView(buf)) return buf;
3222
+ if (buf.byteOffset === 0 && buf.buffer.byteLength === buf.length) return buf;
3223
+ return this.copy(buf, 0, buf.length);
3224
+ }
3225
+ stats() {
3226
+ return {
3227
+ live: this.sealed.length + (this.active === null ? 0 : 1),
3228
+ free: this.free.length,
3229
+ allocs: this.allocs,
3230
+ recycles: this.recycles,
3231
+ ownBuffers: this.ownBuffers
3232
+ };
3233
+ }
3234
+ /** Drop every slab of this pool from the process counters (stream torn down). */
3235
+ dispose() {
3236
+ const live = this.sealed.length + (this.active === null ? 0 : 1);
3237
+ slabsLive -= live;
3238
+ slabsFree -= this.free.length;
3239
+ this.sealed = [];
3240
+ this.free = [];
3241
+ this.active = null;
3242
+ }
3243
+ rotate() {
3244
+ const previous = this.active;
3245
+ if (previous !== null) {
3246
+ this.sealed.push(previous);
3247
+ if (previous.holds === 0) {
3248
+ previous.state = "quarantined";
3249
+ previous.releasedAt = this.now();
3250
+ } else previous.state = "held";
3251
+ }
3252
+ this.reclaim();
3253
+ const next = this.free.pop() ?? this.allocate();
3254
+ if (next.state === "free") {
3255
+ slabsFree -= 1;
3256
+ slabsLive += 1;
3257
+ }
3258
+ next.state = "active";
3259
+ next.offset = 0;
3260
+ next.holds = 0;
3261
+ this.active = next;
3262
+ return next;
3263
+ }
3264
+ allocate() {
3265
+ this.allocs += 1;
3266
+ slabAllocs += 1;
3267
+ slabsLive += 1;
3268
+ const slab = new Slab(new ArrayBuffer(this.slabBytes), this.now);
3269
+ slabOf.set(slab.buffer, slab);
3270
+ return slab;
3271
+ }
3272
+ /** Move quarantined slabs whose time is up to the free list. */
3273
+ reclaim() {
3274
+ if (this.sealed.length === 0) return;
3275
+ const cutoff = this.now() - this.quarantineMs;
3276
+ const keep = [];
3277
+ for (const slab of this.sealed) if (slab.state === "quarantined" && slab.releasedAt <= cutoff) this.recycle(slab);
3278
+ else keep.push(slab);
3279
+ this.sealed = keep;
3280
+ }
3281
+ recycle(slab) {
3282
+ slabsLive -= 1;
3283
+ if (this.free.length >= this.maxFree) {
3284
+ slabOf.delete(slab.buffer);
3285
+ slab.state = "free";
3286
+ return;
3287
+ }
3288
+ const old = slab.buffer;
3289
+ slabOf.delete(old);
3290
+ slab.buffer = isTransferable(old) ? old.transfer() : new ArrayBuffer(this.slabBytes);
3291
+ if (!isTransferable(old)) {
3292
+ this.allocs += 1;
3293
+ slabAllocs += 1;
3294
+ }
3295
+ slabOf.set(slab.buffer, slab);
3296
+ slab.state = "free";
3297
+ slab.offset = 0;
3298
+ slab.holds = 0;
3299
+ this.free.push(slab);
3300
+ slabsFree += 1;
3301
+ this.recycles += 1;
3302
+ slabRecycles += 1;
3303
+ }
3304
+ };
3088
3305
  //#endregion
3089
3306
  //#region src/stream-broker/stream-broker/owned-buffer.ts
3090
3307
  /**
@@ -3111,10 +3328,20 @@ function ownsBackingStore(buf) {
3111
3328
  }
3112
3329
  /**
3113
3330
  * What retaining `buf` really costs: its own length when it owns its
3114
- * ArrayBuffer, the whole shared slab when it is a view of one.
3331
+ * ArrayBuffer (a packet slab is densely packed, so a slab view counts as its
3332
+ * own length too), the whole shared slab when it is a view of a pooled one.
3333
+ * The diagnostic `BackingTally` still sums the distinct ArrayBuffers.
3115
3334
  */
3116
3335
  function backingStoreBytes(buf) {
3117
- return ownsBackingStore(buf) ? buf.length : buf.buffer.byteLength;
3336
+ return ownsBackingStore(buf) || isSlabView(buf) ? buf.length : buf.buffer.byteLength;
3337
+ }
3338
+ /**
3339
+ * What `buf` PINS: its own length when it is the whole of its ArrayBuffer, the
3340
+ * whole ArrayBuffer otherwise — a packet slab view included. This is the
3341
+ * diagnostic figure (`BackingTally`); caps charge {@link backingStoreBytes}.
3342
+ */
3343
+ function pinnedBackingBytes(buf) {
3344
+ return buf.byteOffset === 0 && buf.buffer.byteLength === buf.length ? buf.length : buf.buffer.byteLength;
3118
3345
  }
3119
3346
  /**
3120
3347
  * `buf` itself when it already owns its ArrayBuffer (decided by inspecting the
@@ -3163,7 +3390,7 @@ var BackingTally = class {
3163
3390
  }
3164
3391
  this.entries += 1;
3165
3392
  this.payload += buf.length;
3166
- const backing = backingStoreBytes(buf);
3393
+ const backing = pinnedBackingBytes(buf);
3167
3394
  this.naive += backing;
3168
3395
  if (!this.seen.has(buf.buffer)) {
3169
3396
  this.seen.add(buf.buffer);
@@ -3456,6 +3683,7 @@ function formatBrokerMem(input) {
3456
3683
  const w = input.worker;
3457
3684
  if (w !== void 0 && w !== null) parts.push(`workers=${w.workers}`, `workerHeapMb=${mb(w.heapBytes)}`, `workerPhysicalMb=${mb(w.physicalBytes)}`, `workerUsedMb=${mb(w.usedBytes)}`, `workerAgeS=${Math.round(w.maxAgeMs / 1e3)}`, `workerIdleExit=${w.idleTerminates ? "yes" : "never"}`);
3458
3685
  parts.push(`brokers=${samples.length}`, `ingesting=${ingesting}`, `annexBPlanes=${planes}`, `rtsp=${rtsp}`, `webrtc=${input.webrtcSessions}`, `encoded=${kindList(consumers)}`, `audioSubs=${audio}`, `pipeClients=${pipe}`, `egress=${input.egressTranscodes}`, `children=${input.childProcesses}`, `opened=${churn.opened}`, `closed=${churn.closed}`, `openedKinds=${kindList(churn.openedByKind)}`, `closedKinds=${kindList(churn.closedByKind)}`, `preBufferMb=${mb(pre)}`, `clipMb=${mb(clip)}`, `rtpRingMb=${mb(rtp)}`, `keyframeMb=${mb(kf)}`, `writeQueueSumMb=${mb(qSum)}`, `writeQueueMaxMb=${mb(qMax)}`, `copiedPackets=${copied.packets}`, `copiedMb=${mb(copied.bytes)}`, `topRingMb=${top === "" ? "none" : top}`);
3686
+ if (input.slabs !== void 0) parts.push(`slabs=${input.slabs.live}/${input.slabs.free}`, `slabAllocs=${input.slabs.allocsDelta}`);
3459
3687
  const sessions = input.sessions;
3460
3688
  if (sessions !== void 0) parts.push(...formatSessionMem(sessions, input));
3461
3689
  return parts.join(" ");
@@ -3845,8 +4073,7 @@ var RtspRestreamer = class {
3845
4073
  this.mutedSdp = null;
3846
4074
  this.deframer = null;
3847
4075
  this.detectedCodec = null;
3848
- this.lastKeyframeRtpPackets = [];
3849
- this.lastKeyframeRtpBytes = 0;
4076
+ this.dropKeyframeRtpCache();
3850
4077
  this._rtpPassthroughMode = false;
3851
4078
  this.collectingKeyframeRtp = false;
3852
4079
  this.egressAudioBlock = null;
@@ -4199,21 +4426,21 @@ var RtspRestreamer = class {
4199
4426
  if (isKeyframe || isParameterSet) {
4200
4427
  if (!this.collectingKeyframeRtp) {
4201
4428
  this.collectingKeyframeRtp = true;
4202
- this.lastKeyframeRtpPackets = [];
4203
- this.lastKeyframeRtpBytes = 0;
4429
+ this.dropKeyframeRtpCache();
4204
4430
  }
4205
4431
  }
4206
4432
  if (this.collectingKeyframeRtp && !isKeyframe && !isParameterSet) this.collectingKeyframeRtp = false;
4207
4433
  if (this.collectingKeyframeRtp) {
4434
+ const cached = isSlabView(rtpData) ? rtpData : detachFromPool(rtpData);
4435
+ holdPacket(cached);
4208
4436
  this.lastKeyframeRtpPackets.push({
4209
- data: detachFromPool(rtpData),
4437
+ data: cached,
4210
4438
  marker: rtpPacket.marker
4211
4439
  });
4212
4440
  this.lastKeyframeRtpBytes += rtpData.length;
4213
4441
  if (this.lastKeyframeRtpBytes > MAX_KEYFRAME_CACHE_BYTES) {
4214
4442
  this.warnCacheCapExceeded("rtp", this.lastKeyframeRtpBytes, this.lastKeyframeRtpPackets.length);
4215
- this.lastKeyframeRtpPackets = [];
4216
- this.lastKeyframeRtpBytes = 0;
4443
+ this.dropKeyframeRtpCache();
4217
4444
  this.collectingKeyframeRtp = false;
4218
4445
  }
4219
4446
  }
@@ -4651,6 +4878,7 @@ var RtspRestreamer = class {
4651
4878
  }
4652
4879
  destroy() {
4653
4880
  this._onSessionCountChanged = null;
4881
+ this.dropKeyframeRtpCache();
4654
4882
  this.sentryAudioSilence?.release("restreamer destroyed");
4655
4883
  this.abortAllReplays();
4656
4884
  this.deframer?.flush();
@@ -4659,6 +4887,12 @@ var RtspRestreamer = class {
4659
4887
  this.packetizer = null;
4660
4888
  this.deframer = null;
4661
4889
  }
4890
+ /** Empty the keyframe RTP cache and let go of the slabs it pinned. */
4891
+ dropKeyframeRtpCache() {
4892
+ for (const cached of this.lastKeyframeRtpPackets) releasePacket(cached.data);
4893
+ this.lastKeyframeRtpPackets = [];
4894
+ this.lastKeyframeRtpBytes = 0;
4895
+ }
4662
4896
  /** Called by deframer for each complete NAL unit. */
4663
4897
  handleNal(nal, isKeyframe) {
4664
4898
  if (!this.codecParams) this.tryInitCodec(nal);
@@ -4788,6 +5022,13 @@ function resolveJoinBurstBound(input) {
4788
5022
  };
4789
5023
  }
4790
5024
  //#endregion
5025
+ //#region src/stream-broker/stream-broker/held-write.ts
5026
+ /** Write `payload`, holding its slab until the write completes (or fails). */
5027
+ function writeHeld(sink, payload) {
5028
+ holdPacket(payload);
5029
+ return sink.write(payload, () => releasePacket(payload));
5030
+ }
5031
+ //#endregion
4791
5032
  //#region src/stream-broker/rtsp/rtsp-session.ts
4792
5033
  /**
4793
5034
  * Handles one RTSP client connection.
@@ -5103,15 +5344,17 @@ var RtspSession = class {
5103
5344
  *
5104
5345
  * ## Why sharing the payload is safe, and what would make it unsafe
5105
5346
  *
5106
- * The payload buffer handed here is a private allocation per packet —
5107
- * `copyOwned(readBuffer, …)` in the readers' `onData`, for video and audio
5108
- * alike — never a view of the reader's socket buffer, which advances on the
5109
- * next read. Nothing in the broker writes into a captured packet: every
5110
- * mutation in this path targets a freshly allocated buffer. (The same object
5111
- * is what the broker's `rtpRing` and the restreamer's keyframe cache retain —
5112
- * it owns its backing store, so `detachFromPool` keeps it as is — and that is
5113
- * fine: a pending write holds its own reference, and the rule below is about
5114
- * mutation, not retention.)
5347
+ * The payload is a view of the broker's packet slab (`slab-pool.ts`, D868),
5348
+ * never of the reader's socket buffer, which advances on the next read.
5349
+ * Nothing in the broker writes into a captured packet: every mutation in this
5350
+ * path targets a freshly allocated buffer. A slab is RECYCLED once the
5351
+ * broker's rings let go of it, and a queued socket write outlives that: Node
5352
+ * keeps the chunk behind a full kernel buffer for as long as the peer takes to
5353
+ * drain (seconds, for a recorder or a LAN decoder), and libuv reads the bytes
5354
+ * only when it writes. So the payload goes out through `writeHeld`, which
5355
+ * holds its slab until Node's write callback (success, error or destroy). A
5356
+ * write that skipped the hold would send a header promising N bytes followed
5357
+ * by zeros.
5115
5358
  *
5116
5359
  * The rule to keep: **`socket.write` forbids MUTATION before the write
5117
5360
  * completes, not retention.** So the header, which IS built per call, must
@@ -5148,7 +5391,7 @@ var RtspSession = class {
5148
5391
  process.nextTick(this.uncorkAccessUnit);
5149
5392
  }
5150
5393
  this.socket.write(header);
5151
- this.socket.write(payload);
5394
+ writeHeld(this.socket, payload);
5152
5395
  if (endOfAccessUnit) this.uncorkAccessUnit();
5153
5396
  this.lastRtpAt = Date.now();
5154
5397
  this.bytesSent += 4 + payload.length;
@@ -10511,6 +10754,7 @@ var Rfc4571Reader = class {
10511
10754
  options;
10512
10755
  callbacks;
10513
10756
  logger;
10757
+ allocatePacket;
10514
10758
  host;
10515
10759
  port;
10516
10760
  authLine;
@@ -10524,6 +10768,7 @@ var Rfc4571Reader = class {
10524
10768
  constructor(options, callbacks) {
10525
10769
  this.options = options;
10526
10770
  this.callbacks = callbacks;
10771
+ this.allocatePacket = options.packetAllocator ?? copyOwned;
10527
10772
  this.logger = options.logger;
10528
10773
  const parsed = new URL(options.url);
10529
10774
  this.host = parsed.hostname;
@@ -10588,7 +10833,7 @@ var Rfc4571Reader = class {
10588
10833
  while (this.readBuffer.length >= 2) {
10589
10834
  const length = this.readBuffer.readUInt16BE(0);
10590
10835
  if (this.readBuffer.length < 2 + length) return;
10591
- const rtpPacket = copyOwned(this.readBuffer, 2, 2 + length);
10836
+ const rtpPacket = this.allocatePacket(this.readBuffer, 2, 2 + length);
10592
10837
  this.readBuffer = this.readBuffer.subarray(2 + length);
10593
10838
  this.dispatchRtp(rtpPacket);
10594
10839
  }
@@ -11109,6 +11354,7 @@ var NativeRtspClient = class {
11109
11354
  keepaliveIntervalMs;
11110
11355
  logger;
11111
11356
  callbacks;
11357
+ allocatePacket;
11112
11358
  /** Parsed SDP from DESCRIBE response. */
11113
11359
  parsedSdp = null;
11114
11360
  rawSdpText = null;
@@ -11135,6 +11381,7 @@ var NativeRtspClient = class {
11135
11381
  tornDown = false;
11136
11382
  constructor(options, callbacks) {
11137
11383
  this.callbacks = callbacks;
11384
+ this.allocatePacket = options.packetAllocator ?? copyOwned;
11138
11385
  this.logger = options.logger;
11139
11386
  this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
11140
11387
  this.keepaliveIntervalMs = options.keepaliveIntervalMs ?? DEFAULT_KEEPALIVE_INTERVAL_MS;
@@ -11307,7 +11554,7 @@ var NativeRtspClient = class {
11307
11554
  const channel = this.readBuffer[1];
11308
11555
  const length = this.readBuffer.readUInt16BE(2);
11309
11556
  if (this.readBuffer.length < 4 + length) return;
11310
- const payload = copyOwned(this.readBuffer, 4, 4 + length);
11557
+ const payload = this.allocatePacket(this.readBuffer, 4, 4 + length);
11311
11558
  this.readBuffer = this.readBuffer.subarray(4 + length);
11312
11559
  this.handleInterleavedPacket(channel, payload);
11313
11560
  } else {
@@ -11762,7 +12009,7 @@ var AudioCodecSession = class {
11762
12009
  processPacket(rtpData) {
11763
12010
  if (this.closed) return;
11764
12011
  if (rtpData.length <= RTP_HEADER_LENGTH) return;
11765
- const payload = rtpData.subarray(RTP_HEADER_LENGTH);
12012
+ const payload = Buffer.from(rtpData.subarray(RTP_HEADER_LENGTH));
11766
12013
  this.packetsIn++;
11767
12014
  this.ensureSession().then((sid) => {
11768
12015
  if (!sid || this.closed) return;
@@ -13380,6 +13627,13 @@ var StreamBroker = class StreamBroker {
13380
13627
  * Only populated for RTP sources (`isRtpSource()`).
13381
13628
  */
13382
13629
  rtpRing = [];
13630
+ /**
13631
+ * Where this stream's source packets are cut (`slab-pool.ts`, D868): the
13632
+ * readers copy into the pool's slabs, the ring and the restreamer's keyframe
13633
+ * cache hold/release them, and a slab no ring holds is recycled.
13634
+ */
13635
+ packetPool = new PacketSlabPool();
13636
+ packetAllocator = (src, start, end) => this.packetPool.copy(src, start, end);
13383
13637
  rtpRingCurAuStart = 0;
13384
13638
  rtpRingPrevMarker = true;
13385
13639
  rtpRingHasKeyframe = false;
@@ -13900,6 +14154,8 @@ var StreamBroker = class StreamBroker {
13900
14154
  this.annexBAssembler.reset();
13901
14155
  this.encodedCallbacks.clear();
13902
14156
  this.audioChunkPlane.dispose();
14157
+ this.resetRtpPreBuffer();
14158
+ this.packetPool.dispose();
13903
14159
  }
13904
14160
  /** Whether the broker is currently suspended (idle, no RTSP connection). */
13905
14161
  get suspended() {
@@ -14654,7 +14910,8 @@ var StreamBroker = class StreamBroker {
14654
14910
  captureRtpForPreBuffer(rtpData) {
14655
14911
  const marker = rtpData.length > 1 && (rtpData[1] & 128) !== 0;
14656
14912
  if (this.rtpRingPrevMarker || this.rtpRing.length === 0) this.rtpRingCurAuStart = this.rtpRing.length;
14657
- const owned = detachFromPool(rtpData);
14913
+ const owned = this.packetPool.adopt(rtpData);
14914
+ holdPacket(owned);
14658
14915
  this.rtpRing.push(owned);
14659
14916
  this.rtpRingBytes += backingStoreBytes(owned);
14660
14917
  if (this._lastNalParamSet || this._lastNalKeyframe) {
@@ -14750,6 +15007,7 @@ var StreamBroker = class StreamBroker {
14750
15007
  * non-decodable slice instead of waiting for the new dial's first keyframe.
14751
15008
  */
14752
15009
  resetRtpPreBuffer() {
15010
+ releasePackets(this.rtpRing);
14753
15011
  this.rtpRing = [];
14754
15012
  this.rtpRingBytes = 0;
14755
15013
  this.rtpRingCurAuStart = 0;
@@ -14846,6 +15104,7 @@ var StreamBroker = class StreamBroker {
14846
15104
  if (cut <= 0) return;
14847
15105
  const dropped = this.rtpRing.splice(0, cut);
14848
15106
  for (const b of dropped) this.rtpRingBytes -= backingStoreBytes(b);
15107
+ releasePackets(dropped);
14849
15108
  this.rtpRingKeyframeMarks = this.rtpRingKeyframeMarks.filter((mark) => mark.index >= cut).map((mark) => ({
14850
15109
  index: mark.index - cut,
14851
15110
  ts: mark.ts
@@ -15526,7 +15785,7 @@ var StreamBroker = class StreamBroker {
15526
15785
  this.audioRtpDecoder?.processPacket(rtpData);
15527
15786
  if (this.hasAudioConsumer()) this.audioCodecSession?.processPacket(rtpData);
15528
15787
  if ((this.audioCodec === "PCMU" || this.audioCodec === "PCMA") && this.encodedCallbacks.size > 0 && rtpData.length > 12) {
15529
- const payload = rtpData.subarray(12);
15788
+ const payload = Buffer.from(rtpData.subarray(12));
15530
15789
  if (payload.length > 0) {
15531
15790
  const rtpTimestamp = rtpData.readUInt32BE(4);
15532
15791
  const pts = Math.floor(rtpTimestamp / 8);
@@ -15589,7 +15848,8 @@ var StreamBroker = class StreamBroker {
15589
15848
  this.logger?.info("starting native RTSP reader");
15590
15849
  const client = new NativeRtspClient({
15591
15850
  url: source.url,
15592
- logger: this.logger?.child("rtsp-native")
15851
+ logger: this.logger?.child("rtsp-native"),
15852
+ packetAllocator: this.packetAllocator
15593
15853
  }, this.buildRtpStreamCallbacks("native RTSP", () => this.destroyNativeClient(), () => this.nativeClient !== client));
15594
15854
  this.nativeClient = client;
15595
15855
  client.connect().catch((err) => {
@@ -16096,7 +16356,8 @@ var StreamBroker = class StreamBroker {
16096
16356
  const reader = new Rfc4571Reader({
16097
16357
  url: source.url,
16098
16358
  sdp,
16099
- ...this.logger ? { logger: this.logger.child("rfc4571") } : {}
16359
+ ...this.logger ? { logger: this.logger.child("rfc4571") } : {},
16360
+ packetAllocator: this.packetAllocator
16100
16361
  }, this.buildRtpStreamCallbacks("rfc4571", () => this.destroyRfc4571Reader(), () => this.rfc4571Reader !== reader));
16101
16362
  this.rfc4571Reader = reader;
16102
16363
  reader.connect().catch((err) => {
@@ -22686,19 +22947,19 @@ var H265Repacketizer = class {
22686
22947
  return this.codecInfo;
22687
22948
  }
22688
22949
  updateVps(vps) {
22689
- this.ensureCodecInfo().vps = vps;
22950
+ this.ensureCodecInfo().vps = Buffer.from(vps);
22690
22951
  }
22691
22952
  updateSps(sps) {
22692
- this.ensureCodecInfo().sps = sps;
22953
+ this.ensureCodecInfo().sps = Buffer.from(sps);
22693
22954
  }
22694
22955
  updatePps(pps) {
22695
- this.ensureCodecInfo().pps = pps;
22956
+ this.ensureCodecInfo().pps = Buffer.from(pps);
22696
22957
  }
22697
22958
  updateSeiPrefix(sei) {
22698
- this.ensureCodecInfo().seiPrefix = sei;
22959
+ this.ensureCodecInfo().seiPrefix = Buffer.from(sei);
22699
22960
  }
22700
22961
  updateSeiSuffix(sei) {
22701
- this.ensureCodecInfo().seiSuffix = sei;
22962
+ this.ensureCodecInfo().seiSuffix = Buffer.from(sei);
22702
22963
  }
22703
22964
  shouldFilter(_nalType) {
22704
22965
  return false;
@@ -23009,6 +23270,48 @@ var H265Repacketizer = class {
23009
23270
  }
23010
23271
  }
23011
23272
  };
23273
+ //#endregion
23274
+ //#region src/stream-broker/webrtc/output-arena.ts
23275
+ /**
23276
+ * A bump allocator for the buffers the WebRTC send path builds per packet
23277
+ * (repacketizer outputs, STAP-A aggregates).
23278
+ *
23279
+ * `Buffer.from([...])` and `Buffer.concat` below 4 KiB are served from Node's
23280
+ * shared 8 KiB pool: one ~1.2 KB fragment per pool slab share, interleaved
23281
+ * with every other short-lived pooled buffer in the process. werift keeps the
23282
+ * last RTP_HISTORY_SIZE (4096) outputs of a sender for NACK, so each pinned
23283
+ * the shared slab it came from and everything beside it. Here every output is
23284
+ * a dense view of an arena that holds nothing else.
23285
+ *
23286
+ * Nothing is ever reused: an arena is left to the GC once full, and lives as
23287
+ * long as the last view of it (a NACK slot, a datagram still in flight). That
23288
+ * is why there is no hold/release protocol here, unlike `slab-pool.ts`.
23289
+ */
23290
+ /** Bytes per arena: ~50 packets of ~1.2 KB per allocation instead of one per 6. */
23291
+ var OUTPUT_ARENA_BYTES = 64 * 1024;
23292
+ /** A request larger than a quarter of an arena gets its own buffer. */
23293
+ var OUTPUT_ARENA_MAX_REQUEST = 16384;
23294
+ var OutputArena = class {
23295
+ chunk = Buffer.allocUnsafeSlow(0);
23296
+ offset = 0;
23297
+ chunks = 0;
23298
+ /** `size` writable bytes (contents unspecified: the caller fills all of them). */
23299
+ take(size) {
23300
+ if (size > OUTPUT_ARENA_MAX_REQUEST) return Buffer.allocUnsafeSlow(size);
23301
+ if (this.offset + size > this.chunk.length) {
23302
+ this.chunk = Buffer.allocUnsafeSlow(OUTPUT_ARENA_BYTES);
23303
+ this.chunks += 1;
23304
+ this.offset = 0;
23305
+ }
23306
+ const out = this.chunk.subarray(this.offset, this.offset + size);
23307
+ this.offset += size;
23308
+ return out;
23309
+ }
23310
+ /** ArrayBuffers allocated by this arena so far. */
23311
+ get allocations() {
23312
+ return this.chunks;
23313
+ }
23314
+ };
23012
23315
  var NAL_HEADER_SIZE = 1;
23013
23316
  var FU_A_HEADER_SIZE = 2;
23014
23317
  var LENGTH_FIELD_SIZE = 2;
@@ -23053,6 +23356,8 @@ var H264Repacketizer = class {
23053
23356
  pendingFuA;
23054
23357
  stapa;
23055
23358
  fuaMin;
23359
+ /** Dense home for every buffer this repacketizer builds (`output-arena.ts`). */
23360
+ arena = new OutputArena();
23056
23361
  constructor(console, maxPacketSize, codecInfo, jitterBuffer = new JitterBuffer(console, 4)) {
23057
23362
  this.console = console;
23058
23363
  this.maxPacketSize = maxPacketSize;
@@ -23070,70 +23375,78 @@ var H264Repacketizer = class {
23070
23375
  return this.codecInfo;
23071
23376
  }
23072
23377
  updateSps(sps) {
23073
- this.ensureCodecInfo().sps = sps;
23378
+ this.ensureCodecInfo().sps = Buffer.from(sps);
23074
23379
  }
23075
23380
  updatePps(pps) {
23076
- this.ensureCodecInfo().pps = pps;
23381
+ this.ensureCodecInfo().pps = Buffer.from(pps);
23077
23382
  }
23078
23383
  updateSei(sei) {
23079
- this.ensureCodecInfo().sei = sei;
23384
+ this.ensureCodecInfo().sei = Buffer.from(sei);
23080
23385
  }
23081
23386
  shouldFilter(_nalType) {
23082
23387
  return false;
23083
23388
  }
23084
23389
  packetizeFuA(data, noStart, noEnd) {
23390
+ let nalHeader;
23391
+ let bodyStart;
23085
23392
  if ((data[0] & 31) === 28) {
23086
- const fnri = data[0] & 224;
23087
- const originalNalType = data[1] & 31;
23088
23393
  const isFuStart = !!(data[1] & 128);
23089
23394
  const isFuEnd = !!(data[1] & 64);
23090
- const isFuMiddle = !isFuStart && !isFuEnd;
23091
- const originalNalHeader = Buffer.from([fnri | originalNalType]);
23092
- data = Buffer.concat([originalNalHeader, data.subarray(FU_A_HEADER_SIZE)]);
23395
+ nalHeader = data[0] & 224 | data[1] & 31;
23396
+ bodyStart = FU_A_HEADER_SIZE;
23093
23397
  if (isFuStart) noEnd = true;
23094
23398
  else if (isFuEnd) noStart = true;
23095
- else if (isFuMiddle) {
23399
+ else {
23096
23400
  noStart = true;
23097
23401
  noEnd = true;
23098
23402
  }
23403
+ } else {
23404
+ nalHeader = data[0];
23405
+ bodyStart = NAL_HEADER_SIZE;
23099
23406
  }
23100
- const fnri = data[0] & 224;
23101
- const nalType = data[0] & 31;
23102
- const fuIndicator = fnri | 28;
23103
- const fuHeaderMiddle = Buffer.from([fuIndicator, nalType]);
23104
- const fuHeaderStart = noStart ? fuHeaderMiddle : Buffer.from([fuIndicator, nalType | 128]);
23105
- const fuHeaderEnd = noEnd ? fuHeaderMiddle : Buffer.from([fuIndicator, nalType | 64]);
23106
- let fuHeader = fuHeaderStart;
23407
+ const nalType = nalHeader & 31;
23408
+ const fuIndicator = nalHeader & 224 | 28;
23409
+ const startHeader = noStart ? nalType : nalType | 128;
23410
+ const endHeader = noEnd ? nalType : nalType | 64;
23107
23411
  const packages = [];
23108
- let offset = NAL_HEADER_SIZE;
23412
+ let offset = bodyStart;
23413
+ let first = true;
23109
23414
  while (offset < data.length) {
23110
23415
  const packageSize = Math.min(this.fuaMax, data.length - offset);
23111
- const payload = data.subarray(offset, offset + packageSize);
23416
+ const last = offset + packageSize === data.length;
23417
+ const out = this.arena.take(FU_A_HEADER_SIZE + packageSize);
23418
+ out[0] = fuIndicator;
23419
+ out[1] = last ? endHeader : first ? startHeader : nalType;
23420
+ data.copy(out, FU_A_HEADER_SIZE, offset, offset + packageSize);
23421
+ packages.push(out);
23112
23422
  offset += packageSize;
23113
- if (offset === data.length) fuHeader = fuHeaderEnd;
23114
- packages.push(Buffer.concat([fuHeader, payload]));
23115
- fuHeader = fuHeaderMiddle;
23423
+ first = false;
23116
23424
  }
23117
23425
  return packages;
23118
23426
  }
23119
23427
  packetizeOneStapA(datas) {
23120
- const payload = [];
23121
23428
  if (!datas.length) throw new Error("packetizeOneStapA requires at least one NAL");
23122
- let counter = 0;
23429
+ const taken = [];
23123
23430
  let availableSize = this.maxPacketSize - STAP_A_HEADER_SIZE;
23124
23431
  const stapHeader = 24;
23125
- while (datas.length && datas[0].length + LENGTH_FIELD_SIZE <= availableSize && counter < 9) {
23432
+ while (datas.length && datas[0].length + LENGTH_FIELD_SIZE <= availableSize && taken.length < 9) {
23126
23433
  const nalu = datas.shift();
23127
23434
  availableSize -= LENGTH_FIELD_SIZE + nalu.length;
23128
- counter += 1;
23129
- const packed = Buffer.alloc(2);
23130
- packed.writeUInt16BE(nalu.length, 0);
23131
- payload.push(packed, nalu);
23435
+ taken.push(nalu);
23436
+ }
23437
+ if (taken.length === 0) return datas.shift();
23438
+ if (taken.length === 1) return taken[0];
23439
+ let total = 1;
23440
+ for (const nalu of taken) total += LENGTH_FIELD_SIZE + nalu.length;
23441
+ const out = this.arena.take(total);
23442
+ out[0] = stapHeader;
23443
+ let offset = 1;
23444
+ for (const nalu of taken) {
23445
+ out.writeUInt16BE(nalu.length, offset);
23446
+ nalu.copy(out, offset + LENGTH_FIELD_SIZE);
23447
+ offset += LENGTH_FIELD_SIZE + nalu.length;
23132
23448
  }
23133
- if (counter === 0) return datas.shift();
23134
- if (counter === 1) return payload[1];
23135
- payload.unshift(Buffer.from([stapHeader]));
23136
- return Buffer.concat(payload);
23449
+ return out;
23137
23450
  }
23138
23451
  packetizeStapA(datas) {
23139
23452
  const ret = [];
@@ -23165,12 +23478,15 @@ var H264Repacketizer = class {
23165
23478
  const originalNalType = first.payload[1] & 31;
23166
23479
  const hasFuStart = !!(first.payload[1] & 128);
23167
23480
  const hasFuEnd = !!(last.payload[1] & 64);
23168
- const fnri = first.payload[0] & 224;
23169
- const originalNalHeader = Buffer.from([fnri | originalNalType]);
23481
+ const originalNalHeader = first.payload[0] & 224 | originalNalType;
23170
23482
  const getDefragmentedPendingFua = () => {
23171
- const originalFragments = pending.map((packet) => packet.payload.subarray(FU_A_HEADER_SIZE));
23172
- originalFragments.unshift(originalNalHeader);
23173
- return Buffer.concat(originalFragments);
23483
+ let total = NAL_HEADER_SIZE;
23484
+ for (const packet of pending) total += packet.payload.length - FU_A_HEADER_SIZE;
23485
+ const out = this.arena.take(total);
23486
+ out[0] = originalNalHeader;
23487
+ let offset = NAL_HEADER_SIZE;
23488
+ for (const packet of pending) offset += packet.payload.copy(out, offset, FU_A_HEADER_SIZE);
23489
+ return out;
23174
23490
  };
23175
23491
  if (originalNalType === 7) {
23176
23492
  const splits = splitH264NaluStartCode(getDefragmentedPendingFua());
@@ -23695,6 +24011,8 @@ function selectAudioClock(mimeType) {
23695
24011
  */
23696
24012
  /** A REMB unchanged this long is no estimate (`RembWatch`). */
23697
24013
  var REMB_STALE_MS = 3e4;
24014
+ /** werift's NACK history depth (patched RTP_HISTORY_SIZE, `patches/werift+0.22.9.patch`). */
24015
+ var SOURCE_RTP_HOLD_PACKETS = 4096;
23698
24016
  var _werift;
23699
24017
  async function loadWerift() {
23700
24018
  if (_werift) return _werift;
@@ -24242,6 +24560,8 @@ var AdaptiveSession = class AdaptiveSession {
24242
24560
  * recovery). While active, live source-RTP packets are enqueued behind
24243
24561
  * the replay tail to preserve packet order. Aborted on close/rebind. */
24244
24562
  activeRtpReplay = null;
24563
+ /** The source packets werift's NACK history may still read (`slab-pool.ts`). */
24564
+ sourceRtpHold = new HeldPacketWindow(SOURCE_RTP_HOLD_PACKETS);
24245
24565
  /**
24246
24566
  * Trickle ICE: the server's locally-gathered candidates, buffered as werift
24247
24567
  * surfaces them via `onIceCandidate`. The client polls `getIceCandidates`
@@ -25120,6 +25440,7 @@ var AdaptiveSession = class AdaptiveSession {
25120
25440
  this.activeRtpReplay.abort();
25121
25441
  this.activeRtpReplay = null;
25122
25442
  }
25443
+ this.sourceRtpHold.releaseAll();
25123
25444
  await this.releaseTranscodeEgress();
25124
25445
  try {
25125
25446
  await this.source.return(void 0);
@@ -25253,6 +25574,7 @@ var AdaptiveSession = class AdaptiveSession {
25253
25574
  } });
25254
25575
  return false;
25255
25576
  }
25577
+ this.sourceRtpHold.hold(rtpData);
25256
25578
  if (this.sourceVideoSsrc === null) this.sourceVideoSsrc = srcPkt.header.ssrc;
25257
25579
  const pt = this.videoSender.codec?.payloadType ?? (codec === "H265" ? 97 : 96);
25258
25580
  let outPkts;
@@ -37858,6 +38180,16 @@ function dialAnswerFromError(err) {
37858
38180
  }
37859
38181
  //#endregion
37860
38182
  //#region src/stream-broker/broker-mem-report.ts
38183
+ function readSlabReading() {
38184
+ const totals = readSlabTotals();
38185
+ const allocsDelta = totals.allocs - lastSlabAllocs;
38186
+ lastSlabAllocs = totals.allocs;
38187
+ return {
38188
+ live: totals.live,
38189
+ free: totals.free,
38190
+ allocsDelta
38191
+ };
38192
+ }
37861
38193
  /** Linux only: elsewhere (or on a read error) the native view is simply absent. */
37862
38194
  async function readProc() {
37863
38195
  if (process.platform !== "linux") return {
@@ -37870,6 +38202,8 @@ async function readProc() {
37870
38202
  maps
37871
38203
  };
37872
38204
  }
38205
+ /** `slabAllocs` is a delta: the totals the previous line saw. */
38206
+ var lastSlabAllocs = readSlabTotals().allocs;
37873
38207
  async function emitOnce(deps) {
37874
38208
  const now = Date.now();
37875
38209
  const [proc, worker] = await Promise.all([readProc(), readDemuxWorkerHeaps(now)]);
@@ -37882,6 +38216,7 @@ async function emitOnce(deps) {
37882
38216
  proc,
37883
38217
  churn: consumerChurn.drain(),
37884
38218
  copied: readCopyOwnedTotals(),
38219
+ slabs: readSlabReading(),
37885
38220
  v8: readV8Heap(),
37886
38221
  worker,
37887
38222
  sessions: deps.sessionMem(),