homebridge-plugin-utils 1.15.2 → 1.16.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.
Files changed (51) hide show
  1. package/README.md +4 -1
  2. package/build/eslint-rules.mjs +1 -0
  3. package/dist/featureoptions.d.ts +83 -5
  4. package/dist/featureoptions.js +65 -6
  5. package/dist/featureoptions.js.map +1 -1
  6. package/dist/ffmpeg/codecs.d.ts +172 -0
  7. package/dist/ffmpeg/codecs.js +374 -0
  8. package/dist/ffmpeg/codecs.js.map +1 -0
  9. package/dist/ffmpeg/exec.d.ts +108 -0
  10. package/dist/ffmpeg/exec.js +122 -0
  11. package/dist/ffmpeg/exec.js.map +1 -0
  12. package/dist/ffmpeg/index.d.ts +8 -0
  13. package/dist/ffmpeg/index.js +13 -0
  14. package/dist/ffmpeg/index.js.map +1 -0
  15. package/dist/ffmpeg/options.d.ts +345 -0
  16. package/dist/ffmpeg/options.js +750 -0
  17. package/dist/ffmpeg/options.js.map +1 -0
  18. package/dist/ffmpeg/process.d.ts +155 -0
  19. package/dist/ffmpeg/process.js +344 -0
  20. package/dist/ffmpeg/process.js.map +1 -0
  21. package/dist/ffmpeg/record.d.ts +230 -0
  22. package/dist/ffmpeg/record.js +504 -0
  23. package/dist/ffmpeg/record.js.map +1 -0
  24. package/dist/ffmpeg/rtp.d.ts +205 -0
  25. package/dist/ffmpeg/rtp.js +335 -0
  26. package/dist/ffmpeg/rtp.js.map +1 -0
  27. package/dist/ffmpeg/settings.d.ts +6 -0
  28. package/dist/ffmpeg/settings.js +17 -0
  29. package/dist/ffmpeg/settings.js.map +1 -0
  30. package/dist/ffmpeg/stream.d.ts +143 -0
  31. package/dist/ffmpeg/stream.js +186 -0
  32. package/dist/ffmpeg/stream.js.map +1 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/mqttclient.d.ts +161 -1
  37. package/dist/mqttclient.js +161 -9
  38. package/dist/mqttclient.js.map +1 -1
  39. package/dist/service.d.ts +9 -2
  40. package/dist/service.js +6 -0
  41. package/dist/service.js.map +1 -1
  42. package/dist/ui/featureoptions.js +65 -6
  43. package/dist/ui/featureoptions.js.map +1 -1
  44. package/dist/ui/webUi-featureoptions.mjs +5 -4
  45. package/dist/util.d.ts +203 -12
  46. package/dist/util.js +95 -12
  47. package/dist/util.js.map +1 -1
  48. package/package.json +13 -9
  49. package/dist/rtp.d.ts +0 -32
  50. package/dist/rtp.js +0 -178
  51. package/dist/rtp.js.map +0 -1
