homebridge-nest-accfactory 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/camera.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Nest Cameras
2
2
  // Part of homebridge-nest-accfactory
3
3
  //
4
- // Code version 30/9/2024
4
+ // Code version 10/11/2024
5
5
  // Mark Hulskamp
6
6
  'use strict';
7
7
 
@@ -321,10 +321,11 @@ export default class NestCamera extends HomeKitDevice {
321
321
  return;
322
322
  }
323
323
 
324
- // Build our ffmpeg command string for recording the video/audio stream
324
+ // Build our ffmpeg command string for the liveview video/audio stream
325
325
  let commandLine = [
326
326
  '-hide_banner',
327
327
  '-nostats',
328
+ '-use_wallclock_as_timestamps 1',
328
329
  '-fflags +discardcorrupt',
329
330
  '-max_delay 500000',
330
331
  '-flags low_delay',
@@ -337,10 +338,22 @@ export default class NestCamera extends HomeKitDevice {
337
338
  this.deviceData.audio_enabled === true &&
338
339
  this.controller.recordingManagement.recordingManagementService.getCharacteristic(this.hap.Characteristic.RecordingAudioActive)
339
340
  .value === this.hap.Characteristic.RecordingAudioActive.ENABLE &&
340
- ((this.streamer?.codecs?.audio === 'aac' && this.deviceData?.ffmpeg?.libfdk_aac === true) ||
341
- (this.streamer?.codecs?.audio === 'opus' && this.deviceData?.ffmpeg?.libopus === true))
341
+ this.streamer?.codecs?.audio === 'aac' &&
342
+ this.deviceData?.ffmpeg?.libfdk_aac === true
342
343
  ) {
343
- // audio data only on extra pipe created in spawn command
344
+ // Audio data only on extra pipe created in spawn command
345
+ commandLine.push('-f aac -i pipe:3');
346
+ includeAudio = true;
347
+ }
348
+
349
+ if (
350
+ this.deviceData.audio_enabled === true &&
351
+ this.controller.recordingManagement.recordingManagementService.getCharacteristic(this.hap.Characteristic.RecordingAudioActive)
352
+ .value === this.hap.Characteristic.RecordingAudioActive.ENABLE &&
353
+ this.streamer?.codecs?.audio === 'opus' &&
354
+ this.deviceData?.ffmpeg?.libopus === true
355
+ ) {
356
+ // Audio data only on extra pipe created in spawn command
344
357
  commandLine.push('-i pipe:3');
345
358
  includeAudio = true;
346
359
  }
@@ -367,11 +380,11 @@ export default class NestCamera extends HomeKitDevice {
367
380
  '-filter:v fps=fps=' + this.#recordingConfig.videoCodec.resolution[2],
368
381
  '-g:v ' + (this.#recordingConfig.videoCodec.resolution[2] * this.#recordingConfig.videoCodec.parameters.iFrameInterval) / 1000,
369
382
  '-b:v ' + this.#recordingConfig.videoCodec.parameters.bitRate + 'k',
383
+ '-bufsize ' + 2 * this.#recordingConfig.videoCodec.parameters.bitRate + 'k',
370
384
  '-fps_mode passthrough',
371
- '-movflags frag_keyframe+empty_moov+default_base_moof',
372
385
  '-reset_timestamps 1',
373
386
  '-video_track_timescale 90000',
374
- '-bufsize ' + 2 * this.#recordingConfig.videoCodec.parameters.bitRate + 'k',
387
+ '-movflags frag_keyframe+empty_moov+default_base_moof',
375
388
  );
376
389
 
377
390
  // We have seperate video and audio streams that need to be muxed together if audio enabled
@@ -390,23 +403,26 @@ export default class NestCamera extends HomeKitDevice {
390
403
 
391
404
  commandLine.push('-f mp4 pipe:1'); // output to stdout in mp4
392
405
 
393
- this.#hkSessions[sessionID] = {};
394
- this.#hkSessions[sessionID].ffmpeg = child_process.spawn(this.deviceData.ffmpeg.binary, commandLine.join(' ').split(' '), {
406
+ // Start our ffmpeg recording process and stream from our streamer
407
+ // video is pipe #1
408
+ // audio is pipe #3 if including audio
409
+ this?.log?.debug &&
410
+ this.log.debug(
411
+ 'ffmpeg process for recording stream from "%s" will be called using the following commandline',
412
+ this.deviceData.description,
413
+ commandLine.join(' ').toString(),
414
+ );
415
+ let ffmpegRecording = child_process.spawn(this.deviceData.ffmpeg.binary, commandLine.join(' ').split(' '), {
395
416
  env: process.env,
396
417
  stdio: ['pipe', 'pipe', 'pipe', includeAudio === true ? 'pipe' : ''],
397
- }); // Extra pipe, #3 for audio data
398
-
399
- this.#hkSessions[sessionID].video = this.#hkSessions[sessionID].ffmpeg.stdin; // Video data on stdio pipe for ffmpeg
400
- this.#hkSessions[sessionID].audio = this.#hkSessions[sessionID]?.ffmpeg?.stdio?.[3]
401
- ? this.#hkSessions[sessionID].ffmpeg.stdio[3]
402
- : null; // Audio data on extra pipe for ffmpeg or null if audio recording disabled
418
+ });
403
419
 
404
420
  // Process FFmpeg output and parse out the fMP4 stream it's generating for HomeKit Secure Video.
405
421
  let mp4FragmentData = [];
406
- this.#hkSessions[sessionID].mp4boxes = [];
407
- this.#hkSessions[sessionID].eventEmitter = new EventEmitter();
422
+ let mp4boxes = [];
423
+ let eventEmitter = new EventEmitter();
408
424
 
409
- this.#hkSessions[sessionID].ffmpeg.stdout.on('data', (data) => {
425
+ ffmpegRecording.stdout.on('data', (data) => {
410
426
  // Process the mp4 data from our socket connection and convert into mp4 fragment boxes we need
411
427
  mp4FragmentData = mp4FragmentData.length === 0 ? data : Buffer.concat([mp4FragmentData, data]);
412
428
  while (mp4FragmentData.length >= 8) {
@@ -419,13 +435,13 @@ export default class NestCamera extends HomeKitDevice {
419
435
  }
420
436
 
421
437
  // Add it to our queue to be pushed out through the generator function.
422
- if (typeof this.#hkSessions?.[sessionID]?.mp4boxes === 'object' && this.#hkSessions?.[sessionID]?.eventEmitter !== undefined) {
423
- this.#hkSessions[sessionID].mp4boxes.push({
438
+ if (Array.isArray(mp4boxes) === true && eventEmitter !== undefined) {
439
+ mp4boxes.push({
424
440
  header: mp4FragmentData.slice(0, 8),
425
441
  type: mp4FragmentData.slice(4, 8).toString(),
426
442
  data: mp4FragmentData.slice(8, boxSize),
427
443
  });
428
- this.#hkSessions[sessionID].eventEmitter.emit(MP4BOX);
444
+ eventEmitter.emit(MP4BOX);
429
445
  }
430
446
 
431
447
  // Remove the section of data we've just processed from our buffer
@@ -433,38 +449,38 @@ export default class NestCamera extends HomeKitDevice {
433
449
  }
434
450
  });
435
451
 
436
- this.#hkSessions[sessionID].ffmpeg.on('exit', (code, signal) => {
452
+ ffmpegRecording.on('exit', (code, signal) => {
437
453
  if (signal !== 'SIGKILL' || signal === null) {
438
454
  this?.log?.error &&
439
455
  this.log.error('ffmpeg recording process for "%s" stopped unexpectedly. Exit code was "%s"', this.deviceData.description, code);
440
456
  }
441
- if (typeof this.#hkSessions[sessionID]?.audio?.end === 'function') {
442
- // Tidy up our created extra pipe
443
- this.#hkSessions[sessionID].audio.end();
457
+
458
+ if (this.#hkSessions?.[sessionID] !== undefined) {
459
+ delete this.#hkSessions[sessionID];
444
460
  }
445
461
  });
446
462
 
447
463
  // eslint-disable-next-line no-unused-vars
448
- this.#hkSessions[sessionID].ffmpeg.on('error', (error) => {
464
+ ffmpegRecording.on('error', (error) => {
449
465
  // Empty
450
466
  });
451
467
 
452
- // ffmpeg outputs to stderr
453
- /*
454
- this.#hkSessions[sessionID].ffmpeg.stderr.on('data', (data) => {
455
- if (data.toString().includes('frame=') === false) {
456
- // Monitor ffmpeg output while testing. Use 'ffmpeg as a debug option'
468
+ // ffmpeg console output is via stderr
469
+ ffmpegRecording.stderr.on('data', (data) => {
470
+ if (data.toString().includes('frame=') === false && this.deviceData?.ffmpeg?.debug === true) {
471
+ // Monitor ffmpeg output
457
472
  this?.log?.debug && this.log.debug(data.toString());
458
473
  }
459
474
  });
460
- */
461
475
 
476
+ // Start the appropriate streamer
462
477
  this.streamer !== undefined &&
463
- this.streamer.startRecordStream(
464
- sessionID,
465
- this.#hkSessions[sessionID].ffmpeg.stdin,
466
- this.#hkSessions[sessionID]?.ffmpeg?.stdio?.[3] && includeAudio === true ? this.#hkSessions[sessionID].ffmpeg.stdio[3] : null,
467
- );
478
+ this.streamer.startRecordStream(sessionID, ffmpegRecording.stdin, ffmpegRecording?.stdio?.[3] ? ffmpegRecording.stdio[3] : null);
479
+
480
+ // Store our ffmpeg sessions
481
+ this.#hkSessions[sessionID] = {};
482
+ this.#hkSessions[sessionID].eventEmitter = eventEmitter;
483
+ this.#hkSessions[sessionID].ffmpeg = ffmpegRecording; // Store ffmpeg process ID
468
484
 
469
485
  this?.log?.info &&
470
486
  this.log.info('Started recording from "%s" %s', this.deviceData.description, includeAudio === false ? 'without audio' : '');
@@ -473,26 +489,19 @@ export default class NestCamera extends HomeKitDevice {
473
489
  // HAP-NodeJS cancels this async generator function when recording completes also
474
490
  let segment = [];
475
491
  for (;;) {
476
- if (
477
- this.#hkSessions?.[sessionID] === undefined ||
478
- this.#hkSessions?.[sessionID]?.ffmpeg === undefined ||
479
- this.#hkSessions?.[sessionID]?.mp4boxes === undefined ||
480
- this.#hkSessions?.[sessionID]?.eventEmitter === undefined
481
- ) {
492
+ if (this.#hkSessions?.[sessionID] === undefined || this.#hkSessions?.[sessionID]?.ffmpeg === undefined) {
482
493
  // Our session object is not present
483
494
  // ffmpeg recorder process is not present
484
- // the mp4box array is not present
485
- // eventEmitter is not present
486
495
  // so finish up the loop
487
496
  break;
488
497
  }
489
498
 
490
- if (this.#hkSessions?.[sessionID]?.mp4boxes?.length === 0 && this.#hkSessions?.[sessionID]?.eventEmitter !== undefined) {
499
+ if (mp4boxes?.length === 0 && eventEmitter !== undefined) {
491
500
  // since the ffmpeg recorder process hasn't notified us of any mp4 fragment boxes, wait until there are some
492
- await EventEmitter.once(this.#hkSessions[sessionID].eventEmitter, MP4BOX);
501
+ await EventEmitter.once(eventEmitter, MP4BOX);
493
502
  }
494
503
 
495
- let mp4box = this.#hkSessions?.[sessionID]?.mp4boxes.shift();
504
+ let mp4box = mp4boxes.shift();
496
505
  if (typeof mp4box !== 'object') {
497
506
  // Not an mp4 fragment box, so try again
498
507
  continue;
@@ -789,19 +798,17 @@ export default class NestCamera extends HomeKitDevice {
789
798
  // Start our ffmpeg streaming process and stream from our streamer
790
799
  // video is pipe #1
791
800
  // audio is pipe #3 if including audio
801
+ this?.log?.debug &&
802
+ this.log.debug(
803
+ 'ffmpeg process for live streaming from "%s" will be called using the following commandline',
804
+ this.deviceData.description,
805
+ commandLine.join(' ').toString(),
806
+ );
792
807
  let ffmpegStreaming = child_process.spawn(this.deviceData.ffmpeg.binary, commandLine.join(' ').split(' '), {
793
808
  env: process.env,
794
809
  stdio: ['pipe', 'pipe', 'pipe', includeAudio === true ? 'pipe' : ''],
795
810
  });
796
811
 
797
- // ffmpeg console output is via stderr
798
- ffmpegStreaming.stderr.on('data', (data) => {
799
- if (data.toString().includes('frame=') === false) {
800
- // Monitor ffmpeg output while testing. Use 'ffmpeg as a debug option'
801
- this?.log?.debug && this.log.debug(data.toString());
802
- }
803
- });
804
-
805
812
  ffmpegStreaming.on('exit', (code, signal) => {
806
813
  if (signal !== 'SIGKILL' || signal === null) {
807
814
  this?.log?.error &&
@@ -816,6 +823,14 @@ export default class NestCamera extends HomeKitDevice {
816
823
  }
817
824
  });
818
825
 
826
+ // ffmpeg console output is via stderr
827
+ ffmpegStreaming.stderr.on('data', (data) => {
828
+ if (data.toString().includes('frame=') === false && this.deviceData?.ffmpeg?.debug === true) {
829
+ // Monitor ffmpeg output
830
+ this?.log?.debug && this.log.debug(data.toString());
831
+ }
832
+ });
833
+
819
834
  // eslint-disable-next-line no-unused-vars
820
835
  ffmpegStreaming.on('error', (error) => {
821
836
  // Empty
@@ -875,6 +890,12 @@ export default class NestCamera extends HomeKitDevice {
875
890
 
876
891
  commandLine.push('-f data pipe:1');
877
892
 
893
+ this?.log?.debug &&
894
+ this.log.debug(
895
+ 'ffmpeg process for talkback on "%s" will be called using the following commandline',
896
+ this.deviceData.description,
897
+ commandLine.join(' ').toString(),
898
+ );
878
899
  ffmpegAudioTalkback = child_process.spawn(this.deviceData.ffmpeg.binary, commandLine.join(' ').split(' '), {
879
900
  env: process.env,
880
901
  });
@@ -900,7 +921,10 @@ export default class NestCamera extends HomeKitDevice {
900
921
 
901
922
  // ffmpeg console output is via stderr
902
923
  ffmpegAudioTalkback.stderr.on('data', (data) => {
903
- this?.log?.debug && this.log.debug(data.toString());
924
+ if (data.toString().includes('frame=') === false && this.deviceData?.ffmpeg?.debug === true) {
925
+ // Monitor ffmpeg output
926
+ this?.log?.debug && this.log.debug(data.toString());
927
+ }
904
928
  });
905
929
 
906
930
  // Write out SDP configuration
package/dist/doorbell.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Nest Doorbell(s)
2
2
  // Part of homebridge-nest-accfactory
3
3
  //
4
- // Code version 27/9/2024
4
+ // Code version 11/10/2024
5
5
  // Mark Hulskamp
6
6
  'use strict';
7
7
 
@@ -55,7 +55,7 @@ export default class NestDoorbell extends NestCamera {
55
55
  // No longer required to have the switch service
56
56
  // This is to handle Homebridge cached restored accessories and if configuration options have changed
57
57
  this.accessory.removeService(this.switchService);
58
- this.switchService === undefined;
58
+ this.switchService = undefined;
59
59
  }
60
60
 
61
61
  // Create extra details for output
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
  //
17
17
  // Supports both Nest REST and Protobuf APIs for communication
18
18
  //
19
- // Code version 12/9/2024
19
+ // Code version 7/10/2024
20
20
  // Mark Hulskamp
21
21
  'use strict';
22
22
 
package/dist/nexustalk.js CHANGED
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // Credit to https://github.com/Brandawg93/homebridge-nest-cam for the work on the Nest Camera comms code on which this is based
7
7
  //
8
- // Code version 29/9/2024
8
+ // Code version 20/11/2024
9
9
  // Mark Hulskamp
10
10
  'use strict';
11
11
 
@@ -325,7 +325,7 @@ export default class NexusTalk extends Streamer {
325
325
  requireConnectedCamera: false,
326
326
  userAgent: USERAGENT,
327
327
  deviceId: crypto.randomUUID(),
328
- ClientType: 'IOS',
328
+ clientType: 'IOS',
329
329
  authoriseRequest: authoriseRequest,
330
330
  }),
331
331
  ).finish();
@@ -416,7 +416,7 @@ export default class NexusTalk extends Streamer {
416
416
  this.connect(); // try reconnection
417
417
  });
418
418
  this.close(false); // Close existing socket
419
- }, 8000);
419
+ }, 10000);
420
420
 
421
421
  // Handle video packet
422
422
  if (decodedMessage?.channelId !== undefined && decodedMessage.channelId === this.video?.id) {
@@ -474,7 +474,7 @@ export default class NexusTalk extends Streamer {
474
474
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
475
475
  //let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackBegin').decode(payload).toJSON();
476
476
  this.audio.talking = true;
477
- this?.log?.debug && this.log.debug(Streamer.TALKINGSTART, this.uuid);
477
+ this?.log?.debug && this.log.debug('Talking started on uuid "%s"', this.uuid);
478
478
  }
479
479
  }
480
480
 
package/dist/protect.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Nest Protect
2
2
  // Part of homebridge-nest-accfactory
3
3
  //
4
- // Code version 27/9/2024
4
+ // Code version 5/10/2024
5
5
  // Mark Hulskamp
6
6
  'use strict';
7
7
 
@@ -43,12 +43,6 @@ export default class NestProtect extends HomeKitDevice {
43
43
  if (this.carbonMonoxideService === undefined) {
44
44
  this.carbonMonoxideService = this.accessory.addService(this.hap.Service.CarbonMonoxideSensor, '', 1);
45
45
  }
46
- if (this.carbonMonoxideService.testCharacteristic(this.hap.Characteristic.StatusActive) === false) {
47
- this.carbonMonoxideService.addCharacteristic(this.hap.Characteristic.StatusActive);
48
- }
49
- if (this.carbonMonoxideService.testCharacteristic(this.hap.Characteristic.StatusFault) === false) {
50
- this.carbonMonoxideService.addCharacteristic(this.hap.Characteristic.StatusFault);
51
- }
52
46
 
53
47
  // Setup battery service if not already present on the accessory
54
48
  this.batteryService = this.accessory.getService(this.hap.Service.Battery);
@@ -109,13 +103,12 @@ export default class NestProtect extends HomeKitDevice {
109
103
  );
110
104
 
111
105
  // Update smoke details
112
- // If Nest isn't online or removed from base, report in HomeKit
106
+ // If protect isn't online, removed from base, replacement date past, report in HomeKit
113
107
  this.smokeService.updateCharacteristic(
114
108
  this.hap.Characteristic.StatusActive,
115
- deviceData.online === true && deviceData.removed_from_base === false,
109
+ deviceData.online === true && deviceData.removed_from_base === false && Math.floor(Date.now() / 1000) <= deviceData.replacement_date,
116
110
  );
117
111
 
118
- // General fault if replacement date past or Nest isn't online or removed from base
119
112
  this.smokeService.updateCharacteristic(
120
113
  this.hap.Characteristic.StatusFault,
121
114
  deviceData.online === true && deviceData.removed_from_base === false && Math.floor(Date.now() / 1000) <= deviceData.replacement_date
@@ -139,23 +132,9 @@ export default class NestProtect extends HomeKitDevice {
139
132
  }
140
133
 
141
134
  // Update carbon monoxide details
142
- // If Nest isn't online or removed from base, report in HomeKit
143
- this.carbonMonoxideService.updateCharacteristic(
144
- this.hap.Characteristic.StatusActive,
145
- deviceData.online === true && deviceData.removed_from_base === false,
146
- );
147
-
148
- // General fault if replacement date past or Nest isn't online or removed from base
149
- this.carbonMonoxideService.updateCharacteristic(
150
- this.hap.Characteristic.StatusFault,
151
- deviceData.online === true && deviceData.removed_from_base === false && Math.floor(Date.now() / 1000) <= deviceData.replacement_date
152
- ? this.hap.Characteristic.StatusFault.NO_FAULT
153
- : this.hap.Characteristic.StatusFault.GENERAL_FAULT,
154
- );
155
-
156
135
  this.carbonMonoxideService.updateCharacteristic(
157
136
  this.hap.Characteristic.CarbonMonoxideDetected,
158
- deviceData.co_status === 2
137
+ deviceData.co_status !== 0
159
138
  ? this.hap.Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL
160
139
  : this.hap.Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL,
161
140
  );
@@ -170,22 +149,11 @@ export default class NestProtect extends HomeKitDevice {
170
149
 
171
150
  // Update motion service if present
172
151
  if (this.motionService !== undefined) {
173
- // Motion detect if auto_away = false. Not supported on battery powered Nest Protects
174
- // If Nest isn't online or removed from base, report in HomeKit
175
- this.motionService.updateCharacteristic(
176
- this.hap.Characteristic.StatusActive,
177
- deviceData.online === true && deviceData.removed_from_base === false,
178
- );
179
-
180
- // General fault if replacement date past or Nest isn't online or removed from base
181
- this.motionService.updateCharacteristic(
182
- this.hap.Characteristic.StatusFault,
183
- deviceData.online === true && deviceData.removed_from_base === false && Math.floor(Date.now() / 1000) <= deviceData.replacement_date
184
- ? this.hap.Characteristic.StatusFault.NO_FAULT
185
- : this.hap.Characteristic.StatusFault.GENERAL_FAULT,
186
- );
187
-
188
- this.motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, deviceData.detected_motion === false);
152
+ this.motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, deviceData.detected_motion === true);
153
+
154
+ if (deviceData.detected_motion === true && this.deviceData.detected_motion === false) {
155
+ this?.log?.info && this.log.info('Motion detected in "%s"', deviceData.description);
156
+ }
189
157
 
190
158
  // Log motion to history only if changed to previous recording
191
159
  if (deviceData.detected_motion !== this.deviceData.detected_motion && typeof this.historyService?.addHistory === 'function') {
@@ -197,7 +165,11 @@ export default class NestProtect extends HomeKitDevice {
197
165
  }
198
166
 
199
167
  // Notify Eve App of device status changes if linked
200
- if (this.deviceData.eveHistory === true && typeof this.historyService?.updateEveHome === 'function') {
168
+ if (
169
+ this.deviceData.eveHistory === true &&
170
+ this.smokeService !== undefined &&
171
+ typeof this.historyService?.updateEveHome === 'function'
172
+ ) {
201
173
  // Update our internal data with properties Eve will need to process
202
174
  this.deviceData.latest_alarm_test = deviceData.latest_alarm_test;
203
175
  this.deviceData.self_test_in_progress = deviceData.self_test_in_progress;
@@ -210,7 +182,7 @@ export default class NestProtect extends HomeKitDevice {
210
182
  }
211
183
 
212
184
  #EveHomeGetcommand(EveHomeGetData) {
213
- // Pass back extra data for Eve Smoke this.hap.CharacteristicEventTypes.GET process command
185
+ // Pass back extra data for Eve Smoke onGet() to process command
214
186
  // Data will already be an object, our only job is to add/modify to it
215
187
  if (typeof EveHomeGetData === 'object') {
216
188
  EveHomeGetData.lastalarmtest = this.deviceData.latest_alarm_test;
package/dist/streamer.js CHANGED
@@ -17,7 +17,7 @@
17
17
  //
18
18
  // blankAudio - Buffer containing a blank audio segment for the type of audio being used
19
19
  //
20
- // Code version 29/9/2024
20
+ // Code version 20/11/2024
21
21
  // Mark Hulskamp
22
22
  'use strict';
23
23
 
@@ -143,11 +143,7 @@ export default class Streamer {
143
143
  if (output.type === 'live' || output.type === 'record') {
144
144
  let packet = output.buffer.shift();
145
145
  if (packet?.type === 'video' && typeof output?.video?.write === 'function') {
146
- // H264 NAL Units '0001' are required to be added to beginning of any video data we output
147
- // If this is missing, add on beginning of data packet
148
- if (packet.data.indexOf(H264NALSTARTCODE) !== 0) {
149
- packet.data = Buffer.concat([H264NALSTARTCODE, packet.data]);
150
- }
146
+ packet.data = Buffer.concat([H264NALSTARTCODE, packet.data]);
151
147
  output.video.write(packet.data);
152
148
  }
153
149
  if (packet?.type === 'audio' && typeof output?.audio?.write === 'function') {
@@ -361,9 +357,14 @@ export default class Streamer {
361
357
  return;
362
358
  }
363
359
 
360
+ if (data.indexOf(H264NALSTARTCODE) === 0) {
361
+ // Strip H264 start code from input buffer. We'll handle this later
362
+ data = data.subarray(H264NALSTARTCODE.length);
363
+ }
364
+
364
365
  Object.values(this.#outputs).forEach((output) => {
365
366
  output.buffer.push({
366
- time: Date.now(), // Timestamp of when tgis was added to buffer
367
+ time: Date.now(), // Timestamp of when this was added to buffer
367
368
  type: type,
368
369
  data: data,
369
370
  });