@ceralive/cerastream 2026.9.1 → 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
@@ -9,12 +9,16 @@ engine's JSON-RPC 2.0 / NDJSON control plane over a Unix domain socket.
9
9
  - The nine server-push event payloads (discriminated union)
10
10
  - Two-tier error codes (RPC + runtime)
11
11
  - Typed capture-probe causes retained on runtime events and rejected-start exceptions
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
12
15
  - A unified engine config schema (= `start` params)
13
16
  - A `CerastreamClient` interface + `connect()` factory (UDS transport)
14
17
  - Additive `client.getCapabilities()` discovery for platform, source, encoder, and
15
18
  local preview availability
16
19
  - Additive `client.changeConfig()` — reconfigure the live session (resolution,
17
- 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
18
22
 
19
23
  The Zod schemas, types, and constants are the frozen wire contract; `connect()`
20
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,17 +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";
103
+ no_silicon_converter: "no_silicon_converter";
104
+ "secondary-unavailable": "secondary-unavailable";
102
105
  }>;
103
106
  export type CaptureCause = z.infer<typeof captureCauseSchema>;
104
107
  export declare const captureCauseEntrySchema: z.ZodObject<{
105
108
  device: z.ZodString;
106
109
  cause: z.ZodEnum<{
110
+ "composition-unsupported": "composition-unsupported";
107
111
  device_busy: "device_busy";
108
112
  negotiation_failed: "negotiation_failed";
109
113
  no_signal: "no_signal";
114
+ no_silicon_converter: "no_silicon_converter";
115
+ "secondary-unavailable": "secondary-unavailable";
110
116
  }>;
111
117
  }, z.core.$strip>;
112
118
  export type CaptureCauseEntry = z.infer<typeof captureCauseEntrySchema>;
package/dist/errors.js CHANGED
@@ -138,7 +138,14 @@ export const processErrorCodeSchema = z.enum([
138
138
  // would be lying. Always carries a captureUnrecoverableReasonSchema `reason`.
139
139
  'capture_unrecoverable',
140
140
  ]);
141
- export const captureCauseSchema = z.enum(['negotiation_failed', 'no_signal', 'device_busy']);
141
+ export const captureCauseSchema = z.enum([
142
+ 'negotiation_failed',
143
+ 'no_signal',
144
+ 'device_busy',
145
+ 'no_silicon_converter',
146
+ 'composition-unsupported',
147
+ 'secondary-unavailable',
148
+ ]);
142
149
  export const captureCauseEntrySchema = z.object({
143
150
  device: z.string(),
144
151
  cause: captureCauseSchema,
package/dist/events.d.ts CHANGED
@@ -240,9 +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";
247
+ no_silicon_converter: "no_silicon_converter";
248
+ "secondary-unavailable": "secondary-unavailable";
246
249
  }>>;
247
250
  }, z.core.$strip>;
248
251
  /** Payload of a {@link runtimeErrorEventSchema} event. */
@@ -463,9 +466,12 @@ export declare const eventParamsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
463
466
  reason: z.ZodOptional<z.ZodString>;
464
467
  selected: z.ZodOptional<z.ZodBoolean>;
465
468
  capture_cause: z.ZodOptional<z.ZodEnum<{
469
+ "composition-unsupported": "composition-unsupported";
466
470
  device_busy: "device_busy";
467
471
  negotiation_failed: "negotiation_failed";
468
472
  no_signal: "no_signal";
473
+ no_silicon_converter: "no_silicon_converter";
474
+ "secondary-unavailable": "secondary-unavailable";
469
475
  }>>;
470
476
  }, z.core.$strip>, z.ZodObject<{
471
477
  type: z.ZodLiteral<"preview">;
@@ -660,9 +666,12 @@ export declare const cerastreamEventSchema: z.ZodObject<{
660
666
  reason: z.ZodOptional<z.ZodString>;
661
667
  selected: z.ZodOptional<z.ZodBoolean>;
662
668
  capture_cause: z.ZodOptional<z.ZodEnum<{
669
+ "composition-unsupported": "composition-unsupported";
663
670
  device_busy: "device_busy";
664
671
  negotiation_failed: "negotiation_failed";
665
672
  no_signal: "no_signal";
673
+ no_silicon_converter: "no_silicon_converter";
674
+ "secondary-unavailable": "secondary-unavailable";
666
675
  }>>;
667
676
  }, z.core.$strip>, z.ZodObject<{
668
677
  type: z.ZodLiteral<"preview">;
@@ -866,9 +875,12 @@ export declare const eventSchemas: {
866
875
  reason: z.ZodOptional<z.ZodString>;
867
876
  selected: z.ZodOptional<z.ZodBoolean>;
868
877
  capture_cause: z.ZodOptional<z.ZodEnum<{
878
+ "composition-unsupported": "composition-unsupported";
869
879
  device_busy: "device_busy";
870
880
  negotiation_failed: "negotiation_failed";
871
881
  no_signal: "no_signal";
882
+ no_silicon_converter: "no_silicon_converter";
883
+ "secondary-unavailable": "secondary-unavailable";
872
884
  }>>;
873
885
  }, z.core.$strip>;
874
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<{
@@ -359,6 +371,12 @@ export declare const encoderCapsSchema: z.ZodObject<{
359
371
  }, z.core.$strip>;
360
372
  }, z.core.$strip>;
361
373
  export type EncoderCaps = z.infer<typeof encoderCapsSchema>;
374
+ export declare const platformConverterSchema: z.ZodEnum<{
375
+ librga: "librga";
376
+ none: "none";
377
+ "v4l2-rga2": "v4l2-rga2";
378
+ }>;
379
+ export type PlatformConverter = z.infer<typeof platformConverterSchema>;
362
380
  export declare const platformCapsSchema: z.ZodObject<{
363
381
  supports_h265: z.ZodBoolean;
364
382
  hardware_accelerated: z.ZodBoolean;
@@ -373,6 +391,11 @@ export declare const platformCapsSchema: z.ZodObject<{
373
391
  detected: "detected";
374
392
  override: "override";
375
393
  }>>;
394
+ converter: z.ZodOptional<z.ZodEnum<{
395
+ librga: "librga";
396
+ none: "none";
397
+ "v4l2-rga2": "v4l2-rga2";
398
+ }>>;
376
399
  }, z.core.$strip>;
377
400
  export type PlatformCaps = z.infer<typeof platformCapsSchema>;
378
401
  export declare const previewAvailabilitySchema: z.ZodObject<{
@@ -397,6 +420,11 @@ export declare const getCapabilitiesResultSchema: z.ZodObject<{
397
420
  detected: "detected";
398
421
  override: "override";
399
422
  }>>;
423
+ converter: z.ZodOptional<z.ZodEnum<{
424
+ librga: "librga";
425
+ none: "none";
426
+ "v4l2-rga2": "v4l2-rga2";
427
+ }>>;
400
428
  }, z.core.$strip>;
401
429
  encoder: z.ZodObject<{
402
430
  codecs: z.ZodArray<z.ZodString>;
@@ -462,6 +490,18 @@ export declare const changeConfigParamsSchema: z.ZodObject<{
462
490
  uvc_h264: "uvc_h264";
463
491
  uvc_h265: "uvc_h265";
464
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>>;
465
505
  }, z.core.$strip>;
466
506
  export type ChangeConfigParams = z.infer<typeof changeConfigParamsSchema>;
467
507
  export declare const changeConfigResultSchema: z.ZodObject<{
@@ -567,6 +607,18 @@ export declare const requestSchemas: {
567
607
  uvc_h264: "uvc_h264";
568
608
  uvc_h265: "uvc_h265";
569
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>>;
570
622
  }, z.core.$strip>;
571
623
  readonly result: z.ZodObject<{
572
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 ----
@@ -172,6 +172,7 @@ export const encoderCapsSchema = z.object({
172
172
  codecs: z.array(z.string()),
173
173
  bitrate_range: bitrateRangeCapsSchema,
174
174
  });
175
+ export const platformConverterSchema = z.enum(['librga', 'v4l2-rga2', 'none']);
175
176
  export const platformCapsSchema = z.object({
176
177
  supports_h265: z.boolean(),
177
178
  hardware_accelerated: z.boolean(),
@@ -180,6 +181,7 @@ export const platformCapsSchema = z.object({
180
181
  // Optional so a pre-field engine (which omits both) still parses.
181
182
  hardware_kind: z.enum(['rk3588', 'jetson', 'n100', 'generic']).optional(),
182
183
  source: z.enum(['detected', 'override']).optional(),
184
+ converter: platformConverterSchema.optional(),
183
185
  });
184
186
  // Preview-server availability (0.4.0, additive). Lets the UI tell an unbound /
185
187
  // port-conflicted preview (enabled:true, bound:false) from a down engine.
@@ -237,6 +239,7 @@ export const changeConfigParamsSchema = z
237
239
  // engine runs a release → re-enumeration-barrier → open transaction and
238
240
  // rolls back honestly (reason: mode_barrier_timeout) if the barrier expires.
239
241
  input_mode: captureDeviceKindSchema.optional(),
242
+ composition: compositionConfigSchema.optional(),
240
243
  })
241
244
  .refine((delta) => Object.keys(delta).length > 0, {
242
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.1",
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",