@@ -0,0 +1,504 @@
1
+ /* Copyright(C) 2017-2025, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * ffmpeg/record.ts: Provide FFmpeg process control to support livestreaming and HomeKit Secure Video.
4
+ *
5
+ */
6
+ /**
7
+ * FFmpeg process management for HomeKit Secure Video (HKSV) events and fMP4 livestreaming.
8
+ *
9
+ * This module defines classes for orchestrating FFmpeg processes that produce fMP4 segments suitable for HomeKit Secure Video and realtime livestreaming scenarios. It
10
+ * handles process lifecycle, segment buffering, initialization segment detection, and streaming event generation, abstracting away the complexity of interacting directly
11
+ * with FFmpeg for these workflows.
12
+ *
13
+ * Key features:
14
+ *
15
+ * - Automated setup and management of FFmpeg processes for HKSV event recording and livestreaming (with support for audio and video).
16
+ * - Parsing and generation of fMP4 boxes/segments for HomeKit, including initialization and media segments.
17
+ * - Async generator APIs for efficient, event-driven segment handling.
18
+ * - Flexible error handling and timeouts for HomeKit’s strict realtime requirements.
19
+ * - Designed for Homebridge plugin authors or advanced users who need robust, platform-aware FFmpeg session control for HomeKit and related integrations.
20
+ *
21
+ * @module
22
+ */
23
+ import { HKSV_IDR_INTERVAL, HKSV_TIMEOUT } from "./settings.js";
24
+ import { runWithTimeout } from "../util.js";
25
+ import { FfmpegProcess } from "./process.js";
26
+ import events from "node:events";
27
+ import { once } from "node:events";
28
+ /**
29
+ * FFmpeg process controller for HomeKit Secure Video (HKSV) and fMP4 livestreaming and recording.
30
+ *
31
+ * This class manages the lifecycle and parsing of an FFmpeg process to support HKSV and livestreaming in fMP4 format. It handles initialization segments, media segment
32
+ * parsing, buffering, and HomeKit segment generation, and emits events for segment and initialization.
33
+ *
34
+ * @example
35
+ *
36
+ * ```ts
37
+ * // Create a new recording process for an HKSV event.
38
+ * const process = new FfmpegRecordingProcess(ffmpegOptions, recordingConfig, 30, true, 5000000, 0);
39
+ *
40
+ * // Start the process.
41
+ * process.start();
42
+ *
43
+ * // Iterate over generated segments.
44
+ * for await(const segment of process.segmentGenerator()) {
45
+ *
46
+ * // Send segment to HomeKit, etc.
47
+ * }
48
+ *
49
+ * // Stop when finished.
50
+ * process.stop();
51
+ * ```
52
+ *
53
+ * @see FfmpegOptions
54
+ * @see FfmpegProcess
55
+ * @see {@link https://ffmpeg.org/ffmpeg.html | FFmpeg Documentation}
56
+ */
57
+ class FfmpegFmp4Process extends FfmpegProcess {
58
+ hasInitSegment;
59
+ _initSegment;
60
+ isLivestream;
61
+ isLoggingErrors;
62
+ isTimedOut;
63
+ recordingBuffer;
64
+ recordingConfig;
65
+ segmentLength;
66
+ /**
67
+ * Constructs a new fMP4 FFmpeg process for HKSV event recording or livestreaming.
68
+ *
69
+ * @param ffmpegOptions - FFmpeg configuration options.
70
+ * @param recordingConfig - HomeKit recording configuration for the session.
71
+ * @param isAudioActive - If `true`, enables audio stream processing.
72
+ * @param fmp4Options - Partial configuration for the fMP4 session (fps, url, etc.).
73
+ *
74
+ * @example
75
+ *
76
+ * ```ts
77
+ * const process = new FfmpegFmp4Process(ffmpegOptions, recordingConfig, true, { fps: 30 });
78
+ * ```
79
+ */
80
+ constructor(ffmpegOptions, recordingConfig, isAudioActive, fmp4Options = {}) {
81
+ // Initialize our parent.
82
+ super(ffmpegOptions);
83
+ // We want to log errors when they occur.
84
+ this.isLoggingErrors = true;
85
+ // Initialize our recording buffer.
86
+ this.hasInitSegment = false;
87
+ this._initSegment = Buffer.alloc(0);
88
+ this.recordingBuffer = [];
89
+ // Initialize our state.
90
+ this.isLivestream = fmp4Options.livestream ?? false;
91
+ this.isTimedOut = false;
92
+ fmp4Options.fps ??= 30;
93
+ fmp4Options.url ??= "";
94
+ // Save our recording configuration.
95
+ this.recordingConfig = recordingConfig;
96
+ // Configure our video parameters for our input:
97
+ //
98
+ // -hide_banner Suppress printing the startup banner in FFmpeg.
99
+ // -nostats Suppress printing progress reports while encoding in FFmpeg.
100
+ // -fflags flags Set the format flags to generate a presentation timestamp if it's missing and discard any corrupt packets rather than exit.
101
+ // -err_detect ignore_err Ignore decoding errors and continue rather than exit.
102
+ // -probesize number How many bytes should be analyzed for stream information. Use the size of the timeshift buffer or our configured defaults.
103
+ // -r fps Set the input frame rate for the video stream.
104
+ // -f mp4 Tell FFmpeg that it should expect an MP4-encoded input stream.
105
+ // -i pipe:0 Use standard input to get video data.
106
+ // -ss Fast forward to where HKSV is expecting us to be for a recording event.
107
+ this.commandLineArgs = [
108
+ "-hide_banner",
109
+ "-nostats",
110
+ "-fflags", "+discardcorrupt+genpts",
111
+ "-err_detect", "ignore_err"
112
+ ];
113
+ if (this.isLivestream) {
114
+ this.commandLineArgs.push("-avioflags", "direct", "-rtsp_transport", "tcp", "-i", fmp4Options.url);
115
+ }
116
+ else {
117
+ this.commandLineArgs.push("-probesize", (fmp4Options.probesize ?? 5000000).toString(), "-r", fmp4Options.fps.toString(), "-f", "mp4", "-i", "pipe:0", "-ss", (fmp4Options.timeshift ?? 0).toString() + "ms");
118
+ }
119
+ // Configure our recording options for the video stream:
120
+ //
121
+ // -map 0:v:0 Selects the first available video track from the stream.
122
+ this.commandLineArgs.push("-map", "0:v:0", ...(this.isLivestream ? ["-vcodec", "copy"] : this.options.recordEncoder({
123
+ bitrate: recordingConfig.videoCodec.parameters.bitRate,
124
+ fps: recordingConfig.videoCodec.resolution[2],
125
+ height: recordingConfig.videoCodec.resolution[1],
126
+ idrInterval: HKSV_IDR_INTERVAL,
127
+ inputFps: fmp4Options.fps,
128
+ level: recordingConfig.videoCodec.parameters.level,
129
+ profile: recordingConfig.videoCodec.parameters.profile,
130
+ width: recordingConfig.videoCodec.resolution[0]
131
+ })));
132
+ // If we're livestreaming, emit fragments at one-second intervals.
133
+ if (this.isLivestream) {
134
+ this.commandLineArgs.push("-frag_duration", "1000000");
135
+ }
136
+ // -movflags flags In the generated fMP4 stream: start a new fragment at each keyframe, write a blank MOOV box, and avoid writing absolute offsets.
137
+ // -reset_timestamps Reset timestamps at the beginning of each segment.
138
+ // -metadata Set the metadata to the name of the camera to distinguish between FFmpeg sessions.
139
+ this.commandLineArgs.push("-movflags", "frag_keyframe+empty_moov+default_base_moof+skip_sidx+skip_trailer", "-reset_timestamps", "1", "-metadata", "comment=" + this.options.name() + " " + (this.isLivestream ? "Livestream Buffer" : "HKSV Event"));
140
+ if (isAudioActive) {
141
+ // Configure the audio portion of the command line. Options we use are:
142
+ //
143
+ // -map 0:a:0? Selects the first available audio track from the stream, if it exists.
144
+ // -acodec copy Copy the stream without reencoding it.
145
+ this.commandLineArgs.push("-map", "0:a:0?", "-acodec", "copy");
146
+ }
147
+ // Configure our video parameters for outputting our final stream:
148
+ //
149
+ // -f mp4 Tell ffmpeg that it should create an MP4-encoded output stream.
150
+ // pipe:1 Output the stream to standard output.
151
+ this.commandLineArgs.push("-f", "mp4", "pipe:1");
152
+ // Additional logging, but only if we're debugging.
153
+ if (this.options.codecSupport.verbose || this.isVerbose) {
154
+ this.commandLineArgs.unshift("-loglevel", "level+verbose");
155
+ }
156
+ }
157
+ /**
158
+ * Prepares and configures the FFmpeg process for reading and parsing output fMP4 data.
159
+ *
160
+ * This method is called internally by the process lifecycle and is not typically invoked directly by consumers.
161
+ */
162
+ configureProcess() {
163
+ let dataListener;
164
+ // Call our parent to get started.
165
+ super.configureProcess();
166
+ // Initialize our variables that we need to process incoming FFmpeg packets.
167
+ let header = Buffer.alloc(0);
168
+ let bufferRemaining = Buffer.alloc(0);
169
+ let dataLength = 0;
170
+ let type = "";
171
+ // Process FFmpeg output and parse out the fMP4 stream it's generating for HomeKit Secure Video.
172
+ this.process?.stdout.on("data", dataListener = (buffer) => {
173
+ // If we have anything left from the last buffer we processed, prepend it to this buffer.
174
+ if (bufferRemaining.length > 0) {
175
+ buffer = Buffer.concat([bufferRemaining, buffer]);
176
+ bufferRemaining = Buffer.alloc(0);
177
+ }
178
+ let offset = 0;
179
+ // FFmpeg is outputting an fMP4 stream that's suitable for HomeKit Secure Video. However, we can't just pass this stream directly back to HomeKit since we're using
180
+ // a generator-based API to send packets back to HKSV. Here, we take on the task of parsing the fMP4 stream that's being generated and split it up into the MP4
181
+ // boxes that HAP-NodeJS is ultimately expecting.
182
+ for (;;) {
183
+ let data;
184
+ // The MP4 container format is well-documented and designed around the concept of boxes. A box (or atom as they used to be called) is at the center of an MP4
185
+ // container. It's composed of an 8-byte header, followed by the data payload it carries.
186
+ // No existing header, let's start a new box.
187
+ if (!header.length) {
188
+ // Grab the header. The first four bytes represents the length of the entire box. Second four bytes represent the box type.
189
+ header = buffer.slice(0, 8);
190
+ // Now we retrieve the length of the box.
191
+ dataLength = header.readUInt32BE(0);
192
+ // Get the type of the box. This is always a string and has a funky history to it that makes for an interesting read!
193
+ type = header.slice(4).toString();
194
+ // Finally, we get the data portion of the box.
195
+ data = buffer.slice(8, dataLength);
196
+ // Mark our data offset so we account for the length of the data header and subtract it from the overall length to capture just the data portion.
197
+ dataLength -= offset = 8;
198
+ }
199
+ else {
200
+ // Grab the data from our buffer.
201
+ data = buffer.slice(0, dataLength);
202
+ offset = 0;
203
+ }
204
+ // If we don't have enough data in this buffer, save what we have for the next buffer we see and append it there.
205
+ if (data.length < dataLength) {
206
+ bufferRemaining = data;
207
+ break;
208
+ }
209
+ // If we're creating a livestream to be consumed by the timeshift buffer, we need to track the initialization segment, and emit segments.
210
+ if (this.isLivestream) {
211
+ // If this is part of the initialization segment, store it for future use.
212
+ if (!this.hasInitSegment) {
213
+ // The initialization segment is everything before the first moof box. Once we've seen a moof box, we know we've captured it in full.
214
+ if (type === "moof") {
215
+ this.hasInitSegment = true;
216
+ this.emit("initsegment");
217
+ }
218
+ else {
219
+ this._initSegment = Buffer.concat([this._initSegment, header, data]);
220
+ }
221
+ }
222
+ if (this.hasInitSegment) {
223
+ // We only emit segments once we have the initialization segment.
224
+ this.emit("segment", Buffer.concat([header, data]));
225
+ }
226
+ }
227
+ else {
228
+ // Add it to our queue to be eventually pushed out through our generator function.
229
+ this.recordingBuffer.push({ data: data, header: header, length: dataLength, type: type });
230
+ this.emit("mp4box");
231
+ }
232
+ // Prepare to start a new box for the next buffer that we will be processing.
233
+ data = Buffer.alloc(0);
234
+ header = Buffer.alloc(0);
235
+ type = "";
236
+ // We've parsed an entire box, and there's no more data in this buffer to parse.
237
+ if (buffer.length === (offset + dataLength)) {
238
+ dataLength = 0;
239
+ break;
240
+ }
241
+ // If there's anything left in the buffer, move us to the new box and let's keep iterating.
242
+ buffer = buffer.slice(offset + dataLength);
243
+ dataLength = 0;
244
+ }
245
+ });
246
+ // Make sure we cleanup our listeners when we're done.
247
+ this.process?.once("exit", () => {
248
+ this.process?.stdout?.off("data", dataListener);
249
+ });
250
+ }
251
+ /**
252
+ * Retrieves the fMP4 initialization segment generated by FFmpeg.
253
+ *
254
+ * Waits until the initialization segment is available, then returns it.
255
+ *
256
+ * @returns A promise resolving to the initialization segment as a Buffer.
257
+ *
258
+ * @example
259
+ *
260
+ * ```ts
261
+ * const initSegment = await process.getInitSegment();
262
+ * ```
263
+ */
264
+ async getInitSegment() {
265
+ // If we have the initialization segment, return it.
266
+ if (this.hasInitSegment) {
267
+ return this._initSegment;
268
+ }
269
+ // Wait until the initialization segment is seen and then try again.
270
+ await events.once(this, "initsegment");
271
+ return this.getInitSegment();
272
+ }
273
+ /**
274
+ * Stops the FFmpeg process and performs cleanup, including emitting termination events for segment generators.
275
+ *
276
+ * This is called as part of the process shutdown sequence.
277
+ */
278
+ stopProcess() {
279
+ // Call our parent to get started.
280
+ super.stopProcess();
281
+ // Ensure that we clear out of our segment generator by guaranteeing an exit path.
282
+ this.isEnded = true;
283
+ this.emit("mp4box");
284
+ this.emit("close");
285
+ }
286
+ /**
287
+ * Starts the FFmpeg process, adjusting segment length for livestreams if set.
288
+ *
289
+ * @example
290
+ *
291
+ * ```ts
292
+ * process.start();
293
+ * ```
294
+ */
295
+ start() {
296
+ if (this.isLivestream && (this.segmentLength !== undefined)) {
297
+ const fragIndex = this.commandLineArgs.indexOf("-frag_duration");
298
+ if (fragIndex !== -1) {
299
+ this.commandLineArgs[fragIndex + 1] = (this.segmentLength * 1000).toString();
300
+ }
301
+ }
302
+ // Start the FFmpeg session.
303
+ super.start();
304
+ }
305
+ /**
306
+ * Stops the FFmpeg process and logs errors if specified.
307
+ *
308
+ * @param logErrors - If `true`, logs FFmpeg errors. Defaults to the internal process logging state.
309
+ *
310
+ * @example
311
+ *
312
+ * ```ts
313
+ * process.stop();
314
+ * ```
315
+ */
316
+ stop(logErrors = this.isLoggingErrors) {
317
+ const savedLogErrors = this.isLoggingErrors;
318
+ // Flag whether we should log abnormal exits (e.g. being killed) or not.
319
+ this.isLoggingErrors = logErrors;
320
+ // Call our parent to finish the job.
321
+ super.stop();
322
+ // Restore our previous logging state.
323
+ this.isLoggingErrors = savedLogErrors;
324
+ }
325
+ /**
326
+ * Logs errors from FFmpeg process execution, handling known benign HKSV stream errors gracefully.
327
+ *
328
+ * @param exitCode - The exit code from the FFmpeg process.
329
+ * @param signal - The signal (if any) used to terminate the process.
330
+ */
331
+ logFfmpegError(exitCode, signal) {
332
+ // If we're ignoring errors, we're done.
333
+ if (!this.isLoggingErrors) {
334
+ return;
335
+ }
336
+ // Known HKSV-related errors due to occasional inconsistencies that are occasionally produced by the input stream and FFmpeg's own occasional quirkiness.
337
+ const ffmpegKnownHksvError = new RegExp([
338
+ "(Cannot determine format of input stream 0:0 after EOF)",
339
+ "(Could not write header \\(incorrect codec parameters \\?\\): Broken pipe)",
340
+ "(Could not write header for output file #0)",
341
+ "(Error closing file: Broken pipe)",
342
+ "(Error splitting the input into NAL units\\.)",
343
+ "(Invalid data found when processing input)",
344
+ "(moov atom not found)"
345
+ ].join("|"));
346
+ // See if we know about this error.
347
+ if (this.stderrLog.some(x => ffmpegKnownHksvError.test(x))) {
348
+ this.log.error("FFmpeg ended unexpectedly due to issues processing the media stream. This error can be safely ignored - it will occur occasionally.");
349
+ return;
350
+ }
351
+ // Otherwise, revert to our default logging in our parent.
352
+ super.logFfmpegError(exitCode, signal);
353
+ }
354
+ /**
355
+ * Asynchronously generates complete segments from FFmpeg output, formatted for HomeKit Secure Video.
356
+ *
357
+ * This async generator yields fMP4 segments as Buffers, or ends on process termination or timeout.
358
+ *
359
+ * @yields A Buffer containing a complete MP4 segment suitable for HomeKit.
360
+ *
361
+ * @example
362
+ *
363
+ * ```ts
364
+ * for await(const segment of process.segmentGenerator()) {
365
+ *
366
+ * // Process each segment for HomeKit.
367
+ * }
368
+ * ```
369
+ */
370
+ async *segmentGenerator() {
371
+ let segment = [];
372
+ // Loop forever, generating either FTYP/MOOV box pairs or MOOF/MDAT box pairs for HomeKit Secure Video.
373
+ for (;;) {
374
+ // FFmpeg has finished it's output - we're done.
375
+ if (this.isEnded) {
376
+ return;
377
+ }
378
+ // If the buffer is empty, wait for our FFmpeg process to produce more boxes.
379
+ if (!this.recordingBuffer.length) {
380
+ // Segments are output by FFmpeg according to our specified IDR interval. If we don't see a segment within the timeframe we need for HKSV's timing requirements,
381
+ // we flag it accordingly and return null back to the generator that's calling us.
382
+ // eslint-disable-next-line no-await-in-loop
383
+ await runWithTimeout(once(this, "mp4box"), HKSV_TIMEOUT);
384
+ }
385
+ // Grab the next fMP4 box from our buffer.
386
+ const box = this.recordingBuffer.shift();
387
+ // FFmpeg hasn't produced any output. Given the time-sensitive nature of HKSV that constrains us to no more than 5 seconds to provide the next segment, we're done.
388
+ if (!box) {
389
+ this.isTimedOut = true;
390
+ return;
391
+ }
392
+ // Queue up this fMP4 box to send back to HomeKit.
393
+ segment.push(box.header, box.data);
394
+ // What we want to send are two types of complete segments, made up of multiple MP4 boxes:
395
+ //
396
+ // - a complete MOOV box, usually with an accompanying FTYP box, that's sent at the very beginning of any valid fMP4 stream. HomeKit Secure Video looks for this
397
+ // before anything else.
398
+ //
399
+ // - a complete MOOF/MDAT pair. MOOF describes the sample locations and their sizes and MDAT contains the actual audio and video data related to that segment. Think
400
+ // of MOOF as the audio/video data "header", and MDAT as the "payload".
401
+ //
402
+ // Once we see these, we combine all the segments in our queue to send back to HomeKit.
403
+ if ((box.type === "moov") || (box.type === "mdat")) {
404
+ yield Buffer.concat(segment);
405
+ segment = [];
406
+ }
407
+ }
408
+ }
409
+ /**
410
+ * Returns the initialization segment as a Buffer, or null if not yet available.
411
+ *
412
+ * @returns The initialization segment Buffer, or `null` if not yet generated.
413
+ *
414
+ * @example
415
+ *
416
+ * ```ts
417
+ * const init = process.initSegment;
418
+ * if(init) {
419
+ *
420
+ * // Use the initialization segment.
421
+ * }
422
+ * ```
423
+ */
424
+ get initSegment() {
425
+ if (!this.hasInitSegment) {
426
+ return null;
427
+ }
428
+ return this._initSegment;
429
+ }
430
+ }
431
+ /**
432
+ * Manages a HomeKit Secure Video recording FFmpeg process.
433
+ *
434
+ * @example
435
+ *
436
+ * ```ts
437
+ * const process = new FfmpegRecordingProcess(ffmpegOptions, recordingConfig, 30, true, 5000000, 0);
438
+ * process.start();
439
+ * ```
440
+ *
441
+ * @see FfmpegFmp4Process
442
+ *
443
+ * @category FFmpeg
444
+ */
445
+ export class FfmpegRecordingProcess extends FfmpegFmp4Process {
446
+ /**
447
+ * Constructs a new FFmpeg recording process for HKSV events.
448
+ *
449
+ * @param options - FFmpeg configuration options.
450
+ * @param recordingConfig - HomeKit recording configuration for the session.
451
+ * @param fps - Video frames per second.
452
+ * @param isAudioActive - If `true`, enables audio stream processing.
453
+ * @param probesize - Stream analysis size, in bytes.
454
+ * @param timeshift - Timeshift offset for event-based recording, in milliseconds.
455
+ */
456
+ constructor(options, recordingConfig, fps, isAudioActive, probesize, timeshift) {
457
+ super(options, recordingConfig, isAudioActive, { fps: fps, probesize: probesize, timeshift: timeshift });
458
+ }
459
+ }
460
+ /**
461
+ * Manages a HomeKit livestream FFmpeg process for generating fMP4 segments.
462
+ *
463
+ * @example
464
+ *
465
+ * ```ts
466
+ * const process = new FfmpegLivestreamProcess(ffmpegOptions, recordingConfig, url, 30, true);
467
+ * process.start();
468
+ *
469
+ * const initSegment = await process.getInitSegment();
470
+ * ```
471
+ *
472
+ * @see FfmpegFmp4Process
473
+ *
474
+ * @category FFmpeg
475
+ */
476
+ export class FfmpegLivestreamProcess extends FfmpegFmp4Process {
477
+ /**
478
+ * Constructs a new FFmpeg livestream process.
479
+ *
480
+ * @param options - FFmpeg configuration options.
481
+ * @param recordingConfig - HomeKit recording configuration for the session.
482
+ * @param url - Source RTSP or livestream URL.
483
+ * @param fps - Video frames per second.
484
+ * @param isAudioActive - If `true`, enables audio stream processing.
485
+ */
486
+ constructor(options, recordingConfig, url, fps, isAudioActive) {
487
+ super(options, recordingConfig, isAudioActive, { fps: fps, livestream: true, url: url });
488
+ }
489
+ /**
490
+ * Gets the fMP4 initialization segment generated by FFmpeg for the livestream.
491
+ *
492
+ * @returns A promise resolving to the initialization segment as a Buffer.
493
+ *
494
+ * @example
495
+ *
496
+ * ```ts
497
+ * const initSegment = await process.getInitSegment();
498
+ * ```
499
+ */
500
+ async getInitSegment() {
501
+ return super.getInitSegment();
502
+ }
503
+ }
504
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/ffmpeg/record.ts"],"names":[],"mappings":"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"}