@ceralive/cerastream 2026.9.2 → 2026.9.3

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/README.md CHANGED
@@ -10,12 +10,15 @@ engine's JSON-RPC 2.0 / NDJSON control plane over a Unix domain socket.
10
10
  - Two-tier error codes (RPC + runtime)
11
11
  - Typed capture-probe causes retained on runtime events and rejected-start exceptions
12
12
  - Typed platform capture-converter reporting (`librga`, `v4l2-rga2`, or `none`)
13
+ - Typed RK3588 composition config (secondary input, six PiP/PbP layouts, alpha),
14
+ gated by the READY-trialled `composition` capability token
13
15
  - A unified engine config schema (= `start` params)
14
16
  - A `CerastreamClient` interface + `connect()` factory (UDS transport)
15
17
  - Additive `client.getCapabilities()` discovery for platform, source, encoder, and
16
18
  local preview availability
17
19
  - Additive `client.changeConfig()` — reconfigure the live session (resolution,
18
- framerate, codec, pipeline, source) as one transaction with typed rollback
20
+ framerate, codec, pipeline, source) as one transaction with typed rollback;
21
+ composition-only changes use live layout/alpha writes and secondary rebind
19
22
 
20
23
  The Zod schemas, types, and constants are the frozen wire contract; `connect()`
21
24
  drives that contract over an NDJSON/UDS transport with no native dependencies.
@@ -8,7 +8,7 @@ export declare const PROTOCOL_VERSION: 'cerastream-ipc/1';
8
8
  * additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
9
9
  * only moves when the wire schema itself does, in lockstep across both languages.
10
10
  */
11
- export declare const SCHEMA_VERSION: '0.13.0';
11
+ export declare const SCHEMA_VERSION: '0.14.0';
12
12
  /** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
13
13
  export declare const DEFAULT_IPC_DIR: '/run/cerastream';
14
14
  /** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
@@ -65,6 +65,8 @@ export declare const PROFILE_CATALOG_VERSION: '1.0.0';
65
65
  * the Rust `PIPEWIRE_CAPTURE_FEATURE`.
66
66
  */
67
67
  export declare const PIPEWIRE_CAPTURE_FEATURE: 'pipewire-capture';
68
+ /** Two-leg RK3588 composition, emitted only after `rgacompositor` passes READY. */
69
+ export declare const COMPOSITION_FEATURE: 'composition';
68
70
  /**
69
71
  * Named engine features `get-capabilities` advertises in `features`. This is the
70
72
  * fail-closed negotiation contract: CeraUI sends a new out-of-schema field ONLY
@@ -76,7 +78,7 @@ export declare const PIPEWIRE_CAPTURE_FEATURE: 'pipewire-capture';
76
78
  * array, never on membership here — a configuration-dependent token is filtered
77
79
  * out of the response the engine actually sends.
78
80
  */
79
- export declare const ENGINE_FEATURES: readonly ['video-passthrough', 'input-mode', 'audio-pcm-spec', "pipewire-capture"];
81
+ export declare const ENGINE_FEATURES: readonly ['video-passthrough', 'input-mode', 'audio-pcm-spec', "pipewire-capture", "composition"];
80
82
  /** Engine binary name (systemd-owned; CeraUI never spawns it — ADR-0005). */
81
83
  export declare const CERASTREAM_BIN: 'cerastream';
82
84
  export declare const DEFAULT_MIN_BITRATE = 300;
package/dist/constants.js CHANGED
@@ -11,7 +11,7 @@ export const PROTOCOL_VERSION = 'cerastream-ipc/1';
11
11
  * additive-only within protocol major `cerastream-ipc/1` (ADR-0002 §4); this value
12
12
  * only moves when the wire schema itself does, in lockstep across both languages.
13
13
  */
14
- export const SCHEMA_VERSION = '0.13.0';
14
+ export const SCHEMA_VERSION = '0.14.0';
15
15
  /** Runtime dir holding both control + preview sockets. systemd `RuntimeDirectory=cerastream`. */
16
16
  export const DEFAULT_IPC_DIR = '/run/cerastream';
