@camstack/system 1.2.261 → 1.2.263

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.
@@ -8,7 +8,7 @@ interface CameraGridAddonConfig {
8
8
  export declare class CameraGridAddon extends BaseAddon<CameraGridAddonConfig> {
9
9
  private relay;
10
10
  private reconcileTimer;
11
- /** Keyed by {@link sessionKey} — one composition per grid per profile. */
11
+ /** Keyed by instance id — ONE composition per grid. */
12
12
  private readonly sessions;
13
13
  private readonly deviceIdByInstanceId;
14
14
  private readonly tombstones;
@@ -22,7 +22,7 @@ export declare class CameraGridAddon extends BaseAddon<CameraGridAddonConfig> {
22
22
  * (D224), and 60 s is the window for a fact — "does 615 have a low assigned"
23
23
  * — that only changes when an operator changes it.
24
24
  */
25
- private readonly offersByInstanceId;
25
+ private readonly planByInstanceId;
26
26
  private snapshots;
27
27
  private frameSampler;
28
28
  private ffmpegBinaryPath;
@@ -44,30 +44,35 @@ export declare class CameraGridAddon extends BaseAddon<CameraGridAddonConfig> {
44
44
  private reconcile;
45
45
  private silenceAnalysis;
46
46
  /**
47
- * Which profiles each grid can honestly publish, asked of the BROKER.
47
+ * Which stream each grid reads each of its sources at, asked of the BROKER.
48
48
  *
49
49
  * `listAllProfileSlots` is the one authority on "does 615 have a low
50
50
  * assigned", and `sourceCamStreamId === null` is its way of saying no. A read
51
- * that FAILS leaves the previous answer in place and says so - it is not an
51
+ * that FAILS leaves the previous answer in place and says so — it is not an
52
52
  * empty catalog, and turning it into one would retract the streams of every
53
53
  * grid on the hub.
54
54
  */
55
- private refreshProfileOffers;
55
+ private refreshSourcePlans;
56
56
  /**
57
- * Say which profiles this grid will NOT publish, and why - once per change.
57
+ * Say WHICH stream each cell is read at, and who could not be read - once per
58
+ * change.
58
59
  *
59
- * A profile silently missing from a catalog is indistinguishable from a
60
- * broker that swept it. Logged on the EDGE rather than every minute: a grid
61
- * that can only serve `high` would otherwise write two lines a minute for
62
- * ever.
60
+ * Both halves earn their line. A grid silently missing from a catalog is
61
+ * indistinguishable from a broker that swept it, so the refusal names the
62
+ * sources. And the chosen profiles are the answer to "why is this tile
63
+ * soft?" — a camera whose only assigned slot is `high` is read at `high`, and
64
+ * nothing else in the system would ever say so.
65
+ *
66
+ * Logged on the EDGE: a grid that cannot be composed would otherwise write a
67
+ * line a minute for ever.
63
68
  */
64
- private logRefusals;
65
- private offeredProfiles;
69
+ private logSourcePlan;
70
+ private planFor;
66
71
  private ensureSessions;
67
72
  private makeSession;
68
73
  /** The session a dial is asking for, or `null` - which the relay answers 404 to. */
69
74
  private sessionFor;
70
- /** This grid's sessions, in DESCENDING quality - the snapshot takes the first running one. */
75
+ /** This grid's composition, as the list the snapshot source expects. */
71
76
  private sessionsForDevice;
72
77
  /** A grid that is gone stops immediately - it must not keep N sources dialled. */
73
78
  private retireWithdrawnSessions;
@@ -1,47 +1,42 @@
1
1
  import { CamProfile } from '@camstack/types';
2
2
  /**
3
- * One id, for every grid, forever — for the HIGH composition.
3
+ * One id, for every grid, forever.
4
4
  *
5
- * D519's reason still holds and is what keeps this id bare: a per-instance or
6
- * newly-spelled id mints a second cam-stream and orphans whatever profile
7
- * assignment the operator had made. `grid` was `high` on the live hub before
8
- * this change, so `high` keeps it and the other two are new.
5
+ * D519's reason is what keeps this id bare: a per-instance or newly-spelled id
6
+ * mints a second cam-stream and orphans whatever profile assignment the
7
+ * operator had made. It was `high` on the live hub before the per-profile
8
+ * detour and it is `high` again, so nothing an operator assigned is disturbed.
9
9
  */
10
10
  export declare const GRID_CAM_STREAM_ID = "grid";
11
- /** Every profile a grid could conceivably compose, in descending quality. */
12
- export declare const GRID_PROFILES: readonly CamProfile[];
13
- export interface GridCanvas {
14
- readonly width: number;
15
- readonly height: number;
16
- }
17
- /** The cam-stream id a grid publishes for one profile. */
18
- export declare function gridCamStreamId(profile: CamProfile): string;
19
- /** Even, floored, scaled by the profile. libx264 refuses an odd dimension. */
20
- export declare function gridCanvasFor(canvas: GridCanvas, profile: CamProfile): GridCanvas;
21
- export interface OfferedGridProfilesInput {
11
+ /**
12
+ * The slot the single composition claims.
13
+ *
14
+ * NOT decoration. A device publishing one stream with no hint is ranked by
15
+ * pixel count and lands in `mid` — the slot `recordings` does not select on its
16
+ * own, that `recordingsLow` ignores, and that scrub never reads. The Dreame
17
+ * robot's single 720p relay landed exactly there and its footage was written
18
+ * where nothing looked.
19
+ */
20
+ export declare const GRID_OUTPUT_PROFILE: CamProfile;
21
+ /** The cheapest stream this source serves, or `null` when it serves none. */
22
+ export declare function lowestServedProfile(served: readonly CamProfile[]): CamProfile | null;
23
+ export interface GridSourceProfilesInput {
22
24
  /** Every camera the composition takes a cell from, deduplicated. */
23
25
  readonly sourceDeviceIds: readonly number[];
24
26
  /**
25
27
  * Which profiles this source can serve, or `null` when nobody could say.
26
28
  * `null` is not an empty list: an unanswerable question is a refusal, and
27
- * saying so separately is what keeps "we did not ask" out of the catalog.
29
+ * saying so separately keeps "we did not ask" out of the catalog.
28
30
  */
29
31
  readonly profilesServedBy: (deviceId: number) => readonly CamProfile[] | null;
30
32
  }
31
- export type GridProfileOffer = {
32
- readonly profile: CamProfile;
33
- readonly offered: true;
34
- } | {
35
- readonly profile: CamProfile;
36
- readonly offered: false;
37
- /** The sources that could not serve it. Empty only when there are none at all. */
33
+ export interface GridSourcePlan {
34
+ /** True when every source can be read — the only case a grid is on offer. */
35
+ readonly offered: boolean;
36
+ /** Which stream each source is read at. Only contains readable sources. */
37
+ readonly profileBySource: ReadonlyMap<number, CamProfile>;
38
+ /** The sources that serve nothing, or that nobody could answer for. */
38
39
  readonly missingSources: readonly number[];
39
- };
40
- /**
41
- * What this grid can honestly publish, per profile, with the refusal named.
42
- *
43
- * Every profile gets a row whether it is offered or not: an absent row says
44
- * nothing, and the caller logging a refusal needs the reason more than the
45
- * caller publishing an offer needs the offer.
46
- */
47
- export declare function offeredGridProfiles(input: OfferedGridProfilesInput): readonly GridProfileOffer[];
40
+ }
41
+ /** Which stream to read each source at, and who cannot be read at all. */
42
+ export declare function gridSourceProfiles(input: GridSourceProfilesInput): GridSourcePlan;
@@ -1,4 +1,3 @@
1
- import { CamProfile } from '@camstack/types';
2
1
  import { GridStreamLogger } from './grid-stream-session.js';
3
2
  /** Where a grid's last composed frame is kept between compositions. */
4
3
  export interface GridLastFrameStore {
@@ -8,7 +7,6 @@ export interface GridLastFrameStore {
8
7
  }
9
8
  /** Just enough of a session for the snapshot to choose one and read its state. */
10
9
  export interface GridSnapshotSession {
11
- readonly profile: CamProfile;
12
10
  isRunning(): boolean;
13
11
  }
14
12
  export interface GridSnapshotAnswerInput {
@@ -40,8 +38,10 @@ export interface GridSnapshotImage {
40
38
  export interface GridSnapshotDeps<S extends GridSnapshotSession = GridSnapshotSession> {
41
39
  readonly logger: GridStreamLogger;
42
40
  /**
43
- * This grid's sessions, one per published profile, in descending quality.
44
- * EMPTY means this addon does not own the device — never "no picture".
41
+ * This grid's compositions — one, since the collapse to a single output.
42
+ * Kept as a LIST because the choosing logic below is the same either way and
43
+ * a list of one costs nothing. EMPTY means this addon does not own the
44
+ * device — never "no picture".
45
45
  */
46
46
  readonly sessionsFor: (deviceId: number) => readonly S[];
47
47
  /**
@@ -1,7 +1,5 @@
1
- import { CamProfile, CamStreamDescriptor } from '@camstack/types';
1
+ import { CamStreamDescriptor } from '@camstack/types';
2
2
  export interface GridStreamDescriptorInput {
3
- /** Which composition this is. Also the `profileHint` — see below. */
4
- readonly profile: CamProfile;
5
3
  readonly width: number;
6
4
  readonly height: number;
7
5
  readonly fps: number;
@@ -17,8 +15,8 @@ export interface GridStreamDescriptorInput {
17
15
  * scrub never reads. The Dreame robot's one 720p relay landed exactly there and
18
16
  * its footage was written nowhere anything looked.
19
17
  *
20
- * With several compositions published the hint does a second job: it is the
21
- * only thing that says which slot each one is FOR. Ranked by pixels a grid's
22
- * `mid` would take the `high` slot on any grid whose `high` was not offered.
18
+ * So the single composition claims `high` EXPLICITLY (`GRID_OUTPUT_PROFILE`).
19
+ * It is also the slot this stream held before the per-profile detour, which is
20
+ * what keeps an operator's existing assignment intact.
23
21
  */
24
22
  export declare function gridStreamDescriptor(input: GridStreamDescriptorInput): CamStreamDescriptor;
@@ -1,21 +1,17 @@
1
- import { CamProfile } from '@camstack/types';
2
1
  import { GridStreamLogger, GridStreamSession } from './grid-stream-session.js';
3
2
  /**
4
- * Which grid, and WHICH COMPOSITION of it.
3
+ * Which grid. There is one composition of it, so that is the whole address.
5
4
  *
6
- * The profile is a path segment and not a query string for the reason the
7
- * restream suffixes are (`restream-intent.ts`): the choice belongs to the URL
8
- * the consumer was handed, so a broker configured once keeps asking for the
9
- * same thing. It is also REQUIRED — there is no default composition. A grid
10
- * publishes one stream per profile it can serve, and a dial that named none
11
- * would be the broker asking for "whichever", which is how a `low` slot ends
12
- * up holding a `high` composition.
5
+ * The path used to carry a profile segment, because a grid published one
6
+ * composition per profile and a dial naming none would have been the broker
7
+ * asking for "whichever". With a single output "whichever" is well defined,
8
+ * and the segment would be a choice with one option — which is not a choice,
9
+ * it is a way to spell one stream two ways.
13
10
  */
14
11
  export interface GridStreamRequest {
15
12
  readonly instanceId: string;
16
- readonly profile: CamProfile;
17
13
  }
18
- /** `/grid/<instanceId>/<profile>.flv`, or `null` when it is anything else. */
14
+ /** `/grid/<instanceId>.flv`, or `null` when it is anything else. */
19
15
  export declare function parseGridStreamPath(url: string | undefined): GridStreamRequest | null;
20
16
  export interface GridStreamRelayDeps {
21
17
  readonly logger: GridStreamLogger;
@@ -22,8 +22,8 @@ export type { GridPlanEncodeContext } from './grid-plan.js';
22
22
  export { normalizeGridRows } from './grid-row-normalization.js';
23
23
  export type { NormalizedGridRows } from './grid-row-normalization.js';
24
24
  export { gridStreamDescriptor } from './grid-stream-descriptor.js';
25
- export { GRID_CAM_STREAM_ID, GRID_PROFILES, gridCamStreamId, gridCanvasFor, offeredGridProfiles, } from './grid-profiles.js';
26
- export type { GridCanvas, GridProfileOffer } from './grid-profiles.js';
25
+ export { GRID_CAM_STREAM_ID, GRID_OUTPUT_PROFILE, gridSourceProfiles, lowestServedProfile, } from './grid-profiles.js';
26
+ export type { GridSourcePlan } from './grid-profiles.js';
27
27
  export { gridSourceDial } from './grid-sentry-sources.js';
28
28
  export type { GridSourceAcquisition, GridSourceDial } from './grid-sentry-sources.js';
29
29
  export { GridSnapshotSource, gridSnapshotAnswerFor } from './grid-snapshot.js';