@camstack/addon-pipeline 1.1.42 → 1.1.44
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 +5 -4
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/chunk-D6vf50IK.js +28 -0
- package/dist/detection-pipeline/index.js +535 -115
- package/dist/detection-pipeline/index.mjs +529 -110
- package/dist/{dist-GFd8M6KO.mjs → dist-CgBGV988.mjs} +214 -186
- package/dist/{dist-NvwN60Fq.js → dist-CySHUkXY.js} +219 -185
- package/dist/{model-download-service-C-IHWnXx-DxM2DSns.js → model-download-service-C-IHWnXx-BLXzxpRB.js} +3 -29
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +122 -287
- package/dist/pipeline-runner/index.mjs +118 -285
- package/dist/recorder/index.js +78 -31
- package/dist/recorder/index.mjs +75 -29
- package/dist/{frame-handle-plane-E_AsR77T.mjs → remote-source-plane-B4hRwchu.mjs} +219 -2
- package/dist/{frame-handle-plane-B3Fxww8H.js → remote-source-plane-D9m0z1N9.js} +236 -1
- package/dist/session-decode/decode-worker-child.js +118 -12
- package/dist/session-decode/decode-worker-child.mjs +115 -12
- package/dist/stream-broker/_stub.js +550 -523
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CeielP5P.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-G6wtUb3G.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Dlagq9X_.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-BUpF966l.mjs +26 -0
- package/dist/stream-broker/{hostInit-DmjqskOR.mjs → hostInit-BSzP3BIj.mjs} +2 -2
- package/dist/stream-broker/index.js +840 -196
- package/dist/stream-broker/index.mjs +831 -188
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CEsPjwzT.js → MaskShapeCanvas-DI4BY7W2-D2ML5TPi.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-D43fIRgB.js → MotionZonesSettings-NcxxQN8r-TTWGGaqv.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-C3wJxG8V.js → PrivacyMaskSettings-APgPLF7p-CJqHU2nU.js} +1 -1
- package/embed-dist/assets/index-C1oaL9s7.js +81 -0
- package/embed-dist/assets/{index-CSFtK41z.css → index-CA4kv43T.css} +1 -1
- package/embed-dist/index.html +2 -2
- package/package.json +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DqJvWBKS.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-dGka8yZY.mjs +0 -26
- package/embed-dist/assets/index-CX-rILhw.js +0 -81
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as errMsg, d as RingBuffer } from "./dist-
|
|
1
|
+
import { D as nodePin, I as errMsg, d as RingBuffer } from "./dist-CgBGV988.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
//#region src/stream-broker/stream-broker/decoder-session-proxy.ts
|
|
4
4
|
/**
|
|
@@ -781,4 +781,221 @@ var FrameHandlePlane = class {
|
|
|
781
781
|
}
|
|
782
782
|
};
|
|
783
783
|
//#endregion
|
|
784
|
-
|
|
784
|
+
//#region src/pipeline-runner/remote-source-plane.ts
|
|
785
|
+
/**
|
|
786
|
+
* Runner-side mode selection (pure): whether an attach payload routes this
|
|
787
|
+
* camera through the remote-source leg. Absent `frameSource` (a pre-P2c
|
|
788
|
+
* payload) and `local-broker` both take today's co-located broker path —
|
|
789
|
+
* the rollout-OFF safety invariant.
|
|
790
|
+
*/
|
|
791
|
+
function isRemoteRestream(frameSource) {
|
|
792
|
+
return frameSource !== void 0 && frameSource.kind === "remote-restream";
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Resolve WHICH broker profile a camStream id feeds for a device (pure).
|
|
796
|
+
*
|
|
797
|
+
* `getStreamWithCodec` targets a PROFILE (its assigned camStream), while the
|
|
798
|
+
* runner is dispatched with camStream ids (`motionStreamId` /
|
|
799
|
+
* `detectionStreamId`). The broker's profile slots carry the mapping
|
|
800
|
+
* (`sourceCamStreamId`); a stream no slot feeds returns `null` — the acquire
|
|
801
|
+
* fails fast and the poller's backoff retries (the assignment may still be
|
|
802
|
+
* propagating).
|
|
803
|
+
*/
|
|
804
|
+
function profileForStreamId(slots, deviceId, streamId) {
|
|
805
|
+
for (const slot of slots) if (slot.deviceId === deviceId && slot.sourceCamStreamId === streamId) return slot.profile;
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
var RemoteSourcePlane = class {
|
|
809
|
+
api;
|
|
810
|
+
logger;
|
|
811
|
+
deviceId;
|
|
812
|
+
ownNodeId;
|
|
813
|
+
ownerNodeId;
|
|
814
|
+
hostname;
|
|
815
|
+
/** Single-flight entry creation per camStream id. */
|
|
816
|
+
entries = /* @__PURE__ */ new Map();
|
|
817
|
+
disposed = false;
|
|
818
|
+
constructor(options) {
|
|
819
|
+
this.api = options.api;
|
|
820
|
+
this.logger = options.logger.withTags({ deviceId: options.deviceId });
|
|
821
|
+
this.deviceId = options.deviceId;
|
|
822
|
+
this.ownNodeId = options.ownNodeId;
|
|
823
|
+
this.ownerNodeId = options.ownerNodeId;
|
|
824
|
+
this.hostname = options.hostname;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* A {@link FrameHandleSource} for one camStream id, pluggable into
|
|
828
|
+
* `startFrameHandlePoller`. Motion (`gray`) and detection (`rgb`)
|
|
829
|
+
* subscriptions of the same stream share ONE acquired restream and ONE
|
|
830
|
+
* satellite plane (per-format decoder sessions inside it — hub parity).
|
|
831
|
+
*/
|
|
832
|
+
makeSource(streamId) {
|
|
833
|
+
return {
|
|
834
|
+
subscribe: async (input) => {
|
|
835
|
+
if (this.disposed) throw new Error("RemoteSourcePlane: subscribe after dispose");
|
|
836
|
+
const entry = await this.ensureEntry(streamId);
|
|
837
|
+
const result = await entry.plane.subscribe({
|
|
838
|
+
format: input.format,
|
|
839
|
+
maxFps: input.maxFps,
|
|
840
|
+
tag: input.tag
|
|
841
|
+
});
|
|
842
|
+
entry.subscriptionIds.add(result.subscriptionId);
|
|
843
|
+
return result;
|
|
844
|
+
},
|
|
845
|
+
pullHandles: async (input) => {
|
|
846
|
+
const entry = await this.peekEntry(streamId);
|
|
847
|
+
if (!entry) return [];
|
|
848
|
+
return entry.plane.pullHandles(input.subscriptionId, input.maxCount);
|
|
849
|
+
},
|
|
850
|
+
unsubscribe: async (input) => {
|
|
851
|
+
const entry = await this.peekEntry(streamId);
|
|
852
|
+
if (!entry) return;
|
|
853
|
+
entry.subscriptionIds.delete(input.subscriptionId);
|
|
854
|
+
await entry.plane.unsubscribe(input.subscriptionId).catch(() => {});
|
|
855
|
+
if (entry.subscriptionIds.size === 0) {
|
|
856
|
+
this.entries.delete(streamId);
|
|
857
|
+
await this.teardownEntry(entry, streamId);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/** Dispose every entry: satellite planes destroyed, restreams released. Idempotent. */
|
|
863
|
+
async dispose() {
|
|
864
|
+
if (this.disposed) return;
|
|
865
|
+
this.disposed = true;
|
|
866
|
+
const pending = [...this.entries.entries()];
|
|
867
|
+
this.entries.clear();
|
|
868
|
+
for (const [streamId, entryPromise] of pending) try {
|
|
869
|
+
const entry = await entryPromise;
|
|
870
|
+
await this.teardownEntry(entry, streamId);
|
|
871
|
+
} catch {}
|
|
872
|
+
}
|
|
873
|
+
/** The live entry for a stream, or `null` (never creates one). */
|
|
874
|
+
async peekEntry(streamId) {
|
|
875
|
+
const pending = this.entries.get(streamId);
|
|
876
|
+
if (!pending) return null;
|
|
877
|
+
try {
|
|
878
|
+
return await pending;
|
|
879
|
+
} catch {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
/** Single-flight: acquire the restream + build the satellite plane once per stream. */
|
|
884
|
+
ensureEntry(streamId) {
|
|
885
|
+
const existing = this.entries.get(streamId);
|
|
886
|
+
if (existing) return existing;
|
|
887
|
+
const creation = this.createEntry(streamId);
|
|
888
|
+
this.entries.set(streamId, creation);
|
|
889
|
+
creation.catch(() => {
|
|
890
|
+
if (this.entries.get(streamId) === creation) this.entries.delete(streamId);
|
|
891
|
+
});
|
|
892
|
+
return creation;
|
|
893
|
+
}
|
|
894
|
+
async createEntry(streamId) {
|
|
895
|
+
const { api, deviceId, ownNodeId, ownerNodeId } = this;
|
|
896
|
+
if (this.hostname === void 0) throw new Error("RemoteSourcePlane: owner hostname unresolvable — set frameSource.hubHostnameOverride or CAMSTACK_HUB_URL (a 127.0.0.1 restream URL would dial the wrong box)");
|
|
897
|
+
const profile = profileForStreamId(await api.streamBroker.listAllProfileSlots.query(void 0, nodePin(ownerNodeId)), deviceId, streamId);
|
|
898
|
+
if (profile === null) throw new Error(`RemoteSourcePlane: no broker profile is fed by camStream '${streamId}' of device ${deviceId} — cannot acquire the owner restream`);
|
|
899
|
+
const rtpSource = await api.streamBroker.getStreamWithCodec.mutate({
|
|
900
|
+
deviceId,
|
|
901
|
+
video: "copy",
|
|
902
|
+
audio: "none",
|
|
903
|
+
profile,
|
|
904
|
+
hostname: this.hostname,
|
|
905
|
+
tag: `runner:${ownNodeId}:remote-src:${deviceId}/${streamId}`
|
|
906
|
+
}, nodePin(ownerNodeId));
|
|
907
|
+
const codec = rtpSource.videoCodec === "H265" ? "h265" : "h264";
|
|
908
|
+
const plane = new FrameHandlePlane({
|
|
909
|
+
decoderApi: this.buildPinnedDecoderCapApi(),
|
|
910
|
+
logger: this.logger,
|
|
911
|
+
localNodeId: ownNodeId,
|
|
912
|
+
resolveStreamInfo: () => ({
|
|
913
|
+
codec,
|
|
914
|
+
numericDeviceId: deviceId,
|
|
915
|
+
tag: `runner-remote:${deviceId}/${streamId}`,
|
|
916
|
+
pullModeUrl: rtpSource.url
|
|
917
|
+
})
|
|
918
|
+
});
|
|
919
|
+
this.logger.info("remote-source plane: owner restream acquired", { meta: {
|
|
920
|
+
streamId,
|
|
921
|
+
profile,
|
|
922
|
+
codec,
|
|
923
|
+
pipelineKey: rtpSource.pipelineKey,
|
|
924
|
+
hostname: this.hostname
|
|
925
|
+
} });
|
|
926
|
+
return {
|
|
927
|
+
plane,
|
|
928
|
+
pipelineKey: rtpSource.pipelineKey,
|
|
929
|
+
subscriptionIds: /* @__PURE__ */ new Set(),
|
|
930
|
+
released: false
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
/** Destroy an entry's satellite plane and release its restream refcount once. */
|
|
934
|
+
async teardownEntry(entry, streamId) {
|
|
935
|
+
await entry.plane.dispose().catch((err) => {
|
|
936
|
+
this.logger.warn("remote-source plane: satellite plane dispose failed", { meta: {
|
|
937
|
+
streamId,
|
|
938
|
+
error: errMsg(err)
|
|
939
|
+
} });
|
|
940
|
+
});
|
|
941
|
+
if (entry.released) return;
|
|
942
|
+
entry.released = true;
|
|
943
|
+
await this.api.streamBroker.releaseStreamWithCodec.mutate({ pipelineKey: entry.pipelineKey }, nodePin(this.ownerNodeId)).catch((err) => {
|
|
944
|
+
this.logger.debug("remote-source plane: releaseStreamWithCodec failed (benign on hub restart)", { meta: {
|
|
945
|
+
streamId,
|
|
946
|
+
pipelineKey: entry.pipelineKey,
|
|
947
|
+
error: errMsg(err)
|
|
948
|
+
} });
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Every decoder call pinned to THIS runner's node — mirror of the hub
|
|
953
|
+
* broker's wrapper (`stream-broker-manager.buildDecoderCapApi`): the
|
|
954
|
+
* `decoder` cap is a singleton whose UNPINNED calls resolve to the
|
|
955
|
+
* cluster-elected node, which is generally NOT this agent. Pinning create
|
|
956
|
+
* AND every session op to the same local node keeps the session + its shm
|
|
957
|
+
* ring here, where this plane's readers live.
|
|
958
|
+
*/
|
|
959
|
+
buildPinnedDecoderCapApi() {
|
|
960
|
+
const api = this.api;
|
|
961
|
+
const pin = nodePin(this.ownNodeId);
|
|
962
|
+
const toWireBytes = (bytes) => {
|
|
963
|
+
const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
|
|
964
|
+
copy.set(bytes);
|
|
965
|
+
return copy;
|
|
966
|
+
};
|
|
967
|
+
return {
|
|
968
|
+
supportsCodec: async (input) => api.decoder.supportsCodec.query({ ...input }, pin),
|
|
969
|
+
getInfo: async () => api.decoder.getInfo.query(void 0, pin),
|
|
970
|
+
createSession: async (config) => api.decoder.createSession.query({ ...config }, pin),
|
|
971
|
+
pushPacket: async (input) => {
|
|
972
|
+
await api.decoder.pushPacket.query({
|
|
973
|
+
sessionId: input.sessionId,
|
|
974
|
+
packet: {
|
|
975
|
+
...input.packet,
|
|
976
|
+
data: toWireBytes(input.packet.data)
|
|
977
|
+
}
|
|
978
|
+
}, pin);
|
|
979
|
+
},
|
|
980
|
+
pullFrames: async (input) => {
|
|
981
|
+
return (await api.decoder.pullFrames.query({ ...input }, pin)).map((frame) => ({
|
|
982
|
+
...frame,
|
|
983
|
+
data: Buffer.from(frame.data)
|
|
984
|
+
}));
|
|
985
|
+
},
|
|
986
|
+
pullHandles: async (input) => api.decoder.pullHandles.query({ ...input }, pin),
|
|
987
|
+
destroySession: async (input) => {
|
|
988
|
+
await api.decoder.destroySession.query({ ...input }, pin);
|
|
989
|
+
},
|
|
990
|
+
openStream: async (input) => {
|
|
991
|
+
await api.decoder.openStream.query({ ...input }, pin);
|
|
992
|
+
},
|
|
993
|
+
updateConfig: async (input) => {
|
|
994
|
+
await api.decoder.updateConfig.query({ ...input }, pin);
|
|
995
|
+
},
|
|
996
|
+
getStats: async (input) => api.decoder.getStats.query({ ...input }, pin)
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
//#endregion
|
|
1001
|
+
export { DecoderSessionProxy as a, FrameHandlePlane as i, isRemoteRestream as n, profileForStreamId as r, RemoteSourcePlane as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-CySHUkXY.js");
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
3
|
//#region src/stream-broker/stream-broker/decoder-session-proxy.ts
|
|
4
4
|
/**
|
|
@@ -781,6 +781,223 @@ var FrameHandlePlane = class {
|
|
|
781
781
|
}
|
|
782
782
|
};
|
|
783
783
|
//#endregion
|
|
784
|
+
//#region src/pipeline-runner/remote-source-plane.ts
|
|
785
|
+
/**
|
|
786
|
+
* Runner-side mode selection (pure): whether an attach payload routes this
|
|
787
|
+
* camera through the remote-source leg. Absent `frameSource` (a pre-P2c
|
|
788
|
+
* payload) and `local-broker` both take today's co-located broker path —
|
|
789
|
+
* the rollout-OFF safety invariant.
|
|
790
|
+
*/
|
|
791
|
+
function isRemoteRestream(frameSource) {
|
|
792
|
+
return frameSource !== void 0 && frameSource.kind === "remote-restream";
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Resolve WHICH broker profile a camStream id feeds for a device (pure).
|
|
796
|
+
*
|
|
797
|
+
* `getStreamWithCodec` targets a PROFILE (its assigned camStream), while the
|
|
798
|
+
* runner is dispatched with camStream ids (`motionStreamId` /
|
|
799
|
+
* `detectionStreamId`). The broker's profile slots carry the mapping
|
|
800
|
+
* (`sourceCamStreamId`); a stream no slot feeds returns `null` — the acquire
|
|
801
|
+
* fails fast and the poller's backoff retries (the assignment may still be
|
|
802
|
+
* propagating).
|
|
803
|
+
*/
|
|
804
|
+
function profileForStreamId(slots, deviceId, streamId) {
|
|
805
|
+
for (const slot of slots) if (slot.deviceId === deviceId && slot.sourceCamStreamId === streamId) return slot.profile;
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
var RemoteSourcePlane = class {
|
|
809
|
+
api;
|
|
810
|
+
logger;
|
|
811
|
+
deviceId;
|
|
812
|
+
ownNodeId;
|
|
813
|
+
ownerNodeId;
|
|
814
|
+
hostname;
|
|
815
|
+
/** Single-flight entry creation per camStream id. */
|
|
816
|
+
entries = /* @__PURE__ */ new Map();
|
|
817
|
+
disposed = false;
|
|
818
|
+
constructor(options) {
|
|
819
|
+
this.api = options.api;
|
|
820
|
+
this.logger = options.logger.withTags({ deviceId: options.deviceId });
|
|
821
|
+
this.deviceId = options.deviceId;
|
|
822
|
+
this.ownNodeId = options.ownNodeId;
|
|
823
|
+
this.ownerNodeId = options.ownerNodeId;
|
|
824
|
+
this.hostname = options.hostname;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* A {@link FrameHandleSource} for one camStream id, pluggable into
|
|
828
|
+
* `startFrameHandlePoller`. Motion (`gray`) and detection (`rgb`)
|
|
829
|
+
* subscriptions of the same stream share ONE acquired restream and ONE
|
|
830
|
+
* satellite plane (per-format decoder sessions inside it — hub parity).
|
|
831
|
+
*/
|
|
832
|
+
makeSource(streamId) {
|
|
833
|
+
return {
|
|
834
|
+
subscribe: async (input) => {
|
|
835
|
+
if (this.disposed) throw new Error("RemoteSourcePlane: subscribe after dispose");
|
|
836
|
+
const entry = await this.ensureEntry(streamId);
|
|
837
|
+
const result = await entry.plane.subscribe({
|
|
838
|
+
format: input.format,
|
|
839
|
+
maxFps: input.maxFps,
|
|
840
|
+
tag: input.tag
|
|
841
|
+
});
|
|
842
|
+
entry.subscriptionIds.add(result.subscriptionId);
|
|
843
|
+
return result;
|
|
844
|
+
},
|
|
845
|
+
pullHandles: async (input) => {
|
|
846
|
+
const entry = await this.peekEntry(streamId);
|
|
847
|
+
if (!entry) return [];
|
|
848
|
+
return entry.plane.pullHandles(input.subscriptionId, input.maxCount);
|
|
849
|
+
},
|
|
850
|
+
unsubscribe: async (input) => {
|
|
851
|
+
const entry = await this.peekEntry(streamId);
|
|
852
|
+
if (!entry) return;
|
|
853
|
+
entry.subscriptionIds.delete(input.subscriptionId);
|
|
854
|
+
await entry.plane.unsubscribe(input.subscriptionId).catch(() => {});
|
|
855
|
+
if (entry.subscriptionIds.size === 0) {
|
|
856
|
+
this.entries.delete(streamId);
|
|
857
|
+
await this.teardownEntry(entry, streamId);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/** Dispose every entry: satellite planes destroyed, restreams released. Idempotent. */
|
|
863
|
+
async dispose() {
|
|
864
|
+
if (this.disposed) return;
|
|
865
|
+
this.disposed = true;
|
|
866
|
+
const pending = [...this.entries.entries()];
|
|
867
|
+
this.entries.clear();
|
|
868
|
+
for (const [streamId, entryPromise] of pending) try {
|
|
869
|
+
const entry = await entryPromise;
|
|
870
|
+
await this.teardownEntry(entry, streamId);
|
|
871
|
+
} catch {}
|
|
872
|
+
}
|
|
873
|
+
/** The live entry for a stream, or `null` (never creates one). */
|
|
874
|
+
async peekEntry(streamId) {
|
|
875
|
+
const pending = this.entries.get(streamId);
|
|
876
|
+
if (!pending) return null;
|
|
877
|
+
try {
|
|
878
|
+
return await pending;
|
|
879
|
+
} catch {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
/** Single-flight: acquire the restream + build the satellite plane once per stream. */
|
|
884
|
+
ensureEntry(streamId) {
|
|
885
|
+
const existing = this.entries.get(streamId);
|
|
886
|
+
if (existing) return existing;
|
|
887
|
+
const creation = this.createEntry(streamId);
|
|
888
|
+
this.entries.set(streamId, creation);
|
|
889
|
+
creation.catch(() => {
|
|
890
|
+
if (this.entries.get(streamId) === creation) this.entries.delete(streamId);
|
|
891
|
+
});
|
|
892
|
+
return creation;
|
|
893
|
+
}
|
|
894
|
+
async createEntry(streamId) {
|
|
895
|
+
const { api, deviceId, ownNodeId, ownerNodeId } = this;
|
|
896
|
+
if (this.hostname === void 0) throw new Error("RemoteSourcePlane: owner hostname unresolvable — set frameSource.hubHostnameOverride or CAMSTACK_HUB_URL (a 127.0.0.1 restream URL would dial the wrong box)");
|
|
897
|
+
const profile = profileForStreamId(await api.streamBroker.listAllProfileSlots.query(void 0, require_dist.nodePin(ownerNodeId)), deviceId, streamId);
|
|
898
|
+
if (profile === null) throw new Error(`RemoteSourcePlane: no broker profile is fed by camStream '${streamId}' of device ${deviceId} — cannot acquire the owner restream`);
|
|
899
|
+
const rtpSource = await api.streamBroker.getStreamWithCodec.mutate({
|
|
900
|
+
deviceId,
|
|
901
|
+
video: "copy",
|
|
902
|
+
audio: "none",
|
|
903
|
+
profile,
|
|
904
|
+
hostname: this.hostname,
|
|
905
|
+
tag: `runner:${ownNodeId}:remote-src:${deviceId}/${streamId}`
|
|
906
|
+
}, require_dist.nodePin(ownerNodeId));
|
|
907
|
+
const codec = rtpSource.videoCodec === "H265" ? "h265" : "h264";
|
|
908
|
+
const plane = new FrameHandlePlane({
|
|
909
|
+
decoderApi: this.buildPinnedDecoderCapApi(),
|
|
910
|
+
logger: this.logger,
|
|
911
|
+
localNodeId: ownNodeId,
|
|
912
|
+
resolveStreamInfo: () => ({
|
|
913
|
+
codec,
|
|
914
|
+
numericDeviceId: deviceId,
|
|
915
|
+
tag: `runner-remote:${deviceId}/${streamId}`,
|
|
916
|
+
pullModeUrl: rtpSource.url
|
|
917
|
+
})
|
|
918
|
+
});
|
|
919
|
+
this.logger.info("remote-source plane: owner restream acquired", { meta: {
|
|
920
|
+
streamId,
|
|
921
|
+
profile,
|
|
922
|
+
codec,
|
|
923
|
+
pipelineKey: rtpSource.pipelineKey,
|
|
924
|
+
hostname: this.hostname
|
|
925
|
+
} });
|
|
926
|
+
return {
|
|
927
|
+
plane,
|
|
928
|
+
pipelineKey: rtpSource.pipelineKey,
|
|
929
|
+
subscriptionIds: /* @__PURE__ */ new Set(),
|
|
930
|
+
released: false
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
/** Destroy an entry's satellite plane and release its restream refcount once. */
|
|
934
|
+
async teardownEntry(entry, streamId) {
|
|
935
|
+
await entry.plane.dispose().catch((err) => {
|
|
936
|
+
this.logger.warn("remote-source plane: satellite plane dispose failed", { meta: {
|
|
937
|
+
streamId,
|
|
938
|
+
error: require_dist.errMsg(err)
|
|
939
|
+
} });
|
|
940
|
+
});
|
|
941
|
+
if (entry.released) return;
|
|
942
|
+
entry.released = true;
|
|
943
|
+
await this.api.streamBroker.releaseStreamWithCodec.mutate({ pipelineKey: entry.pipelineKey }, require_dist.nodePin(this.ownerNodeId)).catch((err) => {
|
|
944
|
+
this.logger.debug("remote-source plane: releaseStreamWithCodec failed (benign on hub restart)", { meta: {
|
|
945
|
+
streamId,
|
|
946
|
+
pipelineKey: entry.pipelineKey,
|
|
947
|
+
error: require_dist.errMsg(err)
|
|
948
|
+
} });
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Every decoder call pinned to THIS runner's node — mirror of the hub
|
|
953
|
+
* broker's wrapper (`stream-broker-manager.buildDecoderCapApi`): the
|
|
954
|
+
* `decoder` cap is a singleton whose UNPINNED calls resolve to the
|
|
955
|
+
* cluster-elected node, which is generally NOT this agent. Pinning create
|
|
956
|
+
* AND every session op to the same local node keeps the session + its shm
|
|
957
|
+
* ring here, where this plane's readers live.
|
|
958
|
+
*/
|
|
959
|
+
buildPinnedDecoderCapApi() {
|
|
960
|
+
const api = this.api;
|
|
961
|
+
const pin = require_dist.nodePin(this.ownNodeId);
|
|
962
|
+
const toWireBytes = (bytes) => {
|
|
963
|
+
const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
|
|
964
|
+
copy.set(bytes);
|
|
965
|
+
return copy;
|
|
966
|
+
};
|
|
967
|
+
return {
|
|
968
|
+
supportsCodec: async (input) => api.decoder.supportsCodec.query({ ...input }, pin),
|
|
969
|
+
getInfo: async () => api.decoder.getInfo.query(void 0, pin),
|
|
970
|
+
createSession: async (config) => api.decoder.createSession.query({ ...config }, pin),
|
|
971
|
+
pushPacket: async (input) => {
|
|
972
|
+
await api.decoder.pushPacket.query({
|
|
973
|
+
sessionId: input.sessionId,
|
|
974
|
+
packet: {
|
|
975
|
+
...input.packet,
|
|
976
|
+
data: toWireBytes(input.packet.data)
|
|
977
|
+
}
|
|
978
|
+
}, pin);
|
|
979
|
+
},
|
|
980
|
+
pullFrames: async (input) => {
|
|
981
|
+
return (await api.decoder.pullFrames.query({ ...input }, pin)).map((frame) => ({
|
|
982
|
+
...frame,
|
|
983
|
+
data: Buffer.from(frame.data)
|
|
984
|
+
}));
|
|
985
|
+
},
|
|
986
|
+
pullHandles: async (input) => api.decoder.pullHandles.query({ ...input }, pin),
|
|
987
|
+
destroySession: async (input) => {
|
|
988
|
+
await api.decoder.destroySession.query({ ...input }, pin);
|
|
989
|
+
},
|
|
990
|
+
openStream: async (input) => {
|
|
991
|
+
await api.decoder.openStream.query({ ...input }, pin);
|
|
992
|
+
},
|
|
993
|
+
updateConfig: async (input) => {
|
|
994
|
+
await api.decoder.updateConfig.query({ ...input }, pin);
|
|
995
|
+
},
|
|
996
|
+
getStats: async (input) => api.decoder.getStats.query({ ...input }, pin)
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
//#endregion
|
|
784
1001
|
Object.defineProperty(exports, "DecoderSessionProxy", {
|
|
785
1002
|
enumerable: true,
|
|
786
1003
|
get: function() {
|
|
@@ -793,3 +1010,21 @@ Object.defineProperty(exports, "FrameHandlePlane", {
|
|
|
793
1010
|
return FrameHandlePlane;
|
|
794
1011
|
}
|
|
795
1012
|
});
|
|
1013
|
+
Object.defineProperty(exports, "RemoteSourcePlane", {
|
|
1014
|
+
enumerable: true,
|
|
1015
|
+
get: function() {
|
|
1016
|
+
return RemoteSourcePlane;
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
Object.defineProperty(exports, "isRemoteRestream", {
|
|
1020
|
+
enumerable: true,
|
|
1021
|
+
get: function() {
|
|
1022
|
+
return isRemoteRestream;
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
Object.defineProperty(exports, "profileForStreamId", {
|
|
1026
|
+
enumerable: true,
|
|
1027
|
+
get: function() {
|
|
1028
|
+
return profileForStreamId;
|
|
1029
|
+
}
|
|
1030
|
+
});
|