17
17
  /** Env override for the IPC dir (tests/dev). Defaults to {@link DEFAULT_IPC_DIR}. */
@@ -74,6 +74,8 @@ export const PROFILE_CATALOG_VERSION = '1.0.0';
74
74
  * the Rust `PIPEWIRE_CAPTURE_FEATURE`.
75
75
  */
76
76
  export const PIPEWIRE_CAPTURE_FEATURE = 'pipewire-capture';
77
+ /** Two-leg RK3588 composition, emitted only after `rgacompositor` passes READY. */
78
+ export const COMPOSITION_FEATURE = 'composition';
77
79
  /**
78
80
  * Named engine features `get-capabilities` advertises in `features`. This is the
79
81
  * fail-closed negotiation contract: CeraUI sends a new out-of-schema field ONLY
@@ -90,6 +92,7 @@ export const ENGINE_FEATURES = [
90
92
  'input-mode',
91
93
  'audio-pcm-spec',
92
94
  PIPEWIRE_CAPTURE_FEATURE,
95
+ COMPOSITION_FEATURE,
93
96
  ];
94
97
  /** Engine binary name (systemd-owned; CeraUI never spawns it — ADR-0005). */
95
98
  export const CERASTREAM_BIN = 'cerastream';
package/dist/errors.d.ts CHANGED
@@ -96,19 +96,23 @@ export declare const processErrorCodeSchema: z.ZodEnum<{
96
96
  }>;
97
97
  export type ProcessErrorCode = z.infer<typeof processErrorCodeSchema>;
98
98
  export declare const captureCauseSchema: z.ZodEnum<{
99
+ "composition-unsupported": "composition-unsupported";
99
100
  device_busy: "device_busy";
100
101
  negotiation_failed: "negotiation_failed";
101
102
  no_signal: "no_signal";
102
103
  no_silicon_converter: "no_silicon_converter";
104
+ "secondary-unavailable": "secondary-unavailable";
103
105
  }>;
104
106
  export type CaptureCause = z.infer<typeof captureCauseSchema>;
105
107
  export declare const captureCauseEntrySchema: z.ZodObject<{
106
108
  device: z.ZodString;
107
109
  cause: z.ZodEnum<{
110
+ "composition-unsupported": "composition-unsupported";
108
111
  device_busy: "device_busy";
109
112
  negotiation_failed: "negotiation_failed";
110
113
  no_signal: "no_signal";
111
114
  no_silicon_converter: "no_silicon_converter";
115
+ "secondary-unavailable": "secondary-unavailable";
112
116
  }>;
113
117
  }, z.core.$strip>;
114
118
  export type CaptureCauseEntry = z.infer<typeof captureCauseEntrySchema>;
package/dist/errors.js CHANGED
@@ -143,6 +143,8 @@ export const captureCauseSchema = z.enum([
143
143
  'no_signal',
144
144
  'device_busy',
145
145
  'no_silicon_converter',
146
+ 'composition-unsupported',
147
+ 'secondary-unavailable',
146
148
  ]);
