@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.
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +3 -3
- package/dist/{custom-model-catalog-n9v8MUL6.js → custom-model-catalog-M-ECFeNJ.js} +2 -2
- package/dist/{custom-model-catalog-C6C2cSwd.mjs → custom-model-catalog-UaaDgBow.mjs} +2 -2
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-8AEiKOFO.mjs → dist-BT3uSeR7.mjs} +25 -5
- package/dist/{dist-DmuX7IFJ.js → dist-QBVFmPYQ.js} +25 -5
- package/dist/{model-catalogs-DO4aA1qo.js → model-catalogs-C42Mo0jo.js} +1 -1
- package/dist/{model-catalogs-BSWAHZJh.mjs → model-catalogs-ChW0i2P6.mjs} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-DqA6nD1z.js → node-B6ev-Hqa.js} +1 -1
- package/dist/{node-Bd4va7c-.mjs → node-BVRFl1hZ.mjs} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-2OQ2oxrz.js → process-memory-Cdf2A2qg.js} +1 -1
- package/dist/{process-memory-BvS2Z88w.mjs → process-memory-DdeedEgU.mjs} +1 -1
- package/dist/recorder/index.js +26 -5
- package/dist/recorder/index.mjs +26 -5
- package/dist/{segment-demux-js-By9MLn_g.mjs → segment-demux-js-8CfbEFeD.mjs} +1 -1
- package/dist/{segment-demux-js-BkGK1rhu.js → segment-demux-js-Dp0OI-2n.js} +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- 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
- 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
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-DPiGNc5u.mjs → hostInit-Bq36nWMP.mjs} +2 -2
- package/dist/stream-broker/index.js +407 -72
- package/dist/stream-broker/index.mjs +407 -72
- package/dist/stream-broker/remoteEntry.js +1 -1
- 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-
|
|
3
|
-
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, o as probeFfmpegBinary, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-
|
|
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-
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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:
|
|
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.
|
|
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
|
|
5107
|
-
*
|
|
5108
|
-
*
|
|
5109
|
-
*
|
|
5110
|
-
*
|
|
5111
|
-
*
|
|
5112
|
-
*
|
|
5113
|
-
*
|
|
5114
|
-
*
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
23091
|
-
|
|
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
|
|
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
|
|
23101
|
-
const
|
|
23102
|
-
const
|
|
23103
|
-
const
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
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
|
-
|
|
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
|
|
23169
|
-
const originalNalHeader = Buffer.from([fnri | originalNalType]);
|
|
23481
|
+
const originalNalHeader = first.payload[0] & 224 | originalNalType;
|
|
23170
23482
|
const getDefragmentedPendingFua = () => {
|
|
23171
|
-
|
|
23172
|
-
|
|
23173
|
-
|
|
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(),
|