@camstack/addon-pipeline 1.1.39 → 1.1.40
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 +168 -61
- package/dist/audio-analyzer/index.mjs +168 -61
- package/dist/detection-pipeline/index.js +2 -2
- package/dist/detection-pipeline/index.mjs +2 -2
- package/dist/{dist-DbGdZ8Nr.js → dist-BJTPkJFw.js} +247 -3
- package/dist/{dist-DvvYzO58.mjs → dist-v6cKLmU3.mjs} +247 -3
- package/dist/{frame-handle-plane-B7jMIZc2.js → frame-handle-plane-BP8YV4sF.js} +54 -5
- package/dist/{frame-handle-plane-BFzoIfkd.mjs → frame-handle-plane-DKAXTtfn.mjs} +54 -5
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +893 -12
- package/dist/pipeline-runner/index.mjs +892 -13
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/session-decode/decode-worker-child.js +511 -0
- package/dist/session-decode/decode-worker-child.mjs +510 -0
- package/dist/stream-broker/_stub.js +44 -44
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DezFy4Fu.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-6I7-nufj.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DqJvWBKS.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-dGka8yZY.mjs +26 -0
- package/dist/stream-broker/{hostInit-BOf0hg9q.mjs → hostInit-CUkFGqiI.mjs} +3 -3
- package/dist/stream-broker/index.js +268 -38
- package/dist/stream-broker/index.mjs +268 -38
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/worker-protocol-BCfO8gUF.js +67 -0
- package/dist/worker-protocol-pk7qdYXt.mjs +56 -0
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-Br2fqwe7.js → MaskShapeCanvas-DI4BY7W2-Cr_fCzY5.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DYXRf30g.js → MotionZonesSettings-NcxxQN8r-COcvoHDH.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DLpqLgPo.js → PrivacyMaskSettings-APgPLF7p-Bv-bbyJl.js} +1 -1
- package/embed-dist/assets/index-Ctbq9AXh.css +2 -0
- package/embed-dist/assets/index-jMj5PyJk.js +81 -0
- package/embed-dist/index.html +2 -2
- package/package.json +6 -3
- package/python/inference_pool.py +3 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BsrrdOBU.mjs +0 -26
- package/embed-dist/assets/index-C5UpuPr8.css +0 -2
- package/embed-dist/assets/index-CR367rYR.js +0 -81
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { $ as discriminatedUnion, B as DeviceType, D as nodePin, F as webrtcSessionCapability, G as makeSourceBrokerId, H as createEvent, I as errMsg, L as BaseAddon, N as streamBrokerCapability, Q as boolean, R as CAM_PROFILE_ORDER, V as EventCategory, W as makeProfileBrokerId, X as _enum, Z as array, at as string, c as EncodeProfileSchema, d as RingBuffer, g as cameraStreamsCapability, it as record, nt as number, ot as union, p as addonWidgetsSourceCapability, q as parseProfileBrokerId, rt as object, tt as literal, w as maskUrlCredentials, z as DeviceFeature } from "../dist-
|
|
3
|
-
import { n as DecoderSessionProxy, t as FrameHandlePlane } from "../frame-handle-plane-
|
|
2
|
+
import { $ as discriminatedUnion, B as DeviceType, D as nodePin, F as webrtcSessionCapability, G as makeSourceBrokerId, H as createEvent, I as errMsg, L as BaseAddon, N as streamBrokerCapability, Q as boolean, R as CAM_PROFILE_ORDER, V as EventCategory, W as makeProfileBrokerId, X as _enum, Z as array, at as string, c as EncodeProfileSchema, d as RingBuffer, g as cameraStreamsCapability, it as record, nt as number, ot as union, p as addonWidgetsSourceCapability, q as parseProfileBrokerId, rt as object, tt as literal, w as maskUrlCredentials, z as DeviceFeature } from "../dist-v6cKLmU3.mjs";
|
|
3
|
+
import { n as DecoderSessionProxy, t as FrameHandlePlane } from "../frame-handle-plane-DKAXTtfn.mjs";
|
|
4
4
|
import { t as createFileDataPlaneHandler } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import * as crypto$1 from "node:crypto";
|
|
7
7
|
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
8
10
|
import * as fs from "node:fs";
|
|
9
11
|
import * as path$1 from "node:path";
|
|
10
12
|
import path, { join } from "node:path";
|
|
11
13
|
import * as os from "node:os";
|
|
12
14
|
import { networkInterfaces, tmpdir } from "node:os";
|
|
13
|
-
import { spawn } from "node:child_process";
|
|
14
15
|
import { lookup } from "node:dns/promises";
|
|
15
|
-
import { pathToFileURL } from "node:url";
|
|
16
16
|
import * as net$1 from "node:net";
|
|
17
17
|
import net from "node:net";
|
|
18
18
|
import { Socket } from "net";
|
|
@@ -4748,6 +4748,14 @@ var RtspSession = class {
|
|
|
4748
4748
|
userAgent = null;
|
|
4749
4749
|
/** When true, this session receives video-only (no audio RTP). */
|
|
4750
4750
|
muted;
|
|
4751
|
+
/**
|
|
4752
|
+
* Latch for a `muted` (local-decoder loopback) session that has started
|
|
4753
|
+
* dropping video packets on socket backlog. While set, every non-keyframe
|
|
4754
|
+
* packet is dropped — even after the backlog clears — until the next keyframe
|
|
4755
|
+
* access unit, so the decoder always resumes on a clean GOP boundary instead
|
|
4756
|
+
* of mid-GOP with reference-broken (missing P-frame) packets.
|
|
4757
|
+
*/
|
|
4758
|
+
droppingUntilKeyframe = false;
|
|
4751
4759
|
constructor(socket, sdp, onClose, muted) {
|
|
4752
4760
|
this.socket = socket;
|
|
4753
4761
|
this.sdp = sdp;
|
|
@@ -4797,13 +4805,35 @@ var RtspSession = class {
|
|
|
4797
4805
|
bytesSent: this.bytesSent
|
|
4798
4806
|
};
|
|
4799
4807
|
}
|
|
4800
|
-
/**
|
|
4801
|
-
|
|
4808
|
+
/**
|
|
4809
|
+
* Send a video RTP packet via TCP interleaved framing.
|
|
4810
|
+
*
|
|
4811
|
+
* `prime` marks a packet from the current-GOP catch-up burst replayed on
|
|
4812
|
+
* join — written unconditionally (bounded one-shot the local decoder drains
|
|
4813
|
+
* fast; dropping mid-burst would break frame references). A prime burst
|
|
4814
|
+
* starts at a keyframe by construction, so it also clears the
|
|
4815
|
+
* `droppingUntilKeyframe` latch.
|
|
4816
|
+
*
|
|
4817
|
+
* `isKeyframe` marks a packet that begins a keyframe access unit — the clean
|
|
4818
|
+
* GOP boundary at which a latched `muted` session resumes sending.
|
|
4819
|
+
*/
|
|
4820
|
+
sendRtp(packet, prime = false, isKeyframe = false) {
|
|
4802
4821
|
if (!this.playing || this.closed) return;
|
|
4803
4822
|
if (this.tracks.length === 0) return;
|
|
4804
|
-
if (this.
|
|
4805
|
-
|
|
4806
|
-
|
|
4823
|
+
if (prime) this.droppingUntilKeyframe = false;
|
|
4824
|
+
else {
|
|
4825
|
+
if (this.droppingUntilKeyframe) {
|
|
4826
|
+
if (!isKeyframe) return;
|
|
4827
|
+
this.droppingUntilKeyframe = false;
|
|
4828
|
+
}
|
|
4829
|
+
if (this.socket.writableLength > 2097152) {
|
|
4830
|
+
if (this.muted) {
|
|
4831
|
+
this.droppingUntilKeyframe = true;
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
this.close();
|
|
4835
|
+
return;
|
|
4836
|
+
}
|
|
4807
4837
|
}
|
|
4808
4838
|
const channel = this.tracks[0].rtpChannel;
|
|
4809
4839
|
const frame = Buffer.allocUnsafe(4 + packet.data.length);
|
|
@@ -5528,6 +5558,16 @@ function appendAudioMediaBlock(videoSdp, audioBlock) {
|
|
|
5528
5558
|
//#endregion
|
|
5529
5559
|
//#region src/stream-broker/rtsp/rtsp-restreamer.ts
|
|
5530
5560
|
/**
|
|
5561
|
+
* Max age (ms) of a prime burst a MUTED (local-decoder loopback) session will
|
|
5562
|
+
* accept. A muted decoder must anchor at the LIVE edge — a full-GOP catch-up
|
|
5563
|
+
* burst (2-3s old) makes libav base its pts 2-3s in the past and, with ~zero
|
|
5564
|
+
* decode headroom, it never drains that backlog. So a muted session only joins
|
|
5565
|
+
* off a burst already near live (span ≤ this); otherwise it is left pending to
|
|
5566
|
+
* begin at the next live keyframe. Non-muted sessions (viewer / recorder) still
|
|
5567
|
+
* get the full-GOP burst for an instant picture.
|
|
5568
|
+
*/
|
|
5569
|
+
var LIVE_EDGE_MAX_SPAN_MS = 500;
|
|
5570
|
+
/**
|
|
5531
5571
|
* Per-broker RTSP restreamer.
|
|
5532
5572
|
*
|
|
5533
5573
|
* Operates in two modes:
|
|
@@ -5573,9 +5613,22 @@ var RtspRestreamer = class {
|
|
|
5573
5613
|
collectingKeyframeRtp = false;
|
|
5574
5614
|
logger = null;
|
|
5575
5615
|
_onSessionCountChanged = null;
|
|
5616
|
+
/**
|
|
5617
|
+
* Supplies the broker's current-GOP source-RTP snapshot (keyframe..now) so a
|
|
5618
|
+
* joining decoder is primed with the WHOLE recent GOP as a burst and
|
|
5619
|
+
* fast-forwards to the live edge — instead of a lone keyframe up to a GOP old
|
|
5620
|
+
* followed by reference-broken live packets. Mirrors Scrypted's
|
|
5621
|
+
* prebuffer/findSyncFrame. Returns [] until a keyframe is seen (falls back to
|
|
5622
|
+
* the single cached keyframe AU).
|
|
5623
|
+
*/
|
|
5624
|
+
rtpPreBufferSupplier = null;
|
|
5576
5625
|
constructor(streamName) {
|
|
5577
5626
|
this.streamName = streamName;
|
|
5578
5627
|
}
|
|
5628
|
+
/** Wire the broker's current-GOP RTP snapshot supplier (see field doc). */
|
|
5629
|
+
setRtpPreBufferSupplier(supplier) {
|
|
5630
|
+
this.rtpPreBufferSupplier = supplier;
|
|
5631
|
+
}
|
|
5579
5632
|
/** True when using RTP passthrough (native client) instead of Annex-B re-packetization. */
|
|
5580
5633
|
get rtpPassthroughMode() {
|
|
5581
5634
|
return this._rtpPassthroughMode;
|
|
@@ -5661,9 +5714,8 @@ var RtspRestreamer = class {
|
|
|
5661
5714
|
total: this.sessions.size
|
|
5662
5715
|
} });
|
|
5663
5716
|
this._onSessionCountChanged?.();
|
|
5664
|
-
if (this._rtpPassthroughMode)
|
|
5665
|
-
|
|
5666
|
-
} else if (this.lastKeyframeNals.length > 0 && this.packetizer) setTimeout(() => this.servePendingSessions(), 0);
|
|
5717
|
+
if (this._rtpPassthroughMode) setTimeout(() => this.servePendingSessionsRtp(), 0);
|
|
5718
|
+
else if (this.lastKeyframeNals.length > 0 && this.packetizer) setTimeout(() => this.servePendingSessions(), 0);
|
|
5667
5719
|
}
|
|
5668
5720
|
/**
|
|
5669
5721
|
* Set the camera's SDP directly (from DESCRIBE response).
|
|
@@ -5695,6 +5747,7 @@ var RtspRestreamer = class {
|
|
|
5695
5747
|
data: rtpData,
|
|
5696
5748
|
marker: (rtpData[1] & 128) !== 0
|
|
5697
5749
|
};
|
|
5750
|
+
const keyframeAuStart = (isKeyframe || isParameterSet) && !this.collectingKeyframeRtp;
|
|
5698
5751
|
if (isKeyframe || isParameterSet) {
|
|
5699
5752
|
if (!this.collectingKeyframeRtp) {
|
|
5700
5753
|
this.collectingKeyframeRtp = true;
|
|
@@ -5710,23 +5763,49 @@ var RtspRestreamer = class {
|
|
|
5710
5763
|
}
|
|
5711
5764
|
if (this.sessions.size === 0) return;
|
|
5712
5765
|
this.servePendingSessionsRtp();
|
|
5766
|
+
if (keyframeAuStart) for (const session of this.sessions.values()) {
|
|
5767
|
+
if (!session.isMuted() || !session.isPlaying()) continue;
|
|
5768
|
+
this.pendingKeyframe.delete(session.getSessionId());
|
|
5769
|
+
}
|
|
5713
5770
|
for (const session of this.sessions.values()) {
|
|
5714
5771
|
if (this.pendingKeyframe.has(session.getSessionId())) continue;
|
|
5715
|
-
if (session.isPlaying()) session.sendRtp(rtpPacket);
|
|
5772
|
+
if (session.isPlaying()) session.sendRtp(rtpPacket, false, isKeyframe || isParameterSet);
|
|
5716
5773
|
}
|
|
5717
5774
|
}
|
|
5718
5775
|
/** Send cached keyframe RTP packets to sessions that transitioned to PLAY. */
|
|
5719
5776
|
servePendingSessionsRtp() {
|
|
5720
5777
|
if (this.pendingKeyframe.size === 0) return;
|
|
5721
|
-
|
|
5778
|
+
const ringPackets = this.rtpPreBufferSupplier?.() ?? [];
|
|
5779
|
+
const fromRing = ringPackets.length > 0;
|
|
5780
|
+
const primePackets = fromRing ? ringPackets.map((data) => ({
|
|
5781
|
+
data,
|
|
5782
|
+
marker: (data[1] & 128) !== 0
|
|
5783
|
+
})) : this.lastKeyframeRtpPackets;
|
|
5784
|
+
if (primePackets.length === 0) return;
|
|
5785
|
+
const firstTs = primePackets[0].data.readUInt32BE(4);
|
|
5786
|
+
const spanMs = (primePackets[primePackets.length - 1].data.readUInt32BE(4) - firstTs | 0) / 90;
|
|
5722
5787
|
const ready = [];
|
|
5723
5788
|
for (const sessionId of this.pendingKeyframe) if (this.sessions.get(sessionId)?.isPlaying()) ready.push(sessionId);
|
|
5724
5789
|
if (ready.length === 0) return;
|
|
5725
|
-
|
|
5790
|
+
const served = [];
|
|
5791
|
+
for (const sessionId of ready) {
|
|
5792
|
+
const session = this.sessions.get(sessionId);
|
|
5793
|
+
if (!session) continue;
|
|
5794
|
+
if (session.isMuted() && (!fromRing || spanMs > LIVE_EDGE_MAX_SPAN_MS)) continue;
|
|
5795
|
+
served.push(sessionId);
|
|
5796
|
+
}
|
|
5797
|
+
if (served.length === 0) return;
|
|
5798
|
+
this.logger?.info("restream prime (lat-probe)", { meta: {
|
|
5799
|
+
source: fromRing ? "gop-burst" : "keyframe-only",
|
|
5800
|
+
packets: primePackets.length,
|
|
5801
|
+
spanMs: Math.round(spanMs),
|
|
5802
|
+
clients: served.length
|
|
5803
|
+
} });
|
|
5804
|
+
for (const rtpPacket of primePackets) for (const sessionId of served) {
|
|
5726
5805
|
const session = this.sessions.get(sessionId);
|
|
5727
|
-
if (session?.isPlaying()) session.sendRtp(rtpPacket);
|
|
5806
|
+
if (session?.isPlaying()) session.sendRtp(rtpPacket, true);
|
|
5728
5807
|
}
|
|
5729
|
-
for (const id of
|
|
5808
|
+
for (const id of served) this.pendingKeyframe.delete(id);
|
|
5730
5809
|
}
|
|
5731
5810
|
/**
|
|
5732
5811
|
* Push a raw audio RTP packet from the native RTSP client.
|
|
@@ -8636,6 +8715,38 @@ var AudioCodecSession = class {
|
|
|
8636
8715
|
sessionId: this.sessionId
|
|
8637
8716
|
};
|
|
8638
8717
|
}
|
|
8718
|
+
/**
|
|
8719
|
+
* Tear down the upstream decode session + 40ms poll loop WITHOUT
|
|
8720
|
+
* permanently closing this handle. The broker calls this from its
|
|
8721
|
+
* audio-consumer demand-gate: when audio consumers drop to zero the
|
|
8722
|
+
* upstream decoder session and its poll loop are pure waste, so we
|
|
8723
|
+
* release them. Unlike {@link close} this leaves the handle reusable —
|
|
8724
|
+
* the next pushed packet re-creates the session lazily via
|
|
8725
|
+
* `ensureSession()`. Safe to call repeatedly.
|
|
8726
|
+
*/
|
|
8727
|
+
async idle() {
|
|
8728
|
+
if (this.closed) return;
|
|
8729
|
+
if (this.creating) await this.creating;
|
|
8730
|
+
if (this.closed) return;
|
|
8731
|
+
if (this.pollTimer) {
|
|
8732
|
+
clearInterval(this.pollTimer);
|
|
8733
|
+
this.pollTimer = null;
|
|
8734
|
+
}
|
|
8735
|
+
if (!this.sessionId) return;
|
|
8736
|
+
const sid = this.sessionId;
|
|
8737
|
+
const nodeId = this.sessionNodeId ?? void 0;
|
|
8738
|
+
this.sessionId = null;
|
|
8739
|
+
this.sessionNodeId = null;
|
|
8740
|
+
try {
|
|
8741
|
+
await this.api.closeSession({
|
|
8742
|
+
sessionId: sid,
|
|
8743
|
+
...nodeId ? { nodeId } : {}
|
|
8744
|
+
});
|
|
8745
|
+
} catch (err) {
|
|
8746
|
+
this.logger?.warn("audio-codec: idle closeSession failed", { meta: { error: errMsg(err) } });
|
|
8747
|
+
}
|
|
8748
|
+
this.logger?.info("audio-codec: decode session idled (no audio consumer)", { meta: { sessionId: sid } });
|
|
8749
|
+
}
|
|
8639
8750
|
async close() {
|
|
8640
8751
|
if (this.closed) return;
|
|
8641
8752
|
this.closed = true;
|
|
@@ -8863,12 +8974,25 @@ var EncodedRingBuffer = class {
|
|
|
8863
8974
|
clear() {
|
|
8864
8975
|
this.packets.length = 0;
|
|
8865
8976
|
}
|
|
8866
|
-
/**
|
|
8977
|
+
/**
|
|
8978
|
+
* Remove packets older than maxDurationMs based on PTS.
|
|
8979
|
+
*
|
|
8980
|
+
* PTS here is the source RTP timestamp (90kHz → ms), which is NOT
|
|
8981
|
+
* monotonic across the buffer's lifetime: an RTSP/rfc4571 redial re-bases
|
|
8982
|
+
* it to a fresh random value (RFC 3550) and the 32-bit field wraps every
|
|
8983
|
+
* ~13.25h. A purely `pts < cutoff` head-trim stalls forever whenever the
|
|
8984
|
+
* newest packet's pts drops below the stale head's — the buffer then
|
|
8985
|
+
* retains the entire pre-discontinuity history (an unbounded native-memory
|
|
8986
|
+
* leak of full-bitrate AnnexB copies). So we also evict any head packet
|
|
8987
|
+
* whose pts is GREATER than the newest packet's: it can only belong to a
|
|
8988
|
+
* prior, higher-based epoch that the source has since jumped away from.
|
|
8989
|
+
*/
|
|
8867
8990
|
evict() {
|
|
8868
8991
|
if (this.packets.length === 0) return;
|
|
8869
|
-
const
|
|
8992
|
+
const newest = this.packets[this.packets.length - 1].pts;
|
|
8993
|
+
const cutoff = newest - this.maxDurationMs;
|
|
8870
8994
|
let removeCount = 0;
|
|
8871
|
-
while (removeCount < this.packets.length && this.packets[removeCount].pts < cutoff) removeCount++;
|
|
8995
|
+
while (removeCount < this.packets.length && (this.packets[removeCount].pts < cutoff || this.packets[removeCount].pts > newest)) removeCount++;
|
|
8872
8996
|
if (removeCount > 0) this.packets.splice(0, removeCount);
|
|
8873
8997
|
}
|
|
8874
8998
|
};
|
|
@@ -9148,6 +9272,14 @@ var INITIAL_RECONNECT_DELAY_MS = 1e3;
|
|
|
9148
9272
|
var MAX_RECONNECT_DELAY_MS = 3e4;
|
|
9149
9273
|
/** Grace period before suspending after last consumer leaves. */
|
|
9150
9274
|
var SUSPEND_GRACE_MS = 5e3;
|
|
9275
|
+
/**
|
|
9276
|
+
* Grace window before an idle audio-codec decode session is torn down
|
|
9277
|
+
* after the last audio consumer detaches. Mirrors the frame-handle /
|
|
9278
|
+
* suspend occupancy-linger pattern: a brief gap between consumers (e.g.
|
|
9279
|
+
* a WebRTC renegotiation or an analyzer re-subscribe) must not thrash
|
|
9280
|
+
* the upstream decode session create/destroy.
|
|
9281
|
+
*/
|
|
9282
|
+
var AUDIO_CONSUMER_LINGER_MS = 5e3;
|
|
9151
9283
|
/** Timeout for push-mode sources: if no packet arrives within this window, emit error placeholder. */
|
|
9152
9284
|
var PUSH_STALL_TIMEOUT_MS = 15e3;
|
|
9153
9285
|
var StreamBroker = class StreamBroker {
|
|
@@ -9223,6 +9355,12 @@ var StreamBroker = class StreamBroker {
|
|
|
9223
9355
|
pushRtpFlagDepacketizer = null;
|
|
9224
9356
|
audioRtpDecoder = null;
|
|
9225
9357
|
audioCodecSession = null;
|
|
9358
|
+
/**
|
|
9359
|
+
* Pending occupancy-linger teardown of {@link audioCodecSession} after
|
|
9360
|
+
* the last audio consumer detached. Cancelled when a consumer returns
|
|
9361
|
+
* within {@link AUDIO_CONSUMER_LINGER_MS}. See {@link reconcileAudioConsumer}.
|
|
9362
|
+
*/
|
|
9363
|
+
audioConsumerLingerTimer;
|
|
9226
9364
|
audioRtpSeen = 0;
|
|
9227
9365
|
/** Detected audio codec from RTSP DESCRIBE (e.g. 'PCMU', 'PCMA'). */
|
|
9228
9366
|
audioCodec = null;
|
|
@@ -9436,6 +9574,7 @@ var StreamBroker = class StreamBroker {
|
|
|
9436
9574
|
this.pipeServer = new StreamPipeServer(logger?.child("pipe-server"));
|
|
9437
9575
|
this.rtspRestreamer = new RtspRestreamer(deviceId);
|
|
9438
9576
|
if (logger) this.rtspRestreamer.setLogger(logger.child("rtsp"));
|
|
9577
|
+
this.rtspRestreamer.setRtpPreBufferSupplier(() => this.getRtpPreBuffer());
|
|
9439
9578
|
this.preBuffer = new EncodedRingBuffer(StreamBroker.DEFAULT_PRE_BUFFER_SEC);
|
|
9440
9579
|
this.decoderSeedBuffer = new EncodedRingBuffer(StreamBroker.DECODER_SEED_BUFFER_SEC);
|
|
9441
9580
|
this.audioChunkPlane = new AudioChunkPlane(logger?.child("audio-chunk-plane"));
|
|
@@ -9684,11 +9823,54 @@ var StreamBroker = class StreamBroker {
|
|
|
9684
9823
|
return false;
|
|
9685
9824
|
}
|
|
9686
9825
|
/**
|
|
9826
|
+
* True when at least one consumer needs DECODED audio. The audio-specific
|
|
9827
|
+
* subset of {@link hasDemand} — deliberately excludes the video
|
|
9828
|
+
* frame-handle plane so a video-only viewer never spins up the
|
|
9829
|
+
* AAC/Opus decode chain. `encodedCallbacks` covers WebRTC (its G.711
|
|
9830
|
+
* egress needs decoded PCM); `audioChunkPlane` covers the analyzer.
|
|
9831
|
+
*/
|
|
9832
|
+
hasAudioConsumer() {
|
|
9833
|
+
return this.encodedCallbacks.size > 0 || this.audioChunkPlane.subscriberCount > 0;
|
|
9834
|
+
}
|
|
9835
|
+
/**
|
|
9836
|
+
* Demand-gate the audio-codec decode session lifecycle. Called from
|
|
9837
|
+
* every consumer add/remove edge (via {@link checkDemand}). While an
|
|
9838
|
+
* audio consumer exists, cancel any pending linger-close. When
|
|
9839
|
+
* consumers drop to zero, arm a short linger timer that tears the
|
|
9840
|
+
* upstream decode session + 40ms poll loop down (via
|
|
9841
|
+
* `AudioCodecSession.idle`) so a momentary gap between consumers
|
|
9842
|
+
* doesn't thrash create/destroy. The next gated packet re-arms the
|
|
9843
|
+
* session lazily through `ensureSession()`. No-op when no session is
|
|
9844
|
+
* armed (native PCMU/PCMA cameras never build one).
|
|
9845
|
+
*/
|
|
9846
|
+
reconcileAudioConsumer() {
|
|
9847
|
+
if (!this.audioCodecSession) return;
|
|
9848
|
+
if (this.hasAudioConsumer()) {
|
|
9849
|
+
if (this.audioConsumerLingerTimer) {
|
|
9850
|
+
clearTimeout(this.audioConsumerLingerTimer);
|
|
9851
|
+
this.audioConsumerLingerTimer = void 0;
|
|
9852
|
+
}
|
|
9853
|
+
return;
|
|
9854
|
+
}
|
|
9855
|
+
if (this.audioConsumerLingerTimer) return;
|
|
9856
|
+
this.audioConsumerLingerTimer = setTimeout(() => {
|
|
9857
|
+
this.audioConsumerLingerTimer = void 0;
|
|
9858
|
+
if (this.hasAudioConsumer()) return;
|
|
9859
|
+
const session = this.audioCodecSession;
|
|
9860
|
+
if (!session) return;
|
|
9861
|
+
session.idle().catch((err) => {
|
|
9862
|
+
this.logger?.warn("audio-codec: idle teardown failed", { meta: { error: errMsg(err) } });
|
|
9863
|
+
});
|
|
9864
|
+
}, AUDIO_CONSUMER_LINGER_MS);
|
|
9865
|
+
if (typeof this.audioConsumerLingerTimer.unref === "function") this.audioConsumerLingerTimer.unref();
|
|
9866
|
+
}
|
|
9867
|
+
/**
|
|
9687
9868
|
* Check demand and schedule suspend/resume. Called after every subscriber
|
|
9688
9869
|
* add/remove and after pre-buffer enable/disable.
|
|
9689
9870
|
*/
|
|
9690
9871
|
checkDemand() {
|
|
9691
9872
|
if (this.manualStop || this.stopping) return;
|
|
9873
|
+
this.reconcileAudioConsumer();
|
|
9692
9874
|
if (this.hasDemand()) {
|
|
9693
9875
|
if (!this.demandActive) {
|
|
9694
9876
|
this.demandActive = true;
|
|
@@ -9834,7 +10016,7 @@ var StreamBroker = class StreamBroker {
|
|
|
9834
10016
|
if (packet.type === "audio" && this.source?.type === "push" && this.audioCodecSession) {
|
|
9835
10017
|
const codec = packet.codec.toLowerCase();
|
|
9836
10018
|
if (codec !== "pcmu" && codec !== "pcma") {
|
|
9837
|
-
this.audioCodecSession.pushRawFrame(packet.data);
|
|
10019
|
+
if (this.hasAudioConsumer()) this.audioCodecSession.pushRawFrame(packet.data);
|
|
9838
10020
|
return;
|
|
9839
10021
|
}
|
|
9840
10022
|
}
|
|
@@ -10275,7 +10457,8 @@ var StreamBroker = class StreamBroker {
|
|
|
10275
10457
|
logger: this.logger?.child("frame-handle-plane"),
|
|
10276
10458
|
resolveStreamInfo: () => this.resolveFrameHandleStreamInfo(),
|
|
10277
10459
|
localNodeId: this.localNodeId,
|
|
10278
|
-
getSeedPackets: () => this.decoderSeedBuffer.getPackets()
|
|
10460
|
+
getSeedPackets: () => this.decoderSeedBuffer.getPackets(),
|
|
10461
|
+
isDebugEnabled: () => this.streamingDebug
|
|
10279
10462
|
});
|
|
10280
10463
|
return this.frameHandlePlane;
|
|
10281
10464
|
}
|
|
@@ -10357,6 +10540,17 @@ var StreamBroker = class StreamBroker {
|
|
|
10357
10540
|
subscribedAt: entry.subscribedAt,
|
|
10358
10541
|
packetsDelivered: entry.packetsDelivered
|
|
10359
10542
|
});
|
|
10543
|
+
if (this._preBufferEnabled && encoded.length === 0) encoded.push({
|
|
10544
|
+
id: "prebuffer-hold",
|
|
10545
|
+
channel: "annexb",
|
|
10546
|
+
attribution: {
|
|
10547
|
+
kind: "warmup",
|
|
10548
|
+
userAgent: "prebuffer",
|
|
10549
|
+
media: "video"
|
|
10550
|
+
},
|
|
10551
|
+
subscribedAt: this.startedAt,
|
|
10552
|
+
packetsDelivered: 0
|
|
10553
|
+
});
|
|
10360
10554
|
return {
|
|
10361
10555
|
rtsp: this.rtspRestreamer.listSessionInfos(),
|
|
10362
10556
|
decoded: (this.frameHandlePlane?.listSubscribers() ?? []).map((s) => ({
|
|
@@ -10722,7 +10916,7 @@ var StreamBroker = class StreamBroker {
|
|
|
10722
10916
|
hasCodecSession: this.audioCodecSession !== null
|
|
10723
10917
|
} });
|
|
10724
10918
|
this.audioRtpDecoder?.processPacket(rtpData);
|
|
10725
|
-
this.audioCodecSession?.processPacket(rtpData);
|
|
10919
|
+
if (this.hasAudioConsumer()) this.audioCodecSession?.processPacket(rtpData);
|
|
10726
10920
|
if ((this.audioCodec === "PCMU" || this.audioCodec === "PCMA") && this.encodedCallbacks.size > 0 && rtpData.length > 12) {
|
|
10727
10921
|
const payload = rtpData.subarray(12);
|
|
10728
10922
|
if (payload.length > 0) {
|
|
@@ -11203,6 +11397,10 @@ var StreamBroker = class StreamBroker {
|
|
|
11203
11397
|
this.audioRtpDecoder.flush();
|
|
11204
11398
|
this.audioRtpDecoder = null;
|
|
11205
11399
|
}
|
|
11400
|
+
if (this.audioConsumerLingerTimer) {
|
|
11401
|
+
clearTimeout(this.audioConsumerLingerTimer);
|
|
11402
|
+
this.audioConsumerLingerTimer = void 0;
|
|
11403
|
+
}
|
|
11206
11404
|
if (this.audioCodecSession) {
|
|
11207
11405
|
const session = this.audioCodecSession;
|
|
11208
11406
|
this.audioCodecSession = null;
|
|
@@ -14253,6 +14451,11 @@ var StreamBrokerManager = class {
|
|
|
14253
14451
|
break;
|
|
14254
14452
|
}
|
|
14255
14453
|
this.assignments.set(deviceId, nextAssignment);
|
|
14454
|
+
if (current.map.high !== newMap.high) for (const camStreamId of [current.map.high, newMap.high]) {
|
|
14455
|
+
if (!camStreamId) continue;
|
|
14456
|
+
const broker = this.brokers.get(brokerIdFor(deviceId, camStreamId));
|
|
14457
|
+
if (broker) this.applyPreBufferConfig(broker, deviceId, camStreamId);
|
|
14458
|
+
}
|
|
14256
14459
|
for (const [camId, prevCount] of oldPushConsumers) if ((newPushConsumers.get(camId) ?? 0) === 0 && prevCount > 0) this.emitCamStreamIdle(deviceId, camId);
|
|
14257
14460
|
for (const [camId, nextCount] of newPushConsumers) if ((oldPushConsumers.get(camId) ?? 0) === 0 && nextCount > 0) {
|
|
14258
14461
|
let triggerProfile = "mid";
|
|
@@ -14458,7 +14661,7 @@ var StreamBrokerManager = class {
|
|
|
14458
14661
|
broker.setRestreamers(this.restreamers);
|
|
14459
14662
|
broker.setRestreamUrlResolver(() => this.rtspProvider.getEntry(brokerId)?.mutedUrl ?? null);
|
|
14460
14663
|
broker.setFfmpegBinaryPath(this.ffmpegConfig.binaryPath);
|
|
14461
|
-
this.applyPreBufferConfig(broker, deviceId, camStreamId
|
|
14664
|
+
this.applyPreBufferConfig(broker, deviceId, camStreamId);
|
|
14462
14665
|
if (this.deviceOverrides.get(deviceId)?.streamingDebug) broker.setStreamingDebug(true);
|
|
14463
14666
|
broker.setSourceProvider(() => {
|
|
14464
14667
|
const latest = this.cameraStreams.get(deviceId)?.get(camStreamId);
|
|
@@ -14594,34 +14797,48 @@ var StreamBrokerManager = class {
|
|
|
14594
14797
|
};
|
|
14595
14798
|
}
|
|
14596
14799
|
/**
|
|
14800
|
+
* Whether a stream is warmed BY DEFAULT (no explicit override): the device's
|
|
14801
|
+
* `high` profile, on a non-battery camera. Single source of truth used by both
|
|
14802
|
+
* the broker-config default (`applyPreBufferConfig`) and the settings-patch
|
|
14803
|
+
* override synthesis (`defaultStreamPreBuffer`) so a seconds-only patch never
|
|
14804
|
+
* silently disables the high-profile warm default.
|
|
14805
|
+
*/
|
|
14806
|
+
isDefaultWarm(deviceId, camStreamId) {
|
|
14807
|
+
return findProfileForStream(this.assignments.get(deviceId)?.map ?? {}, camStreamId) === "high" && !this.deviceHasFeature(deviceId, DeviceFeature.BatteryOperated);
|
|
14808
|
+
}
|
|
14809
|
+
/**
|
|
14597
14810
|
* Default `StreamPreBuffer` for a brand-new override entry — mirrors
|
|
14598
|
-
* the
|
|
14599
|
-
*
|
|
14600
|
-
*
|
|
14601
|
-
*
|
|
14602
|
-
*
|
|
14811
|
+
* the effective broker default: `enabled` reflects the high-profile warm
|
|
14812
|
+
* default (`isDefaultWarm`), so a seconds-only patch on a warm high stream
|
|
14813
|
+
* inherits `enabled: true` instead of silently forcing it OFF. Operators
|
|
14814
|
+
* still override explicitly per-stream via the device-settings sub-tabs;
|
|
14815
|
+
* detection / recording paths pass `forceForDetection` to
|
|
14816
|
+
* `getEffectivePreBufferSec` when they need a buffered window irrespective
|
|
14817
|
+
* of the operator toggle.
|
|
14603
14818
|
*/
|
|
14604
|
-
defaultStreamPreBuffer(
|
|
14819
|
+
defaultStreamPreBuffer(deviceId, camStreamId) {
|
|
14605
14820
|
return {
|
|
14606
|
-
enabled:
|
|
14821
|
+
enabled: this.isDefaultWarm(deviceId, camStreamId),
|
|
14607
14822
|
seconds: this.defaultPreBufferSec
|
|
14608
14823
|
};
|
|
14609
14824
|
}
|
|
14610
14825
|
/**
|
|
14611
14826
|
* Apply the effective pre-buffer config to a fresh broker instance.
|
|
14612
|
-
* Default behaviour
|
|
14613
|
-
*
|
|
14614
|
-
* recording subscriber attaches.
|
|
14827
|
+
* Default behaviour: the HIGH profile stream is warm (see below);
|
|
14828
|
+
* every other stream stays OFF — idle until the operator opts in or
|
|
14829
|
+
* a detection / recording subscriber attaches. An explicit per-stream
|
|
14830
|
+
* operator override always wins over both defaults.
|
|
14615
14831
|
*/
|
|
14616
|
-
applyPreBufferConfig(broker, deviceId, camStreamId
|
|
14832
|
+
applyPreBufferConfig(broker, deviceId, camStreamId) {
|
|
14617
14833
|
const perStream = this.deviceOverrides.get(deviceId)?.preBuffer?.[camStreamId];
|
|
14618
14834
|
if (perStream) {
|
|
14619
14835
|
broker.setPreBufferEnabled(perStream.enabled);
|
|
14620
14836
|
broker.setPreBufferDuration(perStream.enabled ? perStream.seconds : 0);
|
|
14621
14837
|
return;
|
|
14622
14838
|
}
|
|
14623
|
-
|
|
14624
|
-
broker.
|
|
14839
|
+
const defaultWarm = this.isDefaultWarm(deviceId, camStreamId);
|
|
14840
|
+
broker.setPreBufferEnabled(defaultWarm);
|
|
14841
|
+
broker.setPreBufferDuration(defaultWarm ? this.defaultPreBufferSec : 0);
|
|
14625
14842
|
}
|
|
14626
14843
|
async fanOutRegistration(deviceId, brokerId, cam, broker) {
|
|
14627
14844
|
const log = this.logger.withTags({
|
|
@@ -17406,6 +17623,15 @@ var BROKER_METRICS_HEARTBEAT_MS = 3e4;
|
|
|
17406
17623
|
/** Throttle for persisting the per-stream probe map — bounds writes regardless
|
|
17407
17624
|
* of streaming churn while keeping the durable copy recent enough for restart. */
|
|
17408
17625
|
var PROBE_SNAPSHOTS_PERSIST_INTERVAL_MS = 6e4;
|
|
17626
|
+
/**
|
|
17627
|
+
* Defensive timeout for the per-session `networkQuality.getDeviceStats` poll.
|
|
17628
|
+
* The stat is an OPTIONAL input to the adaptive downgrade ladder — never worth
|
|
17629
|
+
* blocking the loop for. When the hub-resident provider is transiently
|
|
17630
|
+
* undiscoverable the child's unowned-cap routing falls to the Moleculer
|
|
17631
|
+
* broker-fallback, whose discovery wait is ~30s; without this bound a single
|
|
17632
|
+
* poll would stall the adaptive loop for that long. On timeout we simply treat
|
|
17633
|
+
* the sample as absent (the RTCP Receiver-Report path drives the ladder anyway). */
|
|
17634
|
+
var NETWORK_QUALITY_STATS_TIMEOUT_MS = 2e3;
|
|
17409
17635
|
var StreamBrokerAddon = class extends BaseAddon {
|
|
17410
17636
|
brokerManager = null;
|
|
17411
17637
|
/** ffmpeg binary path from the `ffmpeg` config section, mirrored here so the
|
|
@@ -17734,10 +17960,14 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
17734
17960
|
}
|
|
17735
17961
|
},
|
|
17736
17962
|
clientLossPct: async (deviceId) => {
|
|
17963
|
+
const ctrl = new AbortController();
|
|
17964
|
+
const timer = setTimeout(() => ctrl.abort(), NETWORK_QUALITY_STATS_TIMEOUT_MS);
|
|
17737
17965
|
try {
|
|
17738
|
-
return (await this.ctx.api.networkQuality.getDeviceStats.query({ deviceId }))?.client?.packetLossPercent ?? null;
|
|
17966
|
+
return (await this.ctx.api.networkQuality.getDeviceStats.query({ deviceId }, { signal: ctrl.signal }))?.client?.packetLossPercent ?? null;
|
|
17739
17967
|
} catch {
|
|
17740
17968
|
return null;
|
|
17969
|
+
} finally {
|
|
17970
|
+
clearTimeout(timer);
|
|
17741
17971
|
}
|
|
17742
17972
|
},
|
|
17743
17973
|
resolveTranscodeDecodeHwAccel: () => this.resolveTranscodeDecodeHwAccel(),
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-6I7-nufj.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//#region src/session-decode/worker-protocol.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
}
|
|
5
|
+
function isFiniteNumber(value) {
|
|
6
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
7
|
+
}
|
|
8
|
+
function isWorkerFrameFormat(value) {
|
|
9
|
+
return value === "rgb" || value === "gray";
|
|
10
|
+
}
|
|
11
|
+
function isWorkerCropRegion(value) {
|
|
12
|
+
return isRecord(value) && isFiniteNumber(value.left) && isFiniteNumber(value.top) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
|
|
13
|
+
}
|
|
14
|
+
function isWorkerResizeTarget(value) {
|
|
15
|
+
return isRecord(value) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
|
|
16
|
+
}
|
|
17
|
+
function isFrameImageOptions(value) {
|
|
18
|
+
if (!isRecord(value)) return false;
|
|
19
|
+
if (value.crop !== void 0 && !isWorkerCropRegion(value.crop)) return false;
|
|
20
|
+
if (value.resize !== void 0 && !isWorkerResizeTarget(value.resize)) return false;
|
|
21
|
+
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function isVideoFrameSource(value) {
|
|
25
|
+
return isRecord(value) && typeof value.restreamUrl === "string" && isFiniteNumber(value.deviceId) && typeof value.codec === "string";
|
|
26
|
+
}
|
|
27
|
+
function isVideoFrameGenOptions(value) {
|
|
28
|
+
if (!isRecord(value)) return false;
|
|
29
|
+
if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
|
|
30
|
+
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
|
|
34
|
+
function isWorkerRequest(value) {
|
|
35
|
+
if (!isRecord(value)) return false;
|
|
36
|
+
switch (value.kind) {
|
|
37
|
+
case "start": return isVideoFrameSource(value.source) && isVideoFrameGenOptions(value.opts);
|
|
38
|
+
case "pull": return true;
|
|
39
|
+
case "toBuffer": return isFiniteNumber(value.frameId) && (value.opts === void 0 || isFrameImageOptions(value.opts));
|
|
40
|
+
case "stop": return true;
|
|
41
|
+
default: return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Discriminated-union guard for {@link WorkerReply} — no `as` casts. */
|
|
45
|
+
function isWorkerReply(value) {
|
|
46
|
+
if (!isRecord(value)) return false;
|
|
47
|
+
switch (value.kind) {
|
|
48
|
+
case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format);
|
|
49
|
+
case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
|
|
50
|
+
case "ended": return true;
|
|
51
|
+
case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
|
|
52
|
+
default: return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
Object.defineProperty(exports, "isWorkerReply", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
get: function() {
|
|
59
|
+
return isWorkerReply;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(exports, "isWorkerRequest", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
get: function() {
|
|
65
|
+
return isWorkerRequest;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/session-decode/worker-protocol.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
}
|
|
5
|
+
function isFiniteNumber(value) {
|
|
6
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
7
|
+
}
|
|
8
|
+
function isWorkerFrameFormat(value) {
|
|
9
|
+
return value === "rgb" || value === "gray";
|
|
10
|
+
}
|
|
11
|
+
function isWorkerCropRegion(value) {
|
|
12
|
+
return isRecord(value) && isFiniteNumber(value.left) && isFiniteNumber(value.top) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
|
|
13
|
+
}
|
|
14
|
+
function isWorkerResizeTarget(value) {
|
|
15
|
+
return isRecord(value) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
|
|
16
|
+
}
|
|
17
|
+
function isFrameImageOptions(value) {
|
|
18
|
+
if (!isRecord(value)) return false;
|
|
19
|
+
if (value.crop !== void 0 && !isWorkerCropRegion(value.crop)) return false;
|
|
20
|
+
if (value.resize !== void 0 && !isWorkerResizeTarget(value.resize)) return false;
|
|
21
|
+
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function isVideoFrameSource(value) {
|
|
25
|
+
return isRecord(value) && typeof value.restreamUrl === "string" && isFiniteNumber(value.deviceId) && typeof value.codec === "string";
|
|
26
|
+
}
|
|
27
|
+
function isVideoFrameGenOptions(value) {
|
|
28
|
+
if (!isRecord(value)) return false;
|
|
29
|
+
if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
|
|
30
|
+
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
|
|
34
|
+
function isWorkerRequest(value) {
|
|
35
|
+
if (!isRecord(value)) return false;
|
|
36
|
+
switch (value.kind) {
|
|
37
|
+
case "start": return isVideoFrameSource(value.source) && isVideoFrameGenOptions(value.opts);
|
|
38
|
+
case "pull": return true;
|
|
39
|
+
case "toBuffer": return isFiniteNumber(value.frameId) && (value.opts === void 0 || isFrameImageOptions(value.opts));
|
|
40
|
+
case "stop": return true;
|
|
41
|
+
default: return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Discriminated-union guard for {@link WorkerReply} — no `as` casts. */
|
|
45
|
+
function isWorkerReply(value) {
|
|
46
|
+
if (!isRecord(value)) return false;
|
|
47
|
+
switch (value.kind) {
|
|
48
|
+
case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format);
|
|
49
|
+
case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
|
|
50
|
+
case "ended": return true;
|
|
51
|
+
case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
|
|
52
|
+
default: return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { isWorkerRequest as n, isWorkerReply as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{d as e,f as t,g as n,h as r,m as i,p as a}from"./index-
|
|
1
|
+
import{d as e,f as t,g as n,h as r,m as i,p as a}from"./index-jMj5PyJk.js";var o=n(t()),s=n(e(),1),c=Math.PI/180;function l(){return typeof window<`u`&&({}.toString.call(window)===`[object Window]`||{}.toString.call(window)===`[object global]`)}var u=typeof global<`u`?global:typeof window<`u`?window:typeof WorkerGlobalScope<`u`?self:{},d={_global:u,version:`10.3.0`,isBrowser:l(),isUnminified:/param/.test(function(e){}.toString()),dblClickWindow:400,getAngle(e){return d.angleDeg?e*c:e},enableTrace:!1,pointerEventsEnabled:!0,autoDrawEnabled:!0,hitOnDragEnabled:!1,capturePointerEventsEnabled:!1,_mouseListenClick:!1,_touchListenClick:!1,_pointerListenClick:!1,_mouseInDblClickWindow:!1,_touchInDblClickWindow:!1,_pointerInDblClickWindow:!1,_mouseDblClickPointerId:null,_touchDblClickPointerId:null,_pointerDblClickPointerId:null,_renderBackend:`web`,legacyTextRendering:!1,pixelRatio:typeof window<`u`&&window.devicePixelRatio||1,dragDistance:3,angleDeg:!0,showWarnings:!0,dragButtons:[0,1],isDragging(){return d.DD.isDragging},isTransforming(){return d.Transformer?.isTransforming()??!1},isDragReady(){return!!d.DD.node},releaseCanvasOnDestroy:!0,document:u.document,_injectGlobal(e){u.Konva!==void 0&&console.error(`Several Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.`),u.Konva=e}},f=e=>{d[e.prototype.getClassName()]=e};d._injectGlobal(d);var p=`Konva.js unsupported environment.
|
|
2
2
|
|
|
3
3
|
Looks like you are trying to use Konva.js in Node.js environment. because "document" object is undefined.
|
|
4
4
|
|