homebridge-unifi-protect 5.0.6 → 5.1.1

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.
@@ -3,97 +3,116 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.FfmpegProcess = void 0;
6
+ exports.FfmpegRecordingProcess = exports.FfmpegStreamingProcess = exports.FfmpegProcess = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
8
  const dgram_1 = require("dgram");
9
+ // Base class for all FFmpeg process management.
9
10
  class FfmpegProcess {
10
- constructor(delegate, sessionId, command, returnPort, callback) {
11
- this.command = command.join(" ");
12
- this.debug = delegate.platform.debug.bind(delegate.platform);
13
- this.delegate = delegate;
14
- this.log = delegate.platform.log;
15
- this.name = delegate.name.bind(delegate.protectCamera);
16
- this.sessionId = sessionId;
11
+ // Create a new FFmpeg process instance.
12
+ constructor(protectCamera, commandLineArgs, callback) {
13
+ this.callback = null;
14
+ this.commandLineArgs = [];
15
+ this.debug = protectCamera.platform.debug.bind(protectCamera.platform);
16
+ this.isPrepared = false;
17
+ this.isEnded = false;
18
+ this.isStarted = false;
19
+ this.log = protectCamera.platform.log;
20
+ this.name = protectCamera.name.bind(protectCamera);
21
+ this.nvr = protectCamera.nvr;
22
+ this.process = null;
23
+ this.protectCamera = protectCamera;
17
24
  // Toggle FFmpeg logging, if configured.
18
- this.isVerbose = this.delegate.platform.verboseFfmpeg;
19
- // Create the return port for FFmpeg, if requested to do so. The only time we don't do this is when we're standing up
20
- // a two-way audio stream - in that case, the audio work is done through RtpSplitter and not here.
21
- if (returnPort) {
22
- this.createSocket(returnPort);
25
+ this.isVerbose = protectCamera.platform.verboseFfmpeg;
26
+ // If we've specified a command line or a callback, let's save them.
27
+ if (commandLineArgs) {
28
+ this.commandLineArgs = commandLineArgs;
29
+ }
30
+ if (callback) {
31
+ this.callback = callback;
23
32
  }
24
- void this.startFfmpeg(command, callback);
25
- }
26
- // Create the port for FFmpeg to send data through.
27
- createSocket(portInfo) {
28
- const socket = (0, dgram_1.createSocket)(portInfo.addressVersion === "ipv6" ? "udp6" : "udp4");
29
- // Cleanup after ourselves when the socket closes.
30
- socket.on("close", () => {
31
- if (this.streamTimeout) {
32
- clearTimeout(this.streamTimeout);
33
- }
34
- });
35
- // Handle potential network errors.
36
- socket.on("error", (error) => {
37
- this.log.error("%s: Socket error: %s.", this.name(), error.name);
38
- this.delegate.stopStream(this.sessionId);
39
- });
40
- // Manage our video streams in case we haven't received a stop request, but we're in fact dead zombies.
41
- socket.on("message", () => {
42
- // Clear our last canary.
43
- if (this.streamTimeout) {
44
- clearTimeout(this.streamTimeout);
45
- }
46
- // Set our new canary.
47
- this.streamTimeout = setTimeout(() => {
48
- this.debug("%s: video stream appears to be inactive for 5 seconds. Stopping stream.", this.name());
49
- this.delegate.controller.forceStopStreamingSession(this.sessionId);
50
- this.delegate.stopStream(this.sessionId);
51
- }, 5000);
52
- });
53
- socket.bind(portInfo.port);
54
33
  }
55
- // Start our FFmpeg process.
56
- async startFfmpeg(ffmpegCommandLine, callback) {
57
- var _a, _b, _c, _d;
34
+ // Prepare and start our FFmpeg process.
35
+ prepareProcess(commandLineArgs, callback) {
36
+ var _a, _b, _c;
37
+ // If we've specified a new command line or callback, let's save them.
38
+ if (commandLineArgs) {
39
+ this.commandLineArgs = commandLineArgs;
40
+ }
41
+ // No command line arguments - we're done.
42
+ if (!this.commandLineArgs) {
43
+ this.log.error("%s: No FFmpeg command line specified.", this.name());
44
+ return;
45
+ }
46
+ // Save the callback, if we have one.
47
+ if (callback) {
48
+ this.callback = callback;
49
+ }
58
50
  // See if we should display ffmpeg command output.
59
51
  let hasLogging = false;
60
- // Track if we've started receiving data.
61
- let started = false;
52
+ // Track if we've started or ended FFmpeg.
53
+ this.isStarted = false;
54
+ this.isEnded = false;
62
55
  // If we've got a loglevel specified, ensure we display it.
63
- if (ffmpegCommandLine.indexOf("-loglevel") !== -1) {
56
+ if (this.commandLineArgs.indexOf("-loglevel") !== -1) {
64
57
  hasLogging = true;
65
58
  }
66
- if (hasLogging || this.isVerbose || this.delegate.platform.config.debugAll) {
67
- this.log.info("%s: ffmpeg command: %s %s", this.name(), this.delegate.videoProcessor, ffmpegCommandLine.join(" "));
59
+ // Inform the user, if we've been asked to do so.
60
+ if (hasLogging || this.isVerbose || this.protectCamera.platform.config.debugAll) {
61
+ this.log.info("%s: ffmpeg command: %s %s", this.name(), this.protectCamera.stream.videoProcessor, this.commandLineArgs.join(" "));
68
62
  }
69
63
  else {
70
- this.debug("%s: ffmpeg command: %s %s", this.name(), this.delegate.videoProcessor, ffmpegCommandLine.join(" "));
64
+ this.debug("%s: ffmpeg command: %s %s", this.name(), this.protectCamera.stream.videoProcessor, this.commandLineArgs.join(" "));
71
65
  }
72
66
  // Prepare the command line we want to execute.
73
- this.process = (0, execa_1.default)(this.delegate.videoProcessor, ffmpegCommandLine);
67
+ this.process = (0, execa_1.default)(this.protectCamera.stream.videoProcessor, this.commandLineArgs);
68
+ let dataListener;
69
+ let errorListener;
74
70
  // Handle errors on stdin.
75
- (_a = this.process.stdin) === null || _a === void 0 ? void 0 : _a.on("error", (error) => {
71
+ (_a = this.process.stdin) === null || _a === void 0 ? void 0 : _a.on("error", errorListener = (error) => {
76
72
  if (!error.message.includes("EPIPE")) {
77
73
  this.log.error("%s: FFmpeg error: %s.", this.name(), error.message);
78
74
  }
79
75
  });
80
76
  // Handle logging output that gets sent to stderr.
81
- (_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (data) => {
82
- if (!started) {
83
- started = true;
77
+ (_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on("data", dataListener = (data) => {
78
+ if (!this.isStarted) {
79
+ this.isStarted = true;
80
+ this.isEnded = false;
84
81
  this.debug("%s: Received the first frame.", this.name());
85
82
  // Always remember to execute the callback once we're setup to let homebridge know we're streaming.
86
- if (callback) {
87
- callback();
83
+ if (this.callback) {
84
+ this.callback();
88
85
  }
89
86
  }
90
87
  // Debugging and additional logging, if requested.
91
- if (hasLogging || this.isVerbose || this.delegate.platform.config.debugAll) {
88
+ if (hasLogging || this.isVerbose || this.protectCamera.platform.config.debugAll) {
92
89
  data.toString().split(/\n/).forEach((line) => {
93
- this.log.info(line);
90
+ this.log.info("%s: %s", this.name(), line);
94
91
  });
95
92
  }
96
93
  });
94
+ // Make sure we update our state and cleanup after ourselves if we're ending a process.
95
+ (_c = this.process.stdout) === null || _c === void 0 ? void 0 : _c.once("close", () => {
96
+ var _a, _b, _c, _d;
97
+ this.isStarted = false;
98
+ this.isEnded = true;
99
+ (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.removeListener("error", errorListener);
100
+ (_d = (_c = this.process) === null || _c === void 0 ? void 0 : _c.stderr) === null || _d === void 0 ? void 0 : _d.removeListener("data", dataListener);
101
+ this.process = null;
102
+ });
103
+ this.isPrepared = true;
104
+ }
105
+ // Wait for our FFmpeg process to complete execution.
106
+ async start(commandLineArgs, callback, errorHandler) {
107
+ var _a, _b;
108
+ // If we haven't prepared our FFmpeg process, do so now.
109
+ if (!this.isPrepared) {
110
+ this.prepareProcess(commandLineArgs, callback);
111
+ if (!this.isPrepared) {
112
+ this.log.error("%s: Error preparing to run FFmpeg.", this.name());
113
+ return;
114
+ }
115
+ }
97
116
  try {
98
117
  // Execute the command line.
99
118
  await this.process;
@@ -102,15 +121,15 @@ class FfmpegProcess {
102
121
  // You might think this should be ExecaError, but ExecaError is a type, not a class, and instanceof
103
122
  // only operates on classes.
104
123
  if (!(error instanceof Error)) {
105
- this.log.error("Unknown error received while attempting to start FFmpeg: %s.", error);
124
+ this.log.error("%s: Unknown error received while attempting to start FFmpeg: %s.", this.name(), error);
106
125
  return;
107
126
  }
108
127
  // Recast our error object as an ExecaError.
109
128
  const execError = error;
110
129
  // Some utilities to streamline things.
111
130
  const logPrefix = this.name() + ": FFmpeg process ended ";
112
- const code = (_c = execError.exitCode) !== null && _c !== void 0 ? _c : null;
113
- const signal = (_d = execError.signal) !== null && _d !== void 0 ? _d : null;
131
+ const code = (_a = execError.exitCode) !== null && _a !== void 0 ? _a : null;
132
+ const signal = (_b = execError.signal) !== null && _b !== void 0 ? _b : null;
114
133
  // We asked FFmpeg to stop.
115
134
  if (execError.isCanceled) {
116
135
  this.debug(logPrefix + "(Expected).");
@@ -118,31 +137,29 @@ class FfmpegProcess {
118
137
  }
119
138
  // FFmpeg ended for another reason.
120
139
  const errorMessage = logPrefix + "(Error)." + (code === null ? "" : " Exit code: " + code.toString() + ".") + (signal === null ? "" : " Signal: " + signal + ".");
121
- this.log.error("%s: %s", this.name(), execError.message);
122
- // Stop the stream.
123
- this.delegate.stopStream(this.sessionId);
124
- // Temporarily increase logging verbosity.
125
- this.delegate.setVerboseFfmpeg();
126
- // Let homebridge know what happened and stop the stream if we've already started.
127
- if (!started && callback) {
128
- callback(new Error(errorMessage));
129
- return;
140
+ // this.log.error("%s: %s", this.name(), execError.message);
141
+ this.log.error("%s: FFmpeg failed with error: %s: %s", this.name(), signal, execError.signalDescription);
142
+ // Execute our error handler, if one is provided.
143
+ if (errorHandler) {
144
+ await errorHandler(errorMessage);
130
145
  }
131
- this.delegate.controller.forceStopStreamingSession(this.sessionId);
132
146
  }
133
147
  }
134
148
  // Cleanup after we're done.
135
149
  stop() {
150
+ var _a;
136
151
  // Cancel our process.
137
- this.process.cancel();
152
+ (_a = this.process) === null || _a === void 0 ? void 0 : _a.cancel();
138
153
  }
139
- // Grab the standard input.
140
- getStdin() {
141
- return this.process.stdin;
154
+ // Return the standard input for this process.
155
+ get stdin() {
156
+ var _a, _b;
157
+ return (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdin) !== null && _b !== void 0 ? _b : null;
142
158
  }
143
- // Grab the standard output.
144
- getStdout() {
145
- return this.process.stdout;
159
+ // Return the standard output for this process.
160
+ get stdout() {
161
+ var _a, _b;
162
+ return (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdout) !== null && _b !== void 0 ? _b : null;
146
163
  }
147
164
  // Validate whether or not we have a specific codec available to us in FFmpeg.
148
165
  static async codecEnabled(videoProcessor, codec, log) {
@@ -170,4 +187,256 @@ class FfmpegProcess {
170
187
  }
171
188
  }
172
189
  exports.FfmpegProcess = FfmpegProcess;
190
+ // FFmpeg streaming process management.
191
+ class FfmpegStreamingProcess extends FfmpegProcess {
192
+ // Create a new FFmpeg process instance.
193
+ constructor(delegate, sessionId, commandLineArgs, returnPort, callback) {
194
+ // Initialize our parent.
195
+ super(delegate.protectCamera);
196
+ this.delegate = delegate;
197
+ this.sessionId = sessionId;
198
+ // Create the return port for FFmpeg, if requested to do so. The only time we don't do this is when we're standing up
199
+ // a two-way audio stream - in that case, the audio work is done through RtpSplitter and not here.
200
+ if (returnPort) {
201
+ this.createSocket(returnPort);
202
+ }
203
+ void this.start(commandLineArgs, callback, async (errorMessage) => {
204
+ // Stop the stream.
205
+ await this.delegate.stopStream(this.sessionId);
206
+ // Temporarily increase logging verbosity.
207
+ this.delegate.setVerboseFfmpeg();
208
+ // Let homebridge know what happened and stop the stream if we've already started.
209
+ if (!this.isStarted && this.callback) {
210
+ this.callback(new Error(errorMessage));
211
+ return;
212
+ }
213
+ // Tell Homebridge to forcibly stop the streaming session.
214
+ this.delegate.controller.forceStopStreamingSession(this.sessionId);
215
+ });
216
+ }
217
+ // Create the port for FFmpeg to send data through.
218
+ createSocket(portInfo) {
219
+ let errorListener;
220
+ let messageListener;
221
+ const socket = (0, dgram_1.createSocket)(portInfo.addressVersion === "ipv6" ? "udp6" : "udp4");
222
+ // Cleanup after ourselves when the socket closes.
223
+ socket.once("close", () => {
224
+ if (this.streamTimeout) {
225
+ clearTimeout(this.streamTimeout);
226
+ }
227
+ socket.removeListener("error", errorListener);
228
+ socket.removeListener("message", messageListener);
229
+ });
230
+ // Handle potential network errors.
231
+ socket.on("error", errorListener = (error) => {
232
+ this.log.error("%s: Socket error: %s.", this.name(), error.name);
233
+ void this.delegate.stopStream(this.sessionId);
234
+ });
235
+ // Manage our video streams in case we haven't received a stop request, but we're in fact dead zombies.
236
+ socket.on("message", messageListener = () => {
237
+ // Clear our last canary.
238
+ if (this.streamTimeout) {
239
+ clearTimeout(this.streamTimeout);
240
+ }
241
+ // Set our new canary.
242
+ this.streamTimeout = setTimeout(() => {
243
+ this.debug("%s: video stream appears to be inactive for 5 seconds. Stopping stream.", this.name());
244
+ this.delegate.controller.forceStopStreamingSession(this.sessionId);
245
+ void this.delegate.stopStream(this.sessionId);
246
+ }, 5000);
247
+ });
248
+ // Bind to the port we're opening.
249
+ socket.bind(portInfo.port);
250
+ }
251
+ }
252
+ exports.FfmpegStreamingProcess = FfmpegStreamingProcess;
253
+ // FFmpeg HomeKit Streaming Video recording process management.
254
+ class FfmpegRecordingProcess extends FfmpegProcess {
255
+ // Create a new FFmpeg process instance.
256
+ constructor(device, recordingConfig, isAudioActive) {
257
+ // Initialize our parent.
258
+ super(device);
259
+ // Initialize our recording buffer.
260
+ this.recordingBuffer = [];
261
+ // Determine which H.264 profile HomeKit is expecting from us.
262
+ const requestedProfile = (recordingConfig.videoCodec.parameters.profile === 2 /* HIGH */) ? "high"
263
+ : (recordingConfig.videoCodec.parameters.profile === 1 /* MAIN */) ? "main" : "baseline";
264
+ const requestedLevel = (recordingConfig.videoCodec.parameters.level === 2 /* LEVEL4_0 */) ? "4.0"
265
+ : (recordingConfig.videoCodec.parameters.level === 1 /* LEVEL3_2 */) ? "3.2" : "3.1";
266
+ // Configure our video parameters for transcoding:
267
+ //
268
+ // -hide_banner: Suppress printing the startup banner in FFmpeg.
269
+ // -f mp4 Tell ffmpeg that it should expect an MP4-encoded input stream.
270
+ // -i pipe:0 Use standard input to get video data.
271
+ // -map 0:v Selects the first available video track from the stream. Protect actually maps audio
272
+ // and video tracks in opposite locations from where FFmpeg typically expects them. This
273
+ // setting is a more general solution than naming the track locations directly in case
274
+ // Protect changes this in the future.
275
+ // -vcodec libx264 Copy the stream withour reencoding it.
276
+ // -pix_fmt yuvj420p Use the yuvj420p pixel format, which is what Protect uses.
277
+ // -profile:v high Use the H.264 high profile when encoding, which provides for better stream quality and size efficiency.
278
+ // -preset veryfast Use the veryfast encoding preset in libx264, which provides a good balance of encoding speed and quality.
279
+ // -b:v bitrate The average bitrate to use for this stream. This is specified by HomeKit Secure Video.
280
+ // -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
281
+ // -maxrate bitrate The maximum bitrate tolerance, used with -bufsize. We set this to effectively create a constant bitrate.
282
+ // -force_key_frames expr:gte(t, n_forced * 4) Force a specific keyframe interval in the fMP4 stream we are generating.
283
+ // -fflags +genpts Generate a presentation timestamp (PTS) if there's a valid decoding timestamp (DTS) and PTS is missing.
284
+ // -reset_timestamps 1 Reset timestamps at the beginning of each segment to make the generated segments easier to consume.
285
+ // -movflags frag_keyframe+empty_moov+default_base_moof Start a new fragment at each keyframe, send the MOOV box at the beginning of the stream, and avoid
286
+ // writing absolute byte positions for the segments we send.
287
+ this.commandLineArgs = [
288
+ "-hide_banner",
289
+ "-f", "mp4",
290
+ "-i", "pipe:0",
291
+ "-map", "0:v",
292
+ "-vcodec", "libx264",
293
+ "-pix_fmt", "yuvj420p",
294
+ "-profile:v", requestedProfile,
295
+ "-level:v", requestedLevel,
296
+ "-preset", "veryfast",
297
+ "-b:v", recordingConfig.videoCodec.parameters.bitRate.toString() + "k",
298
+ "-bufsize", (2 * recordingConfig.videoCodec.parameters.bitRate).toString() + "k",
299
+ "-maxrate", recordingConfig.videoCodec.parameters.bitRate.toString() + "k",
300
+ "-force_key_frames", "expr:gte(t, n_forced * " + (recordingConfig.videoCodec.parameters.iFrameInterval / 1000).toString() + ")",
301
+ "-fflags", "+genpts",
302
+ "-reset_timestamps", "1",
303
+ "-movflags", "frag_keyframe+empty_moov+default_base_moof"
304
+ ];
305
+ if (isAudioActive) {
306
+ // Configure the audio portion of the command line. Options we use are:
307
+ //
308
+ // -map 0:a Selects the first available audio track from the stream. Protect actually maps audio
309
+ // and video tracks in opposite locations from where FFmpeg typically expects them. This
310
+ // setting is a more general solution than naming the track locations directly in case
311
+ // Protect changes this in the future.
312
+ // -acodec copy Copy the stream withour reencoding it.
313
+ this.commandLineArgs.push("-map", "0:a", "-acodec", "copy");
314
+ }
315
+ // Configure our video parameters for outputting our final stream:
316
+ //
317
+ // -f mp4 Tell ffmpeg that it should create an MP4-encoded output stream.
318
+ // pipe:1 Output the stream to standard output.
319
+ this.commandLineArgs.push("-f", "mp4", "pipe:1");
320
+ // Additional logging, but only if we're debugging.
321
+ if (device.platform.verboseFfmpeg) {
322
+ this.commandLineArgs.unshift("-loglevel", "level+verbose");
323
+ }
324
+ // Start the FFmpeg session.
325
+ void this.start();
326
+ }
327
+ // Prepare and start our FFmpeg process.
328
+ prepareProcess() {
329
+ var _a, _b;
330
+ // Call our parent to get started.
331
+ super.prepareProcess();
332
+ // Initialize our variables that we need to process incoming FFmpeg packets.
333
+ let header = Buffer.alloc(0);
334
+ let bufferRemaining = Buffer.alloc(0);
335
+ let dataLength = 0;
336
+ let type = "";
337
+ // Process FFmpeg output and parse out the fMP4 stream it's generating for HomeKit Secure Video.
338
+ (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdout) === null || _b === void 0 ? void 0 : _b.on("data", (buffer) => {
339
+ // If we have anything left from the last buffer we processed, prepend it to this buffer.
340
+ if (bufferRemaining.length > 0) {
341
+ buffer = Buffer.concat([bufferRemaining, buffer]);
342
+ bufferRemaining = Buffer.alloc(0);
343
+ }
344
+ let offset = 0;
345
+ // FFmpeg is outputting an fMP4 stream that's suitable for HomeKit Secure Video. However, we can't just
346
+ // pass this stream directly back to HomeKit since we're using a generator-based API to send packets back to
347
+ // HKSV. Here, we take on the task of parsing the fMP4 stream that's being generated and split it up into the
348
+ // MP4 boxes that HAP-NodeJS is ultimately expecting.
349
+ for (;;) {
350
+ let data;
351
+ // The MP4 container format is well-documented format that is based around the concept of boxes. A box (or atom as they
352
+ // used to be called), is at the center of the MP4 format. It's composed of an 8-byte header, followed by the data payload
353
+ // it carries.
354
+ // No existing header, let's start a new box.
355
+ if (!header.length) {
356
+ // Grab the header. The first four bytes represents the length of the entire box. Second four bytes represent the box type.
357
+ header = buffer.slice(0, 8);
358
+ // Now we retrieve the length of the box and subtract the length of the header to get the length of the data portion of the box.
359
+ dataLength = header.readUInt32BE(0) - 8;
360
+ // Get the type of the box. This is always a string and has a funky history to it that makes for an interesting read!
361
+ type = header.slice(4).toString();
362
+ // Finally, we get the data portion of the box.
363
+ data = buffer.slice(8, dataLength + 8);
364
+ offset = 8;
365
+ }
366
+ else {
367
+ // Grab the data from our buffer.
368
+ data = buffer.slice(0, dataLength);
369
+ offset = 0;
370
+ }
371
+ // If we don't have enough data in this buffer, save what we have for the next buffer we see and append it there.
372
+ if (data.length < (dataLength - offset)) {
373
+ bufferRemaining = data;
374
+ break;
375
+ }
376
+ // Add it to our queue to be eventually pushed out through our generator function.
377
+ this.recordingBuffer.push({ data: data, header: header, length: dataLength, type: type });
378
+ // Prepare to start a new box for the next buffer that we will be processing.
379
+ data = Buffer.alloc(0);
380
+ header = Buffer.alloc(0);
381
+ type = "";
382
+ // We've parsed an entire box, and there's no more data in this buffer to parse.
383
+ if (buffer.length === (offset + dataLength)) {
384
+ dataLength = 0;
385
+ break;
386
+ }
387
+ // If there's anything left in the buffer, move us to the new box and let's keep iterating.
388
+ buffer = buffer.slice(offset + dataLength);
389
+ dataLength = 0;
390
+ }
391
+ });
392
+ }
393
+ // Generate complete segments from an FFmpeg output stream that HomeKit Secure Video can process.
394
+ async *generator() {
395
+ let segment = [];
396
+ // Loop forever, generating either FTYP/MOOV box pairs or MOOF/MDAT box pairs for HomeKit Secure Video.
397
+ for (;;) {
398
+ // FFmpeg has finished it's output - we're done.
399
+ if (this.isEnded) {
400
+ break;
401
+ }
402
+ // If we haven't seen any output from FFmpeg yet, sleep for a very short period of time to wait for it.
403
+ // You might think there should be a longer sleep interval here, given the typical HKSV-requested segment
404
+ // size, but since we have a several-second buffer that gets fed to FFmpeg on startup, FFmpeg is likely to
405
+ // generate output very quickly after startup.
406
+ if (!this.isStarted) {
407
+ // eslint-disable-next-line no-await-in-loop
408
+ await this.nvr.sleep(100);
409
+ continue;
410
+ }
411
+ // Grab the next fMP4 box from our buffer.
412
+ const box = this.recordingBuffer.shift();
413
+ // If the buffer is empty, sleep for a second. We sleep a longer interval here because the buffer is likely
414
+ // to populate no more than once a second, and in reality, more likely longer than that in most cases. Smaller
415
+ // boxes (e.g. MOOF) will be buffered faster than larger ones like MDAT that carry the bulk of the audio / video
416
+ // data.
417
+ if (!box) {
418
+ // eslint-disable-next-line no-await-in-loop
419
+ await this.nvr.sleep(1000);
420
+ continue;
421
+ }
422
+ // Queue up this fMP4 box to send back to HomeKit.
423
+ segment.push(box.header, box.data);
424
+ // What we want to send are two types of complete segments:
425
+ //
426
+ // - a complete MOOV box, usually with an accompanying FTYP box, that's sent at the very
427
+ // beginning of any valid fMP4 stream. HomeKit Secure Video looks for this before anything
428
+ // else.
429
+ //
430
+ // - a complete MOOF/MDAT pair. MOOF describes XXX and MDAT describes the actual audio and video
431
+ // data related to that segment.
432
+ //
433
+ // Once we see these, we combine all the segments in our queue to send back to HomeKit.
434
+ if ((box.type === "moov") || (box.type === "mdat")) {
435
+ yield Buffer.concat(segment);
436
+ segment = [];
437
+ }
438
+ }
439
+ }
440
+ }
441
+ exports.FfmpegRecordingProcess = FfmpegRecordingProcess;
173
442
  //# sourceMappingURL=protect-ffmpeg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protect-ffmpeg.js","sourceRoot":"","sources":["../src/protect-ffmpeg.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protect-ffmpeg.js","sourceRoot":"","sources":["../src/protect-ffmpeg.ts"],"names":[],"mappings":"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"}