147
149
  export const captureCauseEntrySchema = z.object({
148
150
  device: z.string(),
package/dist/events.d.ts CHANGED
@@ -240,10 +240,12 @@ export declare const runtimeErrorEventSchema: z.ZodObject<{
240
240
  reason: z.ZodOptional<z.ZodString>;
241
241
  selected: z.ZodOptional<z.ZodBoolean>;
242
242
  capture_cause: z.ZodOptional<z.ZodEnum<{
243
+ "composition-unsupported": "composition-unsupported";
243
244
  device_busy: "device_busy";
244
245
  negotiation_failed: "negotiation_failed";
245
246
  no_signal: "no_signal";
246
247
  no_silicon_converter: "no_silicon_converter";
248
+ "secondary-unavailable": "secondary-unavailable";
247
249
  }>>;
248
250
  }, z.core.$strip>;
249
251
  /** Payload of a {@link runtimeErrorEventSchema} event. */
@@ -464,10 +466,12 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
464
466
  reason: z.ZodOptional<z.ZodString>;
465
467
  selected: z.ZodOptional<z.ZodBoolean>;
466
468
  capture_cause: z.ZodOptional<z.ZodEnum<{
469
+ "composition-unsupported": "composition-unsupported";
467
470
  device_busy: "device_busy";
468
471
  negotiation_failed: "negotiation_failed";
469
472
  no_signal: "no_signal";
470
473
  no_silicon_converter: "no_silicon_converter";
474
+ "secondary-unavailable": "secondary-unavailable";
471
475
  }>>;
472
476
  }, z.core.$strip>, z.ZodObject<{
473
477
  type: z.ZodLiteral<"preview">;
@@ -662,10 +666,12 @@ export declare const cerastreamEventSchema: z.ZodObject<{
662
666
  reason: z.ZodOptional<z.ZodString>;
663
667
  selected: z.ZodOptional<z.ZodBoolean>;
664
668
  capture_cause: z.ZodOptional<z.ZodEnum<{
669
+ "composition-unsupported": "composition-unsupported";
665
670
  device_busy: "device_busy";
666
671
  negotiation_failed: "negotiation_failed";
667
672
  no_signal: "no_signal";
668
673
  no_silicon_converter: "no_silicon_converter";
674
+ "secondary-unavailable": "secondary-unavailable";
669
675
  }>>;
670
676
  }, z.core.$strip>, z.ZodObject<{
671
677
  type: z.ZodLiteral<"preview">;
@@ -869,10 +875,12 @@ export declare const eventSchemas: {
869
875
  reason: z.ZodOptional<z.ZodString>;
870
876
  selected: z.ZodOptional<z.ZodBoolean>;
871
877
  capture_cause: z.ZodOptional<z.ZodEnum<{
878
+ "composition-unsupported": "composition-unsupported";
872
879
  device_busy: "device_busy";
873
880
  negotiation_failed: "negotiation_failed";
874
881
  no_signal: "no_signal";
875
882
  no_silicon_converter: "no_silicon_converter";
883
+ "secondary-unavailable": "secondary-unavailable";
876
884
  }>>;
877
885
  }, z.core.$strip>;
878
886
  readonly preview: z.ZodObject<{
@@ -56,6 +56,18 @@ export declare const startParamsSchema: z.ZodObject<{
56
56
  uvc_h264: "uvc_h264";
57
57
  uvc_h265: "uvc_h265";
58
58
  }>>;
59
+ composition: z.ZodOptional<z.ZodObject<{
60
+ secondary_input_id: z.ZodString;
61
+ layout: z.ZodEnum<{
62
+ "pbp-left-right": "pbp-left-right";
63
+ "pbp-top-bottom": "pbp-top-bottom";
64
+ "pip-bottom-left": "pip-bottom-left";
65
+ "pip-bottom-right": "pip-bottom-right";
66
+ "pip-top-left": "pip-top-left";
67
+ "pip-top-right": "pip-top-right";
68
+ }>;
69
+ alpha: z.ZodOptional<z.ZodNumber>;
70
+ }, z.core.$strip>>;
59
71
  }, z.core.$strip>;
60
72
  export type StartParams = z.infer<typeof startParamsSchema>;
61
73
  export declare const startResultSchema: z.ZodObject<{
@@ -478,6 +490,18 @@ export declare const changeConfigParamsSchema: z.ZodObject<{
478
490
  uvc_h264: "uvc_h264";
479
491
  uvc_h265: "uvc_h265";
480
492
  }>>;
493
+ composition: z.ZodOptional<z.ZodObject<{
494
+ secondary_input_id: z.ZodString;
495
+ layout: z.ZodEnum<{
496
+ "pbp-left-right": "pbp-left-right";
497
+ "pbp-top-bottom": "pbp-top-bottom";
498
+ "pip-bottom-left": "pip-bottom-left";
499
+ "pip-bottom-right": "pip-bottom-right";
500
+ "pip-top-left": "pip-top-left";
501
+ "pip-top-right": "pip-top-right";
502
+ }>;
503
+ alpha: z.ZodOptional<z.ZodNumber>;
504
+ }, z.core.$strip>>;
481
505
  }, z.core.$strip>;
482
506
  export type ChangeConfigParams = z.infer<typeof changeConfigParamsSchema>;
483
507
  export declare const changeConfigResultSchema: z.ZodObject<{
@@ -583,6 +607,18 @@ export declare const requestSchemas: {
583
607
  uvc_h264: "uvc_h264";
584
608
  uvc_h265: "uvc_h265";
585
609
  }>>;
610
+ composition: z.ZodOptional<z.ZodObject<{
611
+ secondary_input_id: z.ZodString;
612
+ layout: z.ZodEnum<{
613
+ "pbp-left-right": "pbp-left-right";
614
+ "pbp-top-bottom": "pbp-top-bottom";
615
+ "pip-bottom-left": "pip-bottom-left";
616
+ "pip-bottom-right": "pip-bottom-right";
617
+ "pip-top-left": "pip-top-left";
618
+ "pip-top-right": "pip-top-right";
619
+ }>;
620
+ alpha: z.ZodOptional<z.ZodNumber>;
621
+ }, z.core.$strip>>;
586
622
  }, z.core.$strip>;
587
623
  readonly result: z.ZodObject<{
588
624
  session_id: z.ZodString;
package/dist/messages.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { AUDIO_DELAY_MAX_MS } from './constants.js';
3
3
  import { helloParamsSchema, helloResultSchema } from './envelope.js';
4
- import { audioBackendSchema, balancerAlgorithmSchema, bitrateControlSchema, captureDeviceKindSchema, captureDeviceSchema, cerastreamConfigSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewEncodeModeSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } from './types.js';
4
+ import { audioBackendSchema, balancerAlgorithmSchema, bitrateControlSchema, captureDeviceKindSchema, captureDeviceSchema, cerastreamConfigSchema, compositionConfigSchema, configChangePhaseSchema, inputModeSchema, mediaClassSchema, previewEncodeModeSchema, previewTierSchema, streamStateSchema, videoCodecSchema, } from './types.js';
5
5
  // The eight v1 control methods (schema.md "v1 messages"). Each is matched
6
6
  // byte-for-byte to schema.md: the JSON shape on the wire is the contract.
7
7
  // ---- 1. start ----
@@ -239,6 +239,7 @@ export const changeConfigParamsSchema = z
239
239
  // engine runs a release → re-enumeration-barrier → open transaction and
240
240
  // rolls back honestly (reason: mode_barrier_timeout) if the barrier expires.
241
241
  input_mode: captureDeviceKindSchema.optional(),
242
+ composition: compositionConfigSchema.optional(),
242
243
  })
243
244
  .refine((delta) => Object.keys(delta).length > 0, {
244
245
  message: 'change-config delta must carry at least one field',
package/dist/types.d.ts CHANGED
@@ -50,6 +50,30 @@ export declare const videoPassthroughSchema: z.ZodEnum<{
50
50
  off: "off";
51
51
  }>;
52
52
  export type VideoPassthrough = z.infer<typeof videoPassthroughSchema>;
53
+ /** Exact preset nicks accepted by the first-party `rgacompositor` element. */
54
+ export declare const compositionLayoutSchema: z.ZodEnum<{
55
+ "pbp-left-right": "pbp-left-right";
56
+ "pbp-top-bottom": "pbp-top-bottom";
57
+ "pip-bottom-left": "pip-bottom-left";
58
+ "pip-bottom-right": "pip-bottom-right";
59
+ "pip-top-left": "pip-top-left";
60
+ "pip-top-right": "pip-top-right";
61
+ }>;
62
+ export type CompositionLayout = z.infer<typeof compositionLayoutSchema>;
63
+ /** One secondary capture leg composited with the primary session input. */
64
+ export declare const compositionConfigSchema: z.ZodObject<{
65
+ secondary_input_id: z.ZodString;
66
+ layout: z.ZodEnum<{
67
+ "pbp-left-right": "pbp-left-right";
68
+ "pbp-top-bottom": "pbp-top-bottom";
69
+ "pip-bottom-left": "pip-bottom-left";
70
+ "pip-bottom-right": "pip-bottom-right";
71
+ "pip-top-left": "pip-top-left";
72
+ "pip-top-right": "pip-top-right";
73
+ }>;
74
+ alpha: z.ZodOptional<z.ZodNumber>;
75
+ }, z.core.$strip>;
76
+ export type CompositionConfig = z.infer<typeof compositionConfigSchema>;
53
77
  export declare const bitrateControlSchema: z.ZodEnum<{
54
78
  encoder: "encoder";
55
79
  "source-fixed": "source-fixed";
@@ -199,6 +223,18 @@ export declare const cerastreamConfigSchema: z.ZodObject<{
199
223
  uvc_h264: "uvc_h264";
200
224
  uvc_h265: "uvc_h265";
201
225
  }>>;
226
+ composition: z.ZodOptional<z.ZodObject<{
227
+ secondary_input_id: z.ZodString;
228
+ layout: z.ZodEnum<{
229
+ "pbp-left-right": "pbp-left-right";
230
+ "pbp-top-bottom": "pbp-top-bottom";
231
+ "pip-bottom-left": "pip-bottom-left";
232
+ "pip-bottom-right": "pip-bottom-right";
233
+ "pip-top-left": "pip-top-left";
234
+ "pip-top-right": "pip-top-right";
235
+ }>;
236
+ alpha: z.ZodOptional<z.ZodNumber>;
237
+ }, z.core.$strip>>;
202
238
  }, z.core.$strip>;
203
239
  export type CerastreamConfig = z.infer<typeof cerastreamConfigSchema>;
204
240
  export type PartialCerastreamConfig = z.input<typeof cerastreamConfigSchema>;
package/dist/types.js CHANGED
@@ -34,6 +34,21 @@ export const videoCodecSchema = z.enum(['h264', 'h265']);
34
34
  // "force" = passthrough whenever eligible (typed start failure otherwise);
35
35
  // "off" = always transcode. See schema.md and the engine's layer-C matrix.
36
36
  export const videoPassthroughSchema = z.enum(['auto', 'force', 'off']);
37
+ /** Exact preset nicks accepted by the first-party `rgacompositor` element. */
38
+ export const compositionLayoutSchema = z.enum([
39
+ 'pip-top-right',
40
+ 'pip-top-left',
41
+ 'pip-bottom-right',
42
+ 'pip-bottom-left',
43
+ 'pbp-left-right',
44
+ 'pbp-top-bottom',
45
+ ]);
46
+ /** One secondary capture leg composited with the primary session input. */
47
+ export const compositionConfigSchema = z.object({
48
+ secondary_input_id: z.string().min(1),
49
+ layout: compositionLayoutSchema,
50
+ alpha: z.number().min(0).max(1).optional(),
51
+ });
37
52
  // Which surface owns the running bitrate, reported additively on a set-bitrate /
38
53
  // reload-config response (0.5.0). "encoder" = applied; "source-fixed" = a
39
54
  // passthrough graph fixes the bitrate at the source, so the request was NOT
@@ -138,6 +153,7 @@ export const cerastreamConfigSchema = z.object({
138
153
  audio: audioConfigSchema.optional(), // additive (0.4.0): audio device/codec/signed delay
139
154
  video_passthrough: videoPassthroughSchema.optional(), // additive (0.5.0): auto|force|off; absent ⇒ auto
140
155
  input_mode: captureDeviceKindSchema.optional(), // additive (0.11.0): which MODE family of the selected device to capture in, naming a list-devices modes[].pipeline_kind. Absent ⇒ the engine's precedence pick (h265 → h264 → mjpeg → raw). The ONLY way to reach the non-precedence family of a dual-format camera; a family the device does not expose is a typed cerastream.params.invalid, never a silent fallback
156
+ composition: compositionConfigSchema.optional(),
141
157
  });
142
158
  // convenience defaults for building a config client-side
143
159
  export const DEFAULT_BITRATE_CONFIG = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceralive/cerastream",
3
- "version": "2026.9.2",
3
+ "version": "2026.9.3",
4
4
  "description": "Type-safe TypeScript schema + IPC client for the cerastream streaming engine (JSON-RPC 2.0 / NDJSON over a Unix domain socket).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",