@remavideo/sdk 0.9.0

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.
@@ -0,0 +1,1020 @@
1
+ import { HealthStatus, NodeState, ActivationGate, TrackInfo, NodeEventMsg, MonitorEventMsg, WorkflowInfo } from '@remavideo/server';
2
+ export { AppRouter, HealthStatus, MonitorDescriptorMsg, MonitorEventMsg, NodeEventKind, NodeNotificationKind, NodeState, PipelineSnapshot, TrackInfo, VisEdge, VisNode, WorkflowInfo, WorkflowResourceUsageMsg } from '@remavideo/server';
3
+ import { createTRPCClient } from '@trpc/client';
4
+
5
+ interface RemaClientOptions {
6
+ host?: string;
7
+ port?: number;
8
+ onLogEvent?: (log: {
9
+ level: string;
10
+ message: string;
11
+ }) => void;
12
+ onReady?: () => void;
13
+ onFailedToConnect?: () => void;
14
+ }
15
+ declare class RemaClient {
16
+ readonly trpc: ReturnType<typeof createTRPCClient<AppRouter>>;
17
+ readonly options: Required<Pick<RemaClientOptions, "host" | "port">>;
18
+ private constructor();
19
+ static connect(opts?: RemaClientOptions): Promise<RemaClient>;
20
+ /** No-op kept for API compatibility — SSE has no persistent connection to close. */
21
+ close(): void;
22
+ /** Fetches the server's health status from the plain HTTP `/api/health` endpoint. */
23
+ health(): Promise<HealthStatus>;
24
+ }
25
+
26
+ /** Lifecycle callbacks available on every node's settings object. */
27
+ interface NodeCallbacks {
28
+ /** Called when the node transitions into the `ERROR` state. */
29
+ onError?: (err: Error) => void;
30
+ /** Called on every lifecycle state transition (`READY`, `ACTIVE`, `ENDED`, `ERROR`, `CLOSED`). */
31
+ onStatusChange?: (state: NodeState, message?: string) => void;
32
+ /** Called when the node closes — locally via `.close()`, or remotely (workflow close, crash). */
33
+ onClose?: () => void;
34
+ }
35
+ /**
36
+ * FFmpeg codec specification for one stream of a subscription.
37
+ *
38
+ * The receiving output node applies it where appropriate (e.g. per-rendition
39
+ * `-c:v:N`/`-c:a:N` for HLS, `-c:v`/`-c:a` for RTMP/File outputs). Omitted =
40
+ * stream copy.
41
+ */
42
+ interface CodecSpec {
43
+ /** FFmpeg encoder name: `"copy"`, `"aac"`, `"libx264"`, … */
44
+ name: string;
45
+ /**
46
+ * Extra encoder options keyed by bare FFmpeg option name (no stream
47
+ * specifier), e.g. `{ preset: "veryfast", b: "3M" }`.
48
+ */
49
+ options?: Record<string, string | number>;
50
+ }
51
+ /**
52
+ * Audio rendition metadata for HLS output nodes. Sets the language tag and
53
+ * label that appear in the player's track menu. Ignored by non-HLS nodes.
54
+ */
55
+ interface AudioRenditionOptions {
56
+ /** BCP-47 language tag, e.g. `"en"`, `"fr"`. Default: `"und"`. */
57
+ language?: string;
58
+ /** Label shown in player track menus. Default: `"Audio"`. */
59
+ label?: string;
60
+ /** Pre-select this track on first play. Default: `false`. */
61
+ defaultTrack?: boolean;
62
+ }
63
+ /**
64
+ * Video rendition metadata for HLS output nodes. Ignored by non-HLS nodes.
65
+ */
66
+ interface VideoRenditionOptions {
67
+ /**
68
+ * Label used to name the HLS variant playlist/segments (sanitized to a
69
+ * URL-safe variant name).
70
+ */
71
+ label?: string;
72
+ }
73
+ /** Fields shared by every subscription variant. */
74
+ interface SubscriptionSpecBase {
75
+ source: BaseNode;
76
+ /**
77
+ * Delay this stream by N milliseconds before it reaches the subscribing
78
+ * node (applied server-side). Typical use: delay the A/V subscriptions so
79
+ * a slower caption source has lead time.
80
+ */
81
+ delayMs?: number;
82
+ /**
83
+ * Read from a keyed output port of a multi-output source node (Gate).
84
+ * Ports are named by the `name` field of the specs the gate itself was
85
+ * subscribed with. Mutually exclusive with `track`.
86
+ */
87
+ port?: string;
88
+ /**
89
+ * When the *subscribing* node is a multi-output node (Gate), names the
90
+ * pass-through port it creates for this source (default: the source's
91
+ * index as a string). Ignored by other node types.
92
+ */
93
+ name?: string;
94
+ }
95
+ /** Subscribe to a video-only stream of a source node. */
96
+ interface VideoSubscriptionSpec extends SubscriptionSpecBase {
97
+ sourceSelector: "VIDEO";
98
+ /**
99
+ * Per-type video track index within the source container (0 = first video
100
+ * track). Discover available indices with `await source.tracks()`.
101
+ * Default: 0.
102
+ */
103
+ track?: number;
104
+ /** Codec applied to the video stream by the receiving output node. */
105
+ codec?: CodecSpec;
106
+ /** Video rendition metadata (HLS). */
107
+ video?: VideoRenditionOptions;
108
+ }
109
+ /** Subscribe to an audio-only stream of a source node. */
110
+ interface AudioSubscriptionSpec extends SubscriptionSpecBase {
111
+ sourceSelector: "AUDIO";
112
+ /**
113
+ * Per-type audio track index within the source container (0 = first audio
114
+ * track). Discover available indices with `await source.tracks()`.
115
+ * Default: 0.
116
+ */
117
+ track?: number;
118
+ /** Codec applied to the audio stream by the receiving output node. */
119
+ codec?: CodecSpec;
120
+ /** Audio rendition metadata (HLS). */
121
+ audio?: AudioRenditionOptions;
122
+ }
123
+ /** Subscribe to the combined A/V stream of a source node. */
124
+ interface AllSubscriptionSpec extends SubscriptionSpecBase {
125
+ sourceSelector: "ALL";
126
+ /** Codec applied to the video track by the receiving output node. */
127
+ videoCodec?: CodecSpec;
128
+ /** Codec applied to the audio track by the receiving output node. */
129
+ audioCodec?: CodecSpec;
130
+ /** Video rendition metadata (HLS). */
131
+ video?: VideoRenditionOptions;
132
+ /** Audio rendition metadata (HLS). */
133
+ audio?: AudioRenditionOptions;
134
+ }
135
+ /** Subscribe to the caption stream of a source node. */
136
+ interface CaptionsSubscriptionSpec extends SubscriptionSpecBase {
137
+ sourceSelector: "CAPTIONS";
138
+ }
139
+ /**
140
+ * One source wired into a subscribing node, discriminated by
141
+ * `sourceSelector` so per-stream options (codec, rendition metadata) are
142
+ * only available where they apply.
143
+ */
144
+ type SubscriptionSpec = VideoSubscriptionSpec | AudioSubscriptionSpec | AllSubscriptionSpec | CaptionsSubscriptionSpec;
145
+ /**
146
+ * Minimal interface passed to factory functions. Defined here (not in workflow.ts)
147
+ * to avoid a circular import: workflow.ts imports factory files, factory files
148
+ * import this interface.
149
+ */
150
+ interface WorkflowHandle {
151
+ readonly workflowId: string;
152
+ readonly client: RemaClient;
153
+ _onNodeEvent(nodeId: string, handler: (evt: NodeEventMsg) => void): () => void;
154
+ }
155
+ /**
156
+ * Base class for all rema nodes.
157
+ * Provides the .subscribe() method that wires nodes together on the server.
158
+ */
159
+ declare abstract class BaseNode {
160
+ readonly nodeId: string;
161
+ readonly workflowId: string;
162
+ protected readonly client: RemaClient;
163
+ private _eventCleanup;
164
+ private _callbacks;
165
+ constructor(nodeId: string, client: RemaClient, workflowId: string);
166
+ /** Called by create functions to register the workflow event handler cleanup. */
167
+ _setEventCleanup(fn: () => void): void;
168
+ /**
169
+ * Called by create functions to register the node's lifecycle callbacks
170
+ * (`onError`/`onStatusChange`/`onClose`), so `.close()` can invoke `onClose`
171
+ * directly on a locally-initiated close.
172
+ */
173
+ _bindCallbacks(callbacks: NodeCallbacks): void;
174
+ /**
175
+ * Subscribe this node to one or more source nodes.
176
+ * This tells the server to route streams from the sources into this node.
177
+ */
178
+ subscribe(specs: SubscriptionSpec[], options?: {
179
+ gate?: ActivationGate;
180
+ }): Promise<void>;
181
+ /** Map one `SubscriptionSpec` to the wire shape expected by the server. */
182
+ private static _toSourceInput;
183
+ /**
184
+ * Probe this node's media stream (ffprobe, server-side) and return its
185
+ * track list. Each entry's `track` is the per-type index to pass back as
186
+ * `track` in a VIDEO/AUDIO subscription:
187
+ *
188
+ * ```ts
189
+ * const tracks = await input.tracks();
190
+ * const ita = tracks.find((t) => t.type === "audio" && t.language === "ita");
191
+ * await output.subscribe([
192
+ * { source: input, sourceSelector: "VIDEO" },
193
+ * { source: input, sourceSelector: "AUDIO", track: ita!.track },
194
+ * ]);
195
+ * ```
196
+ *
197
+ * Probing consumes real-time-paced stream data, so it can take several
198
+ * seconds; `probeSizeBytes` (default 1 MB) trades discovery depth for
199
+ * latency. The server errors after ~15 s if the source produces no data
200
+ * (e.g. no publisher connected yet, or a disabled file input).
201
+ */
202
+ tracks(options?: {
203
+ probeSizeBytes?: number;
204
+ }): Promise<TrackInfo[]>;
205
+ /** Tear down this node on the server. */
206
+ close(): Promise<void>;
207
+ }
208
+
209
+ interface CaptionSourceSettings extends NodeCallbacks {
210
+ /**
211
+ * How many past caption entries to replay to an encoder that subscribes
212
+ * after some captions have already been sent.
213
+ *
214
+ * - `0` (default) — no replay; each encoder only receives captions sent
215
+ * after it connects. Correct for live captioning where old text is stale.
216
+ * - `Infinity` — replay the full history. Use this when pre-scheduling
217
+ * all captions upfront (e.g. from an SRT file) before the pipeline starts,
218
+ * so encoders that activate later still see every entry.
219
+ * - `N` — replay the last N entries only.
220
+ */
221
+ replayCount?: number;
222
+ /**
223
+ * BCP-47 language tag written into the HLS master playlist when this caption
224
+ * source is wired as a subtitle track. Default: "und".
225
+ */
226
+ language?: string;
227
+ /** Human-readable track label shown in player subtitle menus. Default: "Subtitles". */
228
+ label?: string;
229
+ /** Whether this track is pre-selected when the player first loads. Default: true. */
230
+ defaultTrack?: boolean;
231
+ /**
232
+ * Maximum character length per caption line. When set, captions whose text
233
+ * exceeds this length are automatically split into sequential chunks on the
234
+ * server. Each chunk's display duration is proportional to its character
235
+ * count relative to the original caption's total duration.
236
+ */
237
+ maxLineLength?: number;
238
+ }
239
+ declare class CaptionSourceNode extends BaseNode {
240
+ readonly settings: CaptionSourceSettings;
241
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: CaptionSourceSettings);
242
+ /**
243
+ * Queue caption text for injection into the video stream.
244
+ *
245
+ * @param options.startAt Stream-elapsed milliseconds (relative to the
246
+ * first video frame received by the server) at which
247
+ * to show the caption. The server holds the text and
248
+ * injects it at the matching PTS, so you can schedule
249
+ * captions before that point in the stream is reached.
250
+ * Omit (or 0) to inject immediately at the next frame.
251
+ * @param options.duration How long (ms) the caption stays visible before
252
+ * being automatically erased. Required.
253
+ */
254
+ send(text: string, options: {
255
+ duration: number;
256
+ startAt?: number;
257
+ }): Promise<void>;
258
+ /**
259
+ * Parse raw SRT or WebVTT content and schedule all entries on the server.
260
+ *
261
+ * Use `replayCount: Infinity` when creating this node if the pipeline may
262
+ * connect encoders after the captions are loaded.
263
+ *
264
+ * @returns The number of caption entries loaded.
265
+ */
266
+ sendFile(content: string): Promise<{
267
+ count: number;
268
+ }>;
269
+ /**
270
+ * Read a local SRT or WebVTT file and schedule all its entries on the server.
271
+ * The file is read client-side; only the content string is sent over the wire.
272
+ *
273
+ * @param filePath Absolute or relative path to the subtitle file.
274
+ * @returns The number of caption entries loaded.
275
+ */
276
+ sendFileByPath(filePath: string): Promise<{
277
+ count: number;
278
+ }>;
279
+ }
280
+
281
+ interface FileInputSettings extends NodeCallbacks {
282
+ /** Absolute path to the media file (MP4, TS, MKV, …) */
283
+ fileName: string;
284
+ /** Loop the file indefinitely. Default: false */
285
+ loop?: boolean;
286
+ /** Source name used to identify streams from this node */
287
+ sourceName?: string;
288
+ /**
289
+ * Start in a disabled state — the file is not read until `.enable()` is called
290
+ * (or the node is enabled from the web UI). Default: false.
291
+ */
292
+ disabled?: boolean;
293
+ /** Called when the node transitions to the enabled / ready state */
294
+ onReady?: () => void;
295
+ /**
296
+ * Called once when the first data packet from this input arrives at the server.
297
+ * Use this to convert absolute timestamps to the stream-relative `startAt`
298
+ * expected by `CaptionSourceNode.send()`:
299
+ *
300
+ * ```ts
301
+ * onStreamStart(wallClockMs) {
302
+ * const startAt = myAbsoluteCaptionMs - wallClockMs;
303
+ * await captions.send(text, { startAt });
304
+ * }
305
+ * ```
306
+ *
307
+ * @param wallClockMs `Date.now()` on the server at the moment the first
308
+ * MPEG-TS data packet arrived from this input.
309
+ */
310
+ onStreamStart?: (wallClockMs: number) => void;
311
+ /** Called once the file has finished playing (reaches EOF). Never fires when `loop` is true. */
312
+ onEnded?: () => void;
313
+ }
314
+ declare class FileInputNode extends BaseNode {
315
+ readonly settings: FileInputSettings;
316
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: FileInputSettings);
317
+ /** Enable this node — starts reading the file and streaming to downstream nodes. */
318
+ enable(): Promise<void>;
319
+ }
320
+
321
+ interface NodeStreamInputSettings extends NodeCallbacks {
322
+ /**
323
+ * FFmpeg format identifier for the incoming audio data.
324
+ *
325
+ * Raw PCM examples: `"s16le"` (16-bit signed LE), `"f32le"` (32-bit float LE).
326
+ * Encoded examples: `"wav"`, `"mp3"`, `"aac"`, `"flac"`.
327
+ */
328
+ format: string;
329
+ /** Sample rate in Hz. Required when `format` is a raw PCM format. */
330
+ sampleRate?: number;
331
+ /** Number of audio channels. Required when `format` is a raw PCM format. */
332
+ channels?: number;
333
+ /**
334
+ * FFmpeg codec for the MPEG-TS output. Defaults to `"aac"`, which is safe
335
+ * for any input format. Use `"copy"` only when the input is already
336
+ * MPEG-TS-compatible AAC.
337
+ */
338
+ audioCodec?: string;
339
+ /** Human-readable label shown in the dashboard. */
340
+ label?: string;
341
+ /** Called when the node transitions to the ready state on the server. */
342
+ onReady?: () => void;
343
+ /**
344
+ * Called once when the first audio packet is written to the server.
345
+ * The argument is `Date.now()` on the server at that moment.
346
+ */
347
+ onStreamStart?: (wallClockMs: number) => void;
348
+ /** Called once the underlying FFmpeg process exits cleanly after `end()`. */
349
+ onEnded?: () => void;
350
+ }
351
+ declare class NodeStreamInputNode extends BaseNode {
352
+ readonly settings: NodeStreamInputSettings;
353
+ private readonly _apiBase;
354
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: NodeStreamInputSettings);
355
+ /**
356
+ * Push an audio stream to the server.
357
+ *
358
+ * Accepts any `AsyncIterable<Buffer | Uint8Array>`, which includes Node.js
359
+ * `Readable` streams (async-iterable since Node 10) and async generators.
360
+ * The call resolves when the source iterable is exhausted or the server
361
+ * closes the connection.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * import { createReadStream } from "node:fs";
366
+ * await audioInput.stream(createReadStream("audio.mp3"));
367
+ * ```
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * // Raw PCM from a microphone via node-record-lpcm16
372
+ * const mic = recorder.start({ sampleRate: 16000, channels: 1 });
373
+ * await audioInput.stream(mic);
374
+ * ```
375
+ */
376
+ stream(source: AsyncIterable<Buffer | Uint8Array>): Promise<void>;
377
+ }
378
+ declare function createNodeStreamInputNode(workflow: WorkflowHandle, settings: NodeStreamInputSettings, nodeId?: string): Promise<NodeStreamInputNode>;
379
+
380
+ /** Connection info passed to `onConnect` and `onDisconnect`. */
381
+ interface RtmpConnectionInfo {
382
+ app: string;
383
+ sourceName: string;
384
+ /**
385
+ * MediaMTX connection id for this publisher, if it could be resolved at
386
+ * connect time. Pass directly to `operations.disconnectSource`.
387
+ */
388
+ connectionId?: string;
389
+ }
390
+ /** Operations available on a live RTMP publisher connection. */
391
+ interface RtmpAnnouncerOperations {
392
+ /**
393
+ * Forcibly disconnect the connection identified by `connectionId`. Safe to
394
+ * call even if it already disconnected (resolves as a no-op).
395
+ */
396
+ disconnectSource(connectionId: string): Promise<void>;
397
+ }
398
+ interface RtmpAnnouncerSettings extends NodeCallbacks {
399
+ /** RTMP app name to watch (default: "live"). */
400
+ app?: string;
401
+ /**
402
+ * Called when a publisher connects. Create nodes in any workflow from
403
+ * here, or call `operations.disconnectSource(info.connectionId)` to kick
404
+ * the publisher synchronously (e.g. to reject an invalid `sourceName`).
405
+ */
406
+ onConnect: (info: RtmpConnectionInfo, operations: RtmpAnnouncerOperations) => void;
407
+ /** Called when a publisher disconnects. */
408
+ onDisconnect?: (info: RtmpConnectionInfo) => void;
409
+ }
410
+ /** SDK handle for an RTMP announcer node. */
411
+ declare class RtmpAnnouncerNode extends BaseNode {
412
+ /**
413
+ * Forcibly disconnect the connection identified by `connectionId`. Can be
414
+ * called at any time after the node is created — e.g. from an async
415
+ * moderation decision made after `onConnect` already returned.
416
+ */
417
+ disconnectSource(connectionId: string): Promise<void>;
418
+ }
419
+
420
+ /**
421
+ * Represents a single active RTMP stream created by an `RtmpMultiplexerNode`.
422
+ *
423
+ * The server creates this node automatically when a publisher connects. The SDK
424
+ * wraps the server-side node ID so it can be passed as a source to downstream
425
+ * nodes (encoders, outputs) via `subscribe()`.
426
+ */
427
+ declare class RtmpStreamNode extends BaseNode {
428
+ readonly app: string;
429
+ readonly sourceName: string;
430
+ constructor(nodeId: string, client: RemaClient, app: string, sourceName: string, workflowId: string);
431
+ }
432
+ /**
433
+ * SDK handle for an RTMP multiplexer node.
434
+ *
435
+ * Calling `close()` deactivates the multiplexer and all of its active stream
436
+ * nodes on the server.
437
+ */
438
+ declare class RtmpMultiplexerNode extends BaseNode {
439
+ }
440
+ interface RtmpMultiplexerSettings extends NodeCallbacks {
441
+ /** RTMP app name to listen on (default: "live"). */
442
+ app?: string;
443
+ /**
444
+ * Called when a publisher connects. Return `{ accept: false }` to kick the
445
+ * connection immediately; return `{ accept: true }` or omit the return value
446
+ * to accept and trigger `onStream`.
447
+ */
448
+ onConnect?: (info: {
449
+ app: string;
450
+ sourceName: string;
451
+ }) => Promise<{
452
+ accept: boolean;
453
+ } | undefined> | {
454
+ accept: boolean;
455
+ } | undefined;
456
+ /**
457
+ * Called with the per-publisher `RtmpStreamNode` when a new RTMP stream is
458
+ * accepted. Create your downstream pipeline (encoders, outputs) from this node.
459
+ */
460
+ onStream: (node: RtmpStreamNode) => Promise<void> | void;
461
+ /**
462
+ * Called when a publisher disconnects or is rejected. Use this to close any
463
+ * downstream nodes you created in `onStream`.
464
+ */
465
+ onStreamEnd?: (node: RtmpStreamNode) => Promise<void> | void;
466
+ }
467
+
468
+ interface RtmpReaderSettings extends NodeCallbacks {
469
+ /** RTMP app name. */
470
+ app: string;
471
+ /** Source name (stream key). */
472
+ sourceName: string;
473
+ /** Called when an RTMP publisher connects. */
474
+ onPublish?: (info: {
475
+ app: string;
476
+ internalUrl: string;
477
+ }) => void;
478
+ /**
479
+ * Called once when the first data packet from this input arrives at the server.
480
+ * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.
481
+ */
482
+ onStreamStart?: (wallClockMs: number) => void;
483
+ /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */
484
+ onStreamEnded?: () => void;
485
+ }
486
+ declare class RtmpReaderNode extends BaseNode {
487
+ readonly settings: RtmpReaderSettings;
488
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: RtmpReaderSettings);
489
+ }
490
+
491
+ /** Connection info passed to `onConnect` and `onDisconnect`. */
492
+ interface SrtConnectionInfo {
493
+ app: string;
494
+ sourceName: string;
495
+ /** Passphrase configured on the announcer, if any. */
496
+ passphrase?: string;
497
+ /** Negotiated SRT latency in milliseconds, if configured. */
498
+ latencyMs?: number;
499
+ /**
500
+ * MediaMTX connection id for this publisher, if it could be resolved at
501
+ * connect time. Pass directly to `operations.disconnectSource`.
502
+ */
503
+ connectionId?: string;
504
+ }
505
+ /** Operations available on a live SRT publisher connection. */
506
+ interface SrtAnnouncerOperations {
507
+ /**
508
+ * Forcibly disconnect the connection identified by `connectionId`. Safe to
509
+ * call even if it already disconnected (resolves as a no-op).
510
+ */
511
+ disconnectSource(connectionId: string): Promise<void>;
512
+ }
513
+ interface SrtAnnouncerSettings extends NodeCallbacks {
514
+ /** SRT app name to watch (default: "live"). */
515
+ app?: string;
516
+ /** Passphrase to include in connection events (mirrors MediaMTX path config). */
517
+ passphrase?: string;
518
+ /** SRT latency in milliseconds to include in connection events. */
519
+ latencyMs?: number;
520
+ /**
521
+ * Called when a publisher connects. Create nodes in any workflow from
522
+ * here, or call `operations.disconnectSource(info.connectionId)` to kick
523
+ * the publisher synchronously (e.g. to reject an invalid `sourceName`).
524
+ */
525
+ onConnect: (info: SrtConnectionInfo, operations: SrtAnnouncerOperations) => void;
526
+ /** Called when a publisher disconnects. */
527
+ onDisconnect?: (info: Pick<SrtConnectionInfo, "app" | "sourceName">) => void;
528
+ }
529
+ /** SDK handle for an SRT announcer node. */
530
+ declare class SrtAnnouncerNode extends BaseNode {
531
+ /**
532
+ * Forcibly disconnect the connection identified by `connectionId`. Can be
533
+ * called at any time after the node is created — e.g. from an async
534
+ * moderation decision made after `onConnect` already returned.
535
+ */
536
+ disconnectSource(connectionId: string): Promise<void>;
537
+ }
538
+
539
+ interface SrtReaderSettings extends NodeCallbacks {
540
+ /** SRT app name. */
541
+ app: string;
542
+ /** Source name (stream key / stream ID suffix). */
543
+ sourceName: string;
544
+ /** Called when an SRT publisher connects and the stream is ready to read. */
545
+ onPublish?: (info: {
546
+ app: string;
547
+ internalUrl: string;
548
+ }) => void;
549
+ /**
550
+ * Called once when the first data packet from this input arrives at the server.
551
+ * @param wallClockMs `Date.now()` on the server at the moment the first MPEG-TS packet arrived.
552
+ */
553
+ onStreamStart?: (wallClockMs: number) => void;
554
+ /** Called when the publisher disconnects (the underlying FFmpeg pull exits). */
555
+ onStreamEnded?: () => void;
556
+ }
557
+ /** SDK handle for an SRT reader node. */
558
+ declare class SrtReaderNode extends BaseNode {
559
+ readonly settings: SrtReaderSettings;
560
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: SrtReaderSettings);
561
+ }
562
+
563
+ interface FileOutputSettings extends NodeCallbacks {
564
+ /** Absolute path for the output file */
565
+ fileName: string;
566
+ /** Container format: "mp4" | "ts" | "mkv". Default: inferred from extension */
567
+ format?: string;
568
+ /** Overwrite `fileName` if it already exists. Default: `true`. */
569
+ overwrite?: boolean;
570
+ }
571
+ declare class FileOutputNode extends BaseNode {
572
+ readonly settings: FileOutputSettings;
573
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: FileOutputSettings);
574
+ }
575
+
576
+ type HlsDestination = {
577
+ type: "file";
578
+ path: string;
579
+ } | {
580
+ type: "s3";
581
+ bucket: string;
582
+ region: string;
583
+ /** S3-compatible endpoint URL (e.g. Tigris, MinIO, Cloudflare R2). */
584
+ endpoint?: string;
585
+ /** Key prefix prepended to all uploaded paths. */
586
+ keyPrefix?: string;
587
+ /**
588
+ * When true (default), a per-session timestamp suffix is appended to
589
+ * `keyPrefix` so that browser-cached segments from a previous session are
590
+ * never served for a new one. Set to false when the caller already
591
+ * generates a unique prefix or when sub-paths are not supported.
592
+ */
593
+ makePrefixUnique?: boolean;
594
+ accessKeyId: string;
595
+ secretAccessKey: string;
596
+ } | {
597
+ type: "http";
598
+ /** Base URL that receives PUT/DELETE requests for each segment and playlist. */
599
+ baseUrl: string;
600
+ /** Extra headers sent with every PUT/DELETE request. */
601
+ headers?: Record<string, string>;
602
+ };
603
+ interface HlsOutputSettings extends NodeCallbacks {
604
+ /** One or more destinations to fan HLS output to. */
605
+ destinations: HlsDestination[];
606
+ /** Target segment duration in seconds. Default: 6 */
607
+ segmentTime?: number;
608
+ /**
609
+ * Number of segments kept in the live playlist window.
610
+ * 0 = keep all (VOD). Default: 5.
611
+ */
612
+ listSize?: number;
613
+ /**
614
+ * Node-level fallback audio codec, applied to audio streams whose
615
+ * subscription does not specify its own `codec`. Omitted (or `copy`)
616
+ * preserves the input audio codec — except when more than one audio source
617
+ * is connected, where the default becomes `aac` so all alternate-audio
618
+ * renditions share one codec (required by hls.js).
619
+ *
620
+ * For a caption-readiness buffer (the old `avDelayMs`), put `delayMs` on
621
+ * each media subscription instead and leave caption subscriptions
622
+ * undelayed.
623
+ */
624
+ audioCodec?: "copy" | string | null;
625
+ /**
626
+ * Node-level fallback video codec, applied to video streams whose
627
+ * subscription does not specify its own `codec`. Omitted (or `copy`)
628
+ * preserves the input video codec.
629
+ */
630
+ videoCodec?: "copy" | string | null;
631
+ }
632
+ declare class HlsOutputNode extends BaseNode {
633
+ readonly settings: HlsOutputSettings;
634
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: HlsOutputSettings);
635
+ /**
636
+ * Return the effective base path or URL for each configured destination in
637
+ * the current session. Must be called after `subscribe()` so that the server
638
+ * has wired the destinations. For S3 destinations this includes the
639
+ * per-session subfolder appended by `makePrefixUnique`.
640
+ */
641
+ effectivePaths(): Promise<string[]>;
642
+ }
643
+
644
+ interface RtmpOutputSettings extends NodeCallbacks {
645
+ /** Full RTMP URL to push to, e.g. "rtmp://live.twitch.tv/app/stream-key" */
646
+ url: string;
647
+ /**
648
+ * Called when the output FFmpeg process exits with an error after the node
649
+ * has been created — e.g. the destination server refused the connection,
650
+ * dropped mid-stream, or returned an unexpected error code.
651
+ *
652
+ * Errors that occur during node creation still reject the
653
+ * `workflow.output.rtmp()` promise as usual.
654
+ */
655
+ onError?: (err: Error) => void;
656
+ }
657
+ declare class RtmpOutputNode extends BaseNode {
658
+ readonly settings: RtmpOutputSettings;
659
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: RtmpOutputSettings);
660
+ }
661
+
662
+ interface SrtOutputSettings extends NodeCallbacks {
663
+ /**
664
+ * Full SRT URL to push to, e.g. `"srt://host:port"`.
665
+ * Transport parameters (latency, passphrase, etc.) are passed as query
666
+ * params: `"srt://host:port?latency=200&passphrase=secret"`.
667
+ */
668
+ url: string;
669
+ /**
670
+ * Called when the output FFmpeg process exits with an error after the node
671
+ * has been created — e.g. the destination refused the connection or dropped
672
+ * mid-stream.
673
+ *
674
+ * Errors that occur during node creation still reject the
675
+ * `workflow.output.srt()` promise as usual.
676
+ */
677
+ onError?: (err: Error) => void;
678
+ }
679
+ declare class SrtOutputNode extends BaseNode {
680
+ readonly settings: SrtOutputSettings;
681
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: SrtOutputSettings);
682
+ }
683
+
684
+ interface BufferSettings extends NodeCallbacks {
685
+ /** Time (ms) to hold back the input stream before forwarding it to subscribers. */
686
+ bufferMs: number;
687
+ }
688
+ /**
689
+ * Time-shift node. Subscribe it to exactly one source (any selector); it
690
+ * re-emits that source delayed by `bufferMs`, shared so every downstream
691
+ * subscriber sees the same delayed feed.
692
+ */
693
+ declare class BufferNode extends BaseNode {
694
+ readonly settings: BufferSettings;
695
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: BufferSettings);
696
+ }
697
+
698
+ interface CambAiTranslateSettings extends NodeCallbacks {
699
+ /** Camb.ai API key. */
700
+ apiKey: string;
701
+ /** BCP-47 source language code for the input audio (e.g. `"en-US"`). */
702
+ sourceLanguageCode: string;
703
+ /** BCP-47 target language code for the translation output (e.g. `"de-DE"`). */
704
+ targetLanguageCode: string;
705
+ /** Realtime translation model. Defaults to Camb's server-side default. */
706
+ model?: "lilac" | "violet" | "iris" | "orchid";
707
+ /** Cloned voice ID to synthesize the translation with. Defaults to a built-in voice for the target language. */
708
+ voiceId?: number;
709
+ /** Human-readable label shown in the dashboard. */
710
+ label?: string;
711
+ }
712
+ /** Processor node that translates audio via the Camb.ai realtime speech-to-speech API. */
713
+ declare class CambAiTranslateNode extends BaseNode {
714
+ readonly settings: CambAiTranslateSettings;
715
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: CambAiTranslateSettings);
716
+ }
717
+
718
+ type CaptionMode = "rollUp" | "popOn" | "paintOn";
719
+ interface Cea608EncoderSettings extends NodeCallbacks {
720
+ /** Caption display mode. Default: "rollUp" */
721
+ mode?: CaptionMode;
722
+ /**
723
+ * Maximum number of visible rows (2, 3, or 4). Default: 2.
724
+ *
725
+ * - **rollUp**: sets the scroll-window height (RU2/RU3/RU4 command).
726
+ * - **popOn**: captions sent with `\n`-separated lines are displayed
727
+ * bottom-anchored; this value caps how many lines are shown at once.
728
+ * E.g. `rows: 2` with `"line one\nline two"` fills rows 14 and 15.
729
+ */
730
+ rows?: 2 | 3 | 4;
731
+ /**
732
+ * When `mode` is `"popOn"`, automatically word-wrap caption text to
733
+ * CEA-608's 32-column display width before laying it out as a
734
+ * bottom-anchored multi-line caption card (capped at `rows` lines) —
735
+ * instead of requiring you to insert `\n` yourself. Has no effect in
736
+ * `"rollUp"`/`"paintOn"` modes, where each `send()` is naturally a single
737
+ * line.
738
+ *
739
+ * Default: false.
740
+ */
741
+ autoWrap?: boolean;
742
+ }
743
+ declare class Cea608EncoderNode extends BaseNode {
744
+ readonly settings: Cea608EncoderSettings;
745
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: Cea608EncoderSettings);
746
+ }
747
+
748
+ interface FfprobeMonitorSettings extends NodeCallbacks {
749
+ /**
750
+ * How often (ms) to re-run ffprobe on the attached stream.
751
+ * Each cycle spawns a fresh ffprobe process, probes `probeSizeBytes` of data,
752
+ * and publishes the result to the **stream-info** monitor.
753
+ * Default: 30 000 ms.
754
+ */
755
+ intervalMs?: number;
756
+ /**
757
+ * Bytes of stream data to feed into each ffprobe invocation.
758
+ * Larger values give ffprobe more context (e.g. long-GOP streams) at the
759
+ * cost of a slightly longer first-result latency. Default: 1 048 576 (1 MB).
760
+ */
761
+ probeSizeBytes?: number;
762
+ }
763
+ declare class FfprobeMonitorNode extends BaseNode {
764
+ readonly settings: FfprobeMonitorSettings;
765
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: FfprobeMonitorSettings);
766
+ /**
767
+ * Subscribe to a monitor stream on this node.
768
+ *
769
+ * - `"stream-info"` — emits a `status` event (key/value map) every time a
770
+ * probe cycle completes, containing codec names, resolution, frame rate,
771
+ * bit rates, and format.
772
+ * - `"ffprobe-log"` — emits `log` events with raw ffprobe stderr output.
773
+ *
774
+ * Returns an object with an `unsubscribe()` method; call it to stop receiving
775
+ * events and release the tRPC subscription.
776
+ */
777
+ observe(monitorName: "stream-info" | "ffprobe-log", onData: (event: MonitorEventMsg) => void, onError?: (err: unknown) => void): {
778
+ unsubscribe(): void;
779
+ };
780
+ }
781
+
782
+ interface GateSettings extends NodeCallbacks {
783
+ /**
784
+ * Number of distinct sources that must have emitted at least one item
785
+ * before the gate opens. Default: wait for every subscribed source.
786
+ */
787
+ minReady?: number;
788
+ /**
789
+ * Force the gate open after this many ms even if `minReady` hasn't been
790
+ * reached, using whatever sources are ready by then. Omit to wait
791
+ * indefinitely — a source that never produces data would otherwise stall
792
+ * the gate forever.
793
+ */
794
+ timeoutMs?: number;
795
+ }
796
+ /**
797
+ * Synchronization barrier for fan-in from multiple, independently-timed
798
+ * sources (e.g. two different transcription/translation services). Not to
799
+ * be confused with the `gate` option on `.subscribe()` (`ActivationGate`),
800
+ * which defers wiring based on upstream node *lifecycle*, not data.
801
+ *
802
+ * `GateNode` subscribes to every source immediately and queues everything
803
+ * each one emits, then starts forwarding — for every source at once — only
804
+ * once enough of them have produced their first item. It is a one-time
805
+ * rendezvous, not a per-item join.
806
+ *
807
+ * Every source becomes its own keyed pass-through port on the gate — streams
808
+ * are never merged. Name a port with `name` on the gate's subscription spec
809
+ * (default: the source's index as a string) and read it downstream with
810
+ * `port`:
811
+ *
812
+ * ```ts
813
+ * await gate.subscribe([
814
+ * { source: a, sourceSelector: "ALL", name: "main" },
815
+ * { source: b, sourceSelector: "ALL", name: "backup" },
816
+ * ]);
817
+ * await out1.subscribe([{ source: gate, sourceSelector: "ALL", port: "main" }]);
818
+ * await out2.subscribe([{ source: gate, sourceSelector: "ALL", port: "backup" }]);
819
+ * ```
820
+ *
821
+ * With exactly one source of a kind, plain selector subscriptions (no
822
+ * `port`) still resolve to it; with several same-kind sources the server
823
+ * errors and asks for an explicit `port`.
824
+ */
825
+ declare class GateNode extends BaseNode {
826
+ readonly settings: GateSettings;
827
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: GateSettings);
828
+ }
829
+
830
+ interface GeminiTranslateSettings extends NodeCallbacks {
831
+ /** Gemini API key. */
832
+ apiKey: string;
833
+ /**
834
+ * BCP-47 target language code for the translation output
835
+ * (e.g. `"it"`, `"es"`, `"fr"`).
836
+ */
837
+ targetLanguageCode: string;
838
+ /** Human-readable label shown in the dashboard. */
839
+ label?: string;
840
+ }
841
+ /** Processor node that translates audio via the Gemini Live Translate API. */
842
+ declare class GeminiTranslateNode extends BaseNode {
843
+ readonly settings: GeminiTranslateSettings;
844
+ constructor(nodeId: string, workflow: WorkflowHandle, settings: GeminiTranslateSettings);
845
+ }
846
+
847
+ /** Lifecycle callbacks available on a workflow. */
848
+ interface WorkflowCallbacks {
849
+ /**
850
+ * Called if this workflow's worker process crashes. Workflow errors
851
+ * otherwise only appear in server logs — this is the client-side signal.
852
+ */
853
+ onError?: (err: Error) => void;
854
+ /** Called when the workflow closes, whether via `.close()` or a crash. */
855
+ onClose?: () => void;
856
+ }
857
+ /**
858
+ * A workflow is an isolated sub-registry on the server.
859
+ * All nodes created through a workflow handle are scoped to that workflow;
860
+ * they cannot subscribe to nodes in other workflows.
861
+ *
862
+ * Obtain a workflow via `rema.workflow.create()`.
863
+ */
864
+ declare class Workflow {
865
+ readonly workflowId: string;
866
+ /** Non-private: accessible to factory functions via WorkflowHandle. */
867
+ readonly client: RemaClient;
868
+ private readonly _callbacks;
869
+ private readonly _eventHandlers;
870
+ private _eventSub;
871
+ private _stateSub;
872
+ private _seenInList;
873
+ private _erroredFired;
874
+ private _closedFired;
875
+ /** @internal — use `rema.workflow.create()` instead */
876
+ constructor(workflowId: string, client: RemaClient, callbacks?: WorkflowCallbacks);
877
+ /**
878
+ * Register a handler for events emitted by a specific node in this workflow.
879
+ * Returns an unsubscribe function — call it when the node is closed.
880
+ * @internal used by node factory functions
881
+ */
882
+ _onNodeEvent(nodeId: string, handler: (evt: NodeEventMsg) => void): () => void;
883
+ private _startEventSubscription;
884
+ /**
885
+ * Watches the server's list of all workflows for this workflow's entry, so
886
+ * `onError`/`onClose` can be derived without a dedicated per-workflow
887
+ * subscription: a transition to `status: "crashed"` fires `onError`, and
888
+ * the entry disappearing (graceful close) fires `onClose`.
889
+ */
890
+ private _startStateSubscription;
891
+ private _fireClose;
892
+ readonly input: {
893
+ file: (settings: FileInputSettings, nodeId?: string) => Promise<FileInputNode>;
894
+ rtmpReader: (settings: RtmpReaderSettings, nodeId?: string) => Promise<RtmpReaderNode>;
895
+ /** Announces RTMP publisher connections on an `app` without creating nodes. */
896
+ rtmpAnnouncer: (settings: RtmpAnnouncerSettings, nodeId?: string) => Promise<RtmpAnnouncerNode>;
897
+ /** Announces SRT publisher connections on an `app` without creating nodes. */
898
+ srtAnnouncer: (settings: SrtAnnouncerSettings, nodeId?: string) => Promise<SrtAnnouncerNode>;
899
+ /** Reads an SRT stream ingested by MediaMTX, pulling it back via RTSP internally. */
900
+ srtReader: (settings: SrtReaderSettings, nodeId?: string) => Promise<SrtReaderNode>;
901
+ /**
902
+ * An RTMP multiplexer: accepts any number of simultaneous publishers under
903
+ * the given `app`. For each connection `onStream` is called with a fresh
904
+ * `RtmpStreamNode` that can be wired into its own encoder/output pipeline.
905
+ */
906
+ rtmpMultiplexer: (settings: RtmpMultiplexerSettings, nodeId?: string) => Promise<RtmpMultiplexerNode>;
907
+ /** Format-agnostic caption schedule node. Send captions to it; wire it to a cea608() encoder. */
908
+ captions: (settings?: CaptionSourceSettings, nodeId?: string) => Promise<CaptionSourceNode>;
909
+ /**
910
+ * Audio stream input node. Push a Node.js `Readable` (or any
911
+ * `AsyncIterable<Buffer>`) directly into the pipeline by calling
912
+ * `.stream(source)` on the returned node.
913
+ */
914
+ streamInput: (settings: NodeStreamInputSettings, nodeId?: string) => Promise<NodeStreamInputNode>;
915
+ };
916
+ readonly output: {
917
+ rtmp: (settings: RtmpOutputSettings, nodeId?: string) => Promise<RtmpOutputNode>;
918
+ file: (settings: FileOutputSettings, nodeId?: string) => Promise<FileOutputNode>;
919
+ /** HLS output node. Subscribe it to one or more media/subtitle sources. */
920
+ hls: (settings: HlsOutputSettings, nodeId?: string) => Promise<HlsOutputNode>;
921
+ /** SRT output node. Pushes MPEG-TS over SRT to the given URL. */
922
+ srt: (settings: SrtOutputSettings, nodeId?: string) => Promise<SrtOutputNode>;
923
+ };
924
+ readonly processor: {
925
+ /** CEA-608/708 encoder node. Subscribe it to video + a captions() node. */
926
+ cea608: (settings?: Cea608EncoderSettings, nodeId?: string) => Promise<Cea608EncoderNode>;
927
+ /**
928
+ * ffprobe monitor node.
929
+ * Subscribe it to any media source; it periodically probes the live stream
930
+ * and exposes codec/resolution/bitrate info via the `stream-info` monitor.
931
+ */
932
+ ffprobeMonitor: (settings?: FfprobeMonitorSettings, nodeId?: string) => Promise<FfprobeMonitorNode>;
933
+ /**
934
+ * Gemini Live Translate node.
935
+ * Subscribe it to an audio/media source; it outputs translated audio-only
936
+ * MPEG-TS using the Gemini Live Translate API.
937
+ */
938
+ geminiTranslate: (settings: GeminiTranslateSettings, nodeId?: string) => Promise<GeminiTranslateNode>;
939
+ /**
940
+ * Camb.ai realtime speech-to-speech translation node.
941
+ * Subscribe it to an audio/media source; it outputs translated audio-only
942
+ * MPEG-TS using the Camb.ai realtime translation API.
943
+ */
944
+ cambAiTranslate: (settings: CambAiTranslateSettings, nodeId?: string) => Promise<CambAiTranslateNode>;
945
+ /**
946
+ * Time-shift node. Subscribe it to exactly one source; it re-emits that
947
+ * source delayed by `bufferMs`, shared across every downstream
948
+ * subscriber.
949
+ */
950
+ buffer: (settings: BufferSettings, nodeId?: string) => Promise<BufferNode>;
951
+ /**
952
+ * Synchronization barrier for fan-in from multiple, independently-timed
953
+ * sources. Subscribe it to two or more sources; it withholds all of them
954
+ * until enough (`minReady`, default: all) have produced their first
955
+ * item, then forwards everything live.
956
+ */
957
+ gate: (settings?: GateSettings, nodeId?: string) => Promise<GateNode>;
958
+ };
959
+ /**
960
+ * Close this workflow and deactivate all its nodes on the server.
961
+ * After calling this the workflow handle is no longer usable.
962
+ */
963
+ close(): Promise<void>;
964
+ }
965
+
966
+ declare class Rema {
967
+ private readonly client;
968
+ private readonly _autoCloseWorkflows;
969
+ private constructor();
970
+ static connect(opts?: RemaClientOptions): Promise<Rema>;
971
+ readonly workflow: {
972
+ /**
973
+ * Create a new isolated workflow on the server.
974
+ *
975
+ * All nodes created through the returned `Workflow` handle are scoped to
976
+ * this workflow; they cannot subscribe to nodes in other workflows.
977
+ *
978
+ * @param opts.name Human-readable label shown in the web UI.
979
+ * @param opts.workflowId Optional stable ID. Server generates one if omitted.
980
+ * @param opts.closeOnDisconnect When true, this workflow is closed automatically
981
+ * when `rema.close()`/`wf.close()` is called, or
982
+ * when the connection to the server is lost (e.g.
983
+ * the process crashes) for more than a few seconds.
984
+ * @param opts.parentId ID of an existing workflow to attach this one to as
985
+ * a child (shown nested in the web UI).
986
+ * @param opts.closeWithParent When true, this workflow is closed automatically
987
+ * when its parent workflow is closed.
988
+ * @param opts.onError Called if this workflow's worker process crashes.
989
+ * @param opts.onClose Called when the workflow closes, whether via
990
+ * `.close()` or a crash.
991
+ */
992
+ create: (opts?: {
993
+ name?: string;
994
+ workflowId?: string;
995
+ closeOnDisconnect?: boolean;
996
+ parentId?: string;
997
+ closeWithParent?: boolean;
998
+ } & WorkflowCallbacks) => Promise<Workflow>;
999
+ /** List all active workflows on the server. */
1000
+ list: () => Promise<WorkflowInfo[]>;
1001
+ };
1002
+ /** Fetches the server's current health status (uptime, workflow counts, MediaMTX reachability). */
1003
+ health(): Promise<HealthStatus>;
1004
+ /**
1005
+ * Close all `closeOnDisconnect` workflows, then disconnect from the server.
1006
+ */
1007
+ close(): Promise<void>;
1008
+ }
1009
+
1010
+ /**
1011
+ * Stream selectors — used when calling node.subscribe() to pick which
1012
+ * streams flow from source to subscriber.
1013
+ */
1014
+ type StreamSelector = "VIDEO" | "AUDIO" | "CAPTIONS" | "ALL";
1015
+ declare const selectVideo: "VIDEO";
1016
+ declare const selectAudio: "AUDIO";
1017
+ declare const selectCaptions: "CAPTIONS";
1018
+ declare const selectAll: "ALL";
1019
+
1020
+ export { type AllSubscriptionSpec, type AudioRenditionOptions, type AudioSubscriptionSpec, BaseNode, BufferNode, type BufferSettings, CambAiTranslateNode, type CambAiTranslateSettings, type CaptionMode, CaptionSourceNode, type CaptionSourceSettings, type CaptionsSubscriptionSpec, Cea608EncoderNode, type Cea608EncoderSettings, type CodecSpec, FfprobeMonitorNode, type FfprobeMonitorSettings, FileInputNode, type FileInputSettings, FileOutputNode, type FileOutputSettings, GateNode, type GateSettings, GeminiTranslateNode, type GeminiTranslateSettings, type HlsDestination, HlsOutputNode, type HlsOutputSettings, type NodeCallbacks, NodeStreamInputNode, type NodeStreamInputSettings, Rema, type RemaClientOptions, RtmpAnnouncerNode, type RtmpAnnouncerSettings, type RtmpConnectionInfo, RtmpMultiplexerNode, type RtmpMultiplexerSettings, RtmpOutputNode, type RtmpOutputSettings, RtmpReaderNode, type RtmpReaderSettings, RtmpStreamNode, SrtAnnouncerNode, type SrtAnnouncerSettings, type SrtConnectionInfo, SrtOutputNode, type SrtOutputSettings, SrtReaderNode, type SrtReaderSettings, type StreamSelector, type SubscriptionSpec, type VideoRenditionOptions, type VideoSubscriptionSpec, Workflow, type WorkflowCallbacks, createNodeStreamInputNode, selectAll, selectAudio, selectCaptions, selectVideo };