homebridge-eufy-security 2.2.4 → 2.3.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 (102) hide show
  1. package/CHANGELOG.md +39 -11
  2. package/README.md +26 -25
  3. package/dist/accessories/CameraAccessory.d.ts +55 -21
  4. package/dist/accessories/CameraAccessory.d.ts.map +1 -1
  5. package/dist/accessories/CameraAccessory.js +460 -214
  6. package/dist/accessories/CameraAccessory.js.map +1 -1
  7. package/dist/accessories/Device.d.ts +3 -3
  8. package/dist/accessories/Device.d.ts.map +1 -1
  9. package/dist/accessories/Device.js +15 -0
  10. package/dist/accessories/Device.js.map +1 -1
  11. package/dist/accessories/DoorbellCameraAccessory.d.ts +4 -1
  12. package/dist/accessories/DoorbellCameraAccessory.d.ts.map +1 -1
  13. package/dist/accessories/DoorbellCameraAccessory.js +65 -4
  14. package/dist/accessories/DoorbellCameraAccessory.js.map +1 -1
  15. package/dist/accessories/SmartLockAccessory.d.ts +2 -2
  16. package/dist/accessories/StationAccessory.d.ts +4 -0
  17. package/dist/accessories/StationAccessory.d.ts.map +1 -1
  18. package/dist/accessories/StationAccessory.js +64 -7
  19. package/dist/accessories/StationAccessory.js.map +1 -1
  20. package/dist/config.d.ts +4 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/controller/LocalLivestreamManager.d.ts +3 -8
  23. package/dist/controller/LocalLivestreamManager.d.ts.map +1 -1
  24. package/dist/controller/LocalLivestreamManager.js +4 -47
  25. package/dist/controller/LocalLivestreamManager.js.map +1 -1
  26. package/dist/controller/SnapshotManager.d.ts +1 -1
  27. package/dist/controller/SnapshotManager.d.ts.map +1 -1
  28. package/dist/controller/SnapshotManager.js +23 -1
  29. package/dist/controller/SnapshotManager.js.map +1 -1
  30. package/dist/controller/recordingDelegate.d.ts +29 -0
  31. package/dist/controller/recordingDelegate.d.ts.map +1 -0
  32. package/dist/controller/recordingDelegate.js +186 -0
  33. package/dist/controller/recordingDelegate.js.map +1 -0
  34. package/dist/controller/streamingDelegate.d.ts +6 -6
  35. package/dist/controller/streamingDelegate.d.ts.map +1 -1
  36. package/dist/controller/streamingDelegate.js +21 -60
  37. package/dist/controller/streamingDelegate.js.map +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/platform.d.ts +6 -0
  41. package/dist/platform.d.ts.map +1 -1
  42. package/dist/platform.js +112 -66
  43. package/dist/platform.js.map +1 -1
  44. package/dist/utils/EufyClientInteractor.d.ts +36 -0
  45. package/dist/utils/EufyClientInteractor.d.ts.map +1 -0
  46. package/dist/utils/EufyClientInteractor.js +357 -0
  47. package/dist/utils/EufyClientInteractor.js.map +1 -0
  48. package/dist/utils/Talkback.d.ts +0 -1
  49. package/dist/utils/Talkback.d.ts.map +1 -1
  50. package/dist/utils/configTypes.d.ts +15 -5
  51. package/dist/utils/configTypes.d.ts.map +1 -1
  52. package/dist/utils/experimental.d.ts +4 -0
  53. package/dist/utils/experimental.d.ts.map +1 -0
  54. package/dist/utils/experimental.js +49 -0
  55. package/dist/utils/experimental.js.map +1 -0
  56. package/dist/utils/ffmpeg.d.ts +30 -4
  57. package/dist/utils/ffmpeg.d.ts.map +1 -1
  58. package/dist/utils/ffmpeg.js +322 -30
  59. package/dist/utils/ffmpeg.js.map +1 -1
  60. package/dist/utils/interfaces.d.ts +13 -0
  61. package/dist/utils/interfaces.d.ts.map +1 -0
  62. package/dist/utils/interfaces.js +12 -0
  63. package/dist/utils/interfaces.js.map +1 -0
  64. package/homebridge-ui/configui/app/accessory.d.ts +1 -1
  65. package/homebridge-ui/configui/app/accessory.d.ts.map +1 -1
  66. package/homebridge-ui/configui/app/util/types.d.ts +2 -2
  67. package/homebridge-ui/configui/app/util/types.d.ts.map +1 -1
  68. package/homebridge-ui/configui/app/util/types.js +1 -1
  69. package/homebridge-ui/configui/app/util/types.js.map +1 -1
  70. package/homebridge-ui/plugin/utils/EufyClientInteractor.d.ts +36 -0
  71. package/homebridge-ui/plugin/utils/EufyClientInteractor.d.ts.map +1 -0
  72. package/homebridge-ui/plugin/utils/EufyClientInteractor.js +357 -0
  73. package/homebridge-ui/plugin/utils/EufyClientInteractor.js.map +1 -0
  74. package/homebridge-ui/plugin/utils/experimental.d.ts +4 -0
  75. package/homebridge-ui/plugin/utils/experimental.d.ts.map +1 -0
  76. package/homebridge-ui/plugin/utils/experimental.js +49 -0
  77. package/homebridge-ui/plugin/utils/experimental.js.map +1 -0
  78. package/homebridge-ui/plugin/utils/interfaces.d.ts +13 -0
  79. package/homebridge-ui/plugin/utils/interfaces.d.ts.map +1 -0
  80. package/homebridge-ui/plugin/utils/interfaces.js +12 -0
  81. package/homebridge-ui/plugin/utils/interfaces.js.map +1 -0
  82. package/homebridge-ui/plugin/utils/logger.d.ts +12 -0
  83. package/homebridge-ui/plugin/utils/logger.d.ts.map +1 -0
  84. package/homebridge-ui/plugin/utils/logger.js +36 -0
  85. package/homebridge-ui/plugin/utils/logger.js.map +1 -0
  86. package/homebridge-ui/public/3rdpartylicenses.txt +191 -6
  87. package/homebridge-ui/public/index.html +2 -2
  88. package/homebridge-ui/public/main.22a0c49e2138888f.js +1 -0
  89. package/homebridge-ui/public/polyfills.6050693665c0e882.js +1 -0
  90. package/homebridge-ui/public/styles.e02689e7df4304da.css +6 -0
  91. package/homebridge-ui/server.js +17 -1
  92. package/homebridge-ui/server.js.map +1 -1
  93. package/package.json +48 -46
  94. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  95. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.jpg +0 -0
  96. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.jpg +0 -0
  97. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.jpg +0 -0
  98. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.jpg +0 -0
  99. package/homebridge-ui/public/assets/devices/walllight_s100_large.jpg +0 -0
  100. package/homebridge-ui/public/main.0d25748cc9303f6b.js +0 -1
  101. package/homebridge-ui/public/polyfills.cdb21ff95fdea645.js +0 -1
  102. package/homebridge-ui/public/styles.021488511c20c432.css +0 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experimental.js","sourceRoot":"","sources":["../../src/plugin/utils/experimental.ts"],"names":[],"mappings":";;;AAAA,+DAS8B;AAEvB,MAAM,0BAA0B,GAAG,GAAG,EAAE;IAC7C,6BAA6B,EAAE,CAAC;AAClC,CAAC,CAAC;AAFW,QAAA,0BAA0B,8BAErC;AAEF,mCAAY,CAAC,0BAA0B,CAAC,GAAG,0BAA0B,CAAC;AAEtE,MAAM,8BAA8B,GAA4B;IAC9D,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,mCAAY,CAAC,0BAA0B,CAAC;IAC9C,KAAK,EAAE,2BAA2B;IAClC,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,KAAK;IAChB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,MAAM,6BAA6B,GAAG,GAAG,EAAE;IACzC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,iCAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,yBAAyB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,UAA+B,EAAE,EAAE;IACpE,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,IAAI,uCAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,uCAAgB,CAAC,UAAU,CAAC,CAAC,mCAAY,CAAC,gBAAgB,CAAC,EAAE;QAChG,uCAAgB,CAAC,UAAU,CAAC,GAAG;YAC7B,GAAG,uCAAgB,CAAC,UAAU,CAAC;YAC/B,CAAC,mCAAY,CAAC,gBAAgB,CAAC,EAAE,+CAAwB;SAC1D,CAAC;QACF,OAAO,GAAG,IAAI,CAAC;KAChB;IAED,IAAI,uCAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,uCAAgB,CAAC,UAAU,CAAC,CAAC,mCAAY,CAAC,mBAAmB,CAAC,EAAE;QACnG,uCAAgB,CAAC,UAAU,CAAC,GAAG;YAC7B,GAAG,uCAAgB,CAAC,UAAU,CAAC;YAC/B,CAAC,mCAAY,CAAC,mBAAmB,CAAC,EAAE,kDAA2B;SAChE,CAAC;QACF,OAAO,GAAG,IAAI,CAAC;KAChB;IAED,IAAI,OAAO,IAAI,uCAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,uCAAgB,CAAC,UAAU,CAAC,CAAC,mCAAY,CAAC,0BAA0B,CAAC,CAAC,EAAE;QACtH,uCAAgB,CAAC,UAAU,CAAC,GAAG;YAC7B,GAAG,uCAAgB,CAAC,UAAU,CAAC;YAC/B,CAAC,mCAAY,CAAC,0BAA0B,CAAC,CAAC,EAAE,8BAA8B;SAC3E,CAAC;KACH;AACH,CAAC,CAAC;AAEK,MAAM,iBAAiB,GAAG,CAAC,OAAgB,EAAE,MAAc,EAAE,KAAc,EAAE,EAAE;IACpF,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC,CAAC;AAFW,QAAA,iBAAiB,qBAE5B"}
@@ -1,15 +1,16 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
2
+ import { ChildProcessWithoutNullStreams } from 'child_process';
3
+ import net from 'net';
4
4
  import { Readable, Writable } from 'stream';
5
5
  import { Logger } from './logger';
6
6
  import EventEmitter from 'events';
7
- import { CameraConfig } from './configTypes';
8
- import { ReconfigureStreamRequest, SnapshotRequest, StartStreamRequest } from 'homebridge';
7
+ import { CameraConfig, VideoConfig } from './configTypes';
8
+ import { CameraRecordingConfiguration, ReconfigureStreamRequest, SnapshotRequest, StartStreamRequest } from 'homebridge';
9
9
  import { SessionInfo } from '../controller/streamingDelegate';
10
10
  export declare class FFmpegParameters {
11
11
  progressPort: number;
12
12
  debug: boolean;
13
+ processor?: string;
13
14
  private hideBanner;
14
15
  private useWallclockAsTimestamp;
15
16
  private inputSoure;
@@ -40,15 +41,22 @@ export declare class FFmpegParameters {
40
41
  private height?;
41
42
  private bufsize?;
42
43
  private maxrate?;
44
+ private crop;
43
45
  private sampleRate?;
44
46
  private channels?;
45
47
  private flagsGlobalHeader;
46
48
  private numberFrames?;
47
49
  private delaySnapshot;
50
+ private movflags?;
51
+ private maxMuxingQueueSize?;
52
+ private iFrameInterval?;
53
+ private processAudio;
48
54
  private constructor();
49
55
  static forAudio(debug?: boolean): Promise<FFmpegParameters>;
50
56
  static forVideo(debug?: boolean): Promise<FFmpegParameters>;
51
57
  static forSnapshot(debug?: boolean): Promise<FFmpegParameters>;
58
+ static forVideoRecording(debug?: boolean): Promise<FFmpegParameters>;
59
+ static forAudioRecording(debug?: boolean): Promise<FFmpegParameters>;
52
60
  setResolution(width: number, height: number): void;
53
61
  usesStdInAsInput(): boolean;
54
62
  setInputSource(value: string): void;
@@ -56,15 +64,21 @@ export declare class FFmpegParameters {
56
64
  setDelayedSnapshot(): void;
57
65
  setup(cameraConfig: CameraConfig, request: StartStreamRequest | ReconfigureStreamRequest | SnapshotRequest | undefined): void;
58
66
  setRTPTarget(sessionInfo: SessionInfo, request: StartStreamRequest): void;
67
+ setOutput(output: string): void;
68
+ setupForRecording(videoConfig: VideoConfig, configuration: CameraRecordingConfiguration): void;
59
69
  setTalkbackInput(sessionInfo: SessionInfo): Promise<void>;
70
+ setTalkbackChannels(channels: number): void;
60
71
  private buildGenericParameters;
61
72
  private buildInputParamters;
62
73
  private buildEncodingParameters;
63
74
  private buildOutputParameters;
64
75
  private buildParameters;
65
76
  getProcessArguments(): string[];
77
+ static getRecordingArguments(parameters: FFmpegParameters[]): string[];
66
78
  static getCombinedArguments(parameters: FFmpegParameters[]): string[];
67
79
  getStreamStartText(): string;
80
+ hasCustomFfmpeg(): boolean;
81
+ getCustomFfmpeg(): string;
68
82
  }
69
83
  export declare class FFmpeg extends EventEmitter {
70
84
  private process?;
@@ -80,6 +94,18 @@ export declare class FFmpeg extends EventEmitter {
80
94
  constructor(name: string, parameters: FFmpegParameters | FFmpegParameters[], log: Logger);
81
95
  start(): void;
82
96
  getResult(input?: Buffer): Promise<Buffer>;
97
+ startFragmentedMP4Session(): Promise<{
98
+ socket: net.Socket;
99
+ process?: ChildProcessWithoutNullStreams;
100
+ generator: AsyncGenerator<{
101
+ header: Buffer;
102
+ length: number;
103
+ type: string;
104
+ data: Buffer;
105
+ }>;
106
+ }>;
107
+ private parseFragmentedMP4;
108
+ private readLength;
83
109
  stop(): void;
84
110
  private onProgressStarted;
85
111
  private onProcessError;
@@ -1 +1 @@
1
- {"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../src/plugin/utils/ffmpeg.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../src/plugin/utils/ffmpeg.ts"],"names":[],"mappings":"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"}
@@ -56,14 +56,15 @@ class FFmpegProgress extends events_1.default {
56
56
  }
57
57
  class FFmpegParameters {
58
58
  constructor(port, isVideo, isAudio, isSnapshot, debug = false) {
59
- // default parameters
60
59
  this.hideBanner = true;
61
60
  this.useWallclockAsTimestamp = true;
62
61
  this.inputSoure = '-i pipe:';
63
62
  this.output = 'pipe:1';
64
63
  this.codec = 'copy';
64
+ this.crop = false;
65
65
  this.flagsGlobalHeader = false;
66
66
  this.delaySnapshot = false;
67
+ this.processAudio = true;
67
68
  this.progressPort = port;
68
69
  this.isVideo = isVideo;
69
70
  this.isAudio = isAudio;
@@ -101,6 +102,25 @@ class FFmpegParameters {
101
102
  ffmpeg.format = 'image2';
102
103
  return ffmpeg;
103
104
  }
105
+ static async forVideoRecording(debug = false) {
106
+ const port = await (0, pick_port_1.default)({
107
+ type: 'tcp',
108
+ ip: '0.0.0.0',
109
+ reserveTimeout: 15,
110
+ });
111
+ const ffmpeg = new FFmpegParameters(port, true, false, false, debug);
112
+ ffmpeg.useWallclockAsTimestamp = true;
113
+ return ffmpeg;
114
+ }
115
+ static async forAudioRecording(debug = false) {
116
+ const port = await (0, pick_port_1.default)({
117
+ type: 'tcp',
118
+ ip: '0.0.0.0',
119
+ reserveTimeout: 15,
120
+ });
121
+ const ffmpeg = new FFmpegParameters(port, false, true, false, debug);
122
+ return ffmpeg;
123
+ }
104
124
  setResolution(width, height) {
105
125
  this.width = width;
106
126
  this.height = height;
@@ -109,7 +129,6 @@ class FFmpegParameters {
109
129
  return this.inputSoure === '-i pipe:';
110
130
  }
111
131
  setInputSource(value) {
112
- // TODO: check for errors
113
132
  this.inputSoure = `-i ${value}`;
114
133
  }
115
134
  async setInputStream(input) {
@@ -144,6 +163,9 @@ class FFmpegParameters {
144
163
  setup(cameraConfig, request) {
145
164
  var _a;
146
165
  const videoConfig = (_a = cameraConfig.videoConfig) !== null && _a !== void 0 ? _a : (cameraConfig.videoConfig = {});
166
+ if (videoConfig.videoProcessor && videoConfig.videoProcessor !== '') {
167
+ this.processor = videoConfig.videoProcessor;
168
+ }
147
169
  if (videoConfig.readRate) {
148
170
  this.readrate = videoConfig.readRate;
149
171
  }
@@ -171,7 +193,7 @@ class FFmpegParameters {
171
193
  this.bufsize = bitrate * 2;
172
194
  this.maxrate = bitrate;
173
195
  let encoderOptions = codec === 'libx264' ? '-preset ultrafast -tune zerolatency' : '';
174
- if (videoConfig.encoderOptions && videoConfig.encoderOptions !== '') {
196
+ if (videoConfig.encoderOptions) {
175
197
  encoderOptions = videoConfig.encoderOptions;
176
198
  }
177
199
  this.codecOptions = encoderOptions;
@@ -190,16 +212,32 @@ class FFmpegParameters {
190
212
  if (videoConfig.videoFilter && videoConfig.videoFilter !== '') {
191
213
  this.filters = videoConfig.videoFilter;
192
214
  }
215
+ if (videoConfig.crop) {
216
+ this.crop = videoConfig.crop;
217
+ }
193
218
  }
194
219
  }
195
220
  if (this.isAudio) {
196
221
  const req = request;
197
- let codec = req.audio.codec === "OPUS" /* AudioStreamingCodecType.OPUS */ ? 'libopus' : 'libfdk_aac';
198
- let codecOptions = req.audio.codec === "OPUS" /* AudioStreamingCodecType.OPUS */ ? '-application lowdelay' : '-profile:a aac_eld';
222
+ let codec = 'libfdk_aac';
223
+ let codecOptions = '-profile:a aac_eld';
224
+ switch (req.audio.codec) {
225
+ case "OPUS" /* OPUS */:
226
+ codec = 'libopus';
227
+ codecOptions = '-application lowdelay';
228
+ break;
229
+ default:
230
+ codec = 'libfdk_aac';
231
+ codecOptions = '-profile:a aac_eld';
232
+ break;
233
+ }
199
234
  if (videoConfig.acodec && videoConfig.acodec !== '') {
200
235
  codec = videoConfig.acodec;
201
236
  codecOptions = '';
202
237
  }
238
+ if (videoConfig.acodecOptions !== undefined) {
239
+ codecOptions = videoConfig.acodecOptions;
240
+ }
203
241
  if (this.flagsGlobalHeader) {
204
242
  if (codecOptions !== '') {
205
243
  codecOptions += ' ';
@@ -208,15 +246,10 @@ class FFmpegParameters {
208
246
  }
209
247
  this.codec = codec;
210
248
  this.codecOptions = codecOptions;
211
- let samplerate = req.audio.sample_rate;
212
- if (videoConfig.audioSampleRate &&
213
- (videoConfig.audioSampleRate === 8 || videoConfig.audioSampleRate === 16 || videoConfig.audioSampleRate === 24)) {
214
- samplerate = videoConfig.audioSampleRate;
215
- }
216
249
  if (this.codec !== ' copy') {
217
- this.sampleRate = samplerate;
250
+ this.sampleRate = req.audio.sample_rate;
218
251
  this.channels = req.audio.channel;
219
- this.bitrate = req.audio.max_bit_rate;
252
+ this.bitrate = videoConfig.audioBitrate ? videoConfig.audioBitrate : req.audio.max_bit_rate;
220
253
  }
221
254
  }
222
255
  if (this.isSnapshot) {
@@ -234,6 +267,9 @@ class FFmpegParameters {
234
267
  if (videoConfig.videoFilter && videoConfig.videoFilter !== '') {
235
268
  this.filters = videoConfig.videoFilter;
236
269
  }
270
+ if (videoConfig.crop) {
271
+ this.crop = videoConfig.crop;
272
+ }
237
273
  }
238
274
  }
239
275
  setRTPTarget(sessionInfo, request) {
@@ -254,6 +290,92 @@ class FFmpegParameters {
254
290
  this.output = `srtp://${sessionInfo.address}:${sessionInfo.audioPort}?rtcpport=${sessionInfo.audioPort}&pkt_size=188`;
255
291
  }
256
292
  }
293
+ setOutput(output) {
294
+ this.output = output;
295
+ }
296
+ setupForRecording(videoConfig, configuration) {
297
+ var _a, _b;
298
+ this.movflags = 'frag_keyframe+empty_moov+default_base_moof';
299
+ this.maxMuxingQueueSize = 1024;
300
+ if (videoConfig.videoProcessor && videoConfig.videoProcessor !== '') {
301
+ this.processor = videoConfig.videoProcessor;
302
+ }
303
+ if (this.isVideo) {
304
+ if (videoConfig.vcodec && videoConfig.vcodec !== '') {
305
+ this.codec = videoConfig.vcodec;
306
+ }
307
+ else {
308
+ this.codec = 'libx264';
309
+ }
310
+ if (this.codec === 'libx264') {
311
+ this.pixFormat = 'yuv420p';
312
+ const profile = configuration.videoCodec.parameters.profile === 2 /* HIGH */
313
+ ? 'high'
314
+ : configuration.videoCodec.parameters.profile === 1 /* MAIN */
315
+ ? 'main'
316
+ : 'baseline';
317
+ const level = configuration.videoCodec.parameters.level === 2 /* LEVEL4_0 */
318
+ ? '4.0'
319
+ : configuration.videoCodec.parameters.level === 1 /* LEVEL3_2 */
320
+ ? '3.2'
321
+ : '3.1';
322
+ this.codecOptions = `-profile:v ${profile} -level:v ${level}`;
323
+ }
324
+ if (this.codec !== 'copy') {
325
+ this.bitrate = (_a = videoConfig.maxBitrate) !== null && _a !== void 0 ? _a : configuration.videoCodec.parameters.bitRate;
326
+ this.width = configuration.videoCodec.resolution[0];
327
+ this.height = configuration.videoCodec.resolution[1];
328
+ this.fps = (_b = videoConfig.maxFPS) !== null && _b !== void 0 ? _b : configuration.videoCodec.resolution[2];
329
+ this.crop = (videoConfig.crop !== false); // only false if 'crop: false' was specifically set
330
+ }
331
+ this.iFrameInterval = configuration.videoCodec.parameters.iFrameInterval;
332
+ }
333
+ if (this.isAudio) {
334
+ if (videoConfig.audio === false) {
335
+ this.processAudio = false;
336
+ }
337
+ if (videoConfig.acodec && videoConfig.acodec !== '') {
338
+ this.codec = videoConfig.acodec;
339
+ }
340
+ else {
341
+ this.codec = 'libfdk_aac';
342
+ }
343
+ if (this.codec === 'libfdk_aac' || this.codec === 'aac') {
344
+ this.codecOptions = (configuration.audioCodec.type === 0 /* AAC_LC */)
345
+ ? '-profile:a aac_low'
346
+ : '-profile:a aac_eld';
347
+ this.codecOptions += ' -flags +global_header';
348
+ }
349
+ if (this.codec !== 'copy') {
350
+ let samplerate;
351
+ switch (configuration.audioCodec.samplerate) {
352
+ case 0 /* KHZ_8 */:
353
+ samplerate = '8';
354
+ break;
355
+ case 1 /* KHZ_16 */:
356
+ samplerate = '16';
357
+ break;
358
+ case 2 /* KHZ_24 */:
359
+ samplerate = '24';
360
+ break;
361
+ case 3 /* KHZ_32 */:
362
+ samplerate = '32';
363
+ break;
364
+ case 4 /* KHZ_44_1 */:
365
+ samplerate = '44.1';
366
+ break;
367
+ case 5 /* KHZ_48 */:
368
+ samplerate = '48';
369
+ break;
370
+ default:
371
+ throw new Error(`Unsupported audio samplerate: ${configuration.audioCodec.samplerate}`);
372
+ }
373
+ this.sampleRate = samplerate;
374
+ this.bitrate = configuration.audioCodec.bitrate;
375
+ this.channels = configuration.audioCodec.audioChannels;
376
+ }
377
+ }
378
+ }
257
379
  async setTalkbackInput(sessionInfo) {
258
380
  this.useWallclockAsTimestamp = false;
259
381
  this.protocolWhitelist = 'pipe,udp,rtp,file,crypto,tcp';
@@ -303,6 +425,9 @@ class FFmpegParameters {
303
425
  }, 30 * 1000);
304
426
  this.setInputSource(`tcp://127.0.0.1:${port}`);
305
427
  }
428
+ setTalkbackChannels(channels) {
429
+ this.channels = channels;
430
+ }
306
431
  buildGenericParameters() {
307
432
  const params = [];
308
433
  params.push(this.hideBanner ? '-hide_banner' : '');
@@ -340,13 +465,21 @@ class FFmpegParameters {
340
465
  filters.splice(noneFilter, 1);
341
466
  }
342
467
  if (noneFilter < 0 && this.width && this.height) {
343
- const resizeFilter = 'scale=' +
344
- '\'min(' + this.width + ',iw)\'' +
345
- ':' +
346
- '\'min(' + this.height + ',ih)\'' +
347
- ':force_original_aspect_ratio=decrease';
348
- filters.push(resizeFilter);
349
- filters.push('scale=\'trunc(iw/2)*2:trunc(ih/2)*2\''); // Force to fit encoder restrictions
468
+ if (this.crop) {
469
+ const resizeFilter = `scale=${this.width}:${this.height}:force_original_aspect_ratio=increase`;
470
+ filters.push(resizeFilter);
471
+ filters.push(`crop=${this.width}:${this.height}`);
472
+ filters.push(`scale='trunc(${this.width}/2)*2:trunc(${this.height}/2)*2'`); // Force to fit encoder restrictions
473
+ }
474
+ else {
475
+ const resizeFilter = 'scale=' +
476
+ '\'min(' + this.width + ',iw)\'' +
477
+ ':' +
478
+ '\'min(' + this.height + ',ih)\'' +
479
+ ':force_original_aspect_ratio=decrease';
480
+ filters.push(resizeFilter);
481
+ filters.push('scale=\'trunc(iw/2)*2:trunc(ih/2)*2\''); // Force to fit encoder restrictions
482
+ }
350
483
  }
351
484
  if (filters.length > 0) {
352
485
  params.push('-filter:v ' + filters.join(','));
@@ -355,7 +488,7 @@ class FFmpegParameters {
355
488
  params.push(this.bufsize ? '-bufsize ' + this.bufsize + 'k' : '');
356
489
  params.push(this.maxrate ? `-maxrate ${this.maxrate}k` : '');
357
490
  }
358
- if (this.isAudio) {
491
+ if (this.isAudio && this.processAudio) {
359
492
  // audio parameters
360
493
  params.push('-acodec ' + this.codec);
361
494
  params.push(this.codecOptions ? this.codecOptions : '');
@@ -372,13 +505,21 @@ class FFmpegParameters {
372
505
  filters.splice(noneFilter, 1);
373
506
  }
374
507
  if (noneFilter < 0 && this.width && this.height) {
375
- const resizeFilter = 'scale=' +
376
- '\'min(' + this.width + ',iw)\'' +
377
- ':' +
378
- '\'min(' + this.height + ',ih)\'' +
379
- ':force_original_aspect_ratio=decrease';
380
- filters.push(resizeFilter);
381
- filters.push('scale=\'trunc(iw/2)*2:trunc(ih/2)*2\''); // Force to fit encoder restrictions
508
+ if (this.crop) {
509
+ const resizeFilter = `scale=${this.width}:${this.height}:force_original_aspect_ratio=increase`;
510
+ filters.push(resizeFilter);
511
+ filters.push(`crop=${this.width}:${this.height}`);
512
+ filters.push(`scale='trunc(${this.width}/2)*2:trunc(${this.height}/2)*2'`); // Force to fit encoder restrictions
513
+ }
514
+ else {
515
+ const resizeFilter = 'scale=' +
516
+ '\'min(' + this.width + ',iw)\'' +
517
+ ':' +
518
+ '\'min(' + this.height + ',ih)\'' +
519
+ ':force_original_aspect_ratio=decrease';
520
+ filters.push(resizeFilter);
521
+ filters.push('scale=\'trunc(iw/2)*2:trunc(ih/2)*2\''); // Force to fit encoder restrictions
522
+ }
382
523
  }
383
524
  if (filters.length > 0) {
384
525
  params.push('-filter:v ' + filters.join(','));
@@ -410,6 +551,46 @@ class FFmpegParameters {
410
551
  getProcessArguments() {
411
552
  return this.buildParameters();
412
553
  }
554
+ static getRecordingArguments(parameters) {
555
+ let params = [];
556
+ if (parameters.length === 0) {
557
+ return params;
558
+ }
559
+ params = parameters[0].buildGenericParameters();
560
+ // input
561
+ params.push(parameters[0].inputSoure);
562
+ if (parameters.length > 1 && parameters[0].inputSoure !== parameters[1].inputSoure) { // don't include extra audio source for rtsp
563
+ if (parameters[1].processAudio) {
564
+ params.push(parameters[1].inputSoure);
565
+ }
566
+ else {
567
+ params.push('-f lavfi -i anullsrc -shortest');
568
+ }
569
+ }
570
+ if (parameters.length === 1) {
571
+ params.push('-an');
572
+ }
573
+ params.push('-sn -dn');
574
+ // video encoding
575
+ params = params.concat(parameters[0].buildEncodingParameters());
576
+ params.push(parameters[0].iFrameInterval ? `-force_key_frames expr:gte(t,n_forced*${parameters[0].iFrameInterval / 1000})` : '');
577
+ // audio encoding
578
+ if (parameters.length > 1) {
579
+ if (parameters[1].processAudio) {
580
+ params.push('-bsf:a aac_adtstoasc');
581
+ }
582
+ params = params.concat(parameters[1].buildEncodingParameters());
583
+ }
584
+ // fragmented mp4 options
585
+ params.push(parameters[0].movflags ? `-movflags ${parameters[0].movflags}` : '');
586
+ params.push(parameters[0].maxMuxingQueueSize ? `-max_muxing_queue_size ${parameters[0].maxMuxingQueueSize}` : '');
587
+ // output
588
+ params.push('-f mp4');
589
+ params.push(parameters[0].output);
590
+ params.push(`-progress tcp://127.0.0.1:${parameters[0].progressPort}`);
591
+ params = params.filter(x => x !== '');
592
+ return params;
593
+ }
413
594
  static getCombinedArguments(parameters) {
414
595
  let params = [];
415
596
  if (parameters.length === 0) {
@@ -437,6 +618,12 @@ class FFmpegParameters {
437
618
  }
438
619
  return 'Starting unknown stream';
439
620
  }
621
+ hasCustomFfmpeg() {
622
+ return (this.processor !== undefined);
623
+ }
624
+ getCustomFfmpeg() {
625
+ return (this.hasCustomFfmpeg()) ? this.processor : '';
626
+ }
440
627
  }
441
628
  exports.FFmpegParameters = FFmpegParameters;
442
629
  class FFmpeg extends events_1.default {
@@ -454,6 +641,9 @@ class FFmpeg extends events_1.default {
454
641
  else {
455
642
  this.parameters = [parameters];
456
643
  }
644
+ if (this.parameters[0].hasCustomFfmpeg()) {
645
+ this.ffmpegExec = this.parameters[0].getCustomFfmpeg();
646
+ }
457
647
  }
458
648
  start() {
459
649
  this.starttime = Date.now();
@@ -468,9 +658,15 @@ class FFmpeg extends events_1.default {
468
658
  this.stdin = this.process.stdin;
469
659
  this.stdout = this.process.stdout;
470
660
  this.process.stderr.on('data', (chunk) => {
471
- if (this.parameters[0].debug) {
661
+ const isError = chunk.toString().indexOf('[panic]') !== -1 ||
662
+ chunk.toString().indexOf('[error]') !== -1 ||
663
+ chunk.toString().indexOf('[fatal]') !== -1;
664
+ if (this.parameters[0].debug && !isError) {
472
665
  this.log.debug(this.name, 'ffmpeg log message:\n' + chunk.toString());
473
666
  }
667
+ else if (isError) {
668
+ this.log.error(this.name, 'ffmpeg log message:\n' + chunk.toString());
669
+ }
474
670
  });
475
671
  this.process.on('error', this.onProcessError.bind(this));
476
672
  this.process.on('exit', this.onProcessExit.bind(this));
@@ -484,9 +680,15 @@ class FFmpeg extends events_1.default {
484
680
  return new Promise((resolve, reject) => {
485
681
  this.process = (0, child_process_1.spawn)(this.ffmpegExec, processArgs.join(' ').split(/\s+/), { env: process.env });
486
682
  this.process.stderr.on('data', (chunk) => {
487
- if (this.parameters[0].debug) {
683
+ const isError = chunk.toString().indexOf('[panic]') !== -1 ||
684
+ chunk.toString().indexOf('[error]') !== -1 ||
685
+ chunk.toString().indexOf('[fatal]') !== -1;
686
+ if (this.parameters[0].debug && !isError) {
488
687
  this.log.debug(this.name, 'ffmpeg log message:\n' + chunk.toString());
489
688
  }
689
+ else if (isError) {
690
+ this.log.error(this.name, 'ffmpeg log message:\n' + chunk.toString());
691
+ }
490
692
  });
491
693
  const killTimeout = setTimeout(() => {
492
694
  this.stop();
@@ -516,6 +718,95 @@ class FFmpeg extends events_1.default {
516
718
  }
517
719
  });
518
720
  }
721
+ async startFragmentedMP4Session() {
722
+ this.starttime = Date.now();
723
+ this.progress = new FFmpegProgress(this.parameters[0].progressPort);
724
+ this.progress.on('progress started', this.onProgressStarted.bind(this));
725
+ const port = await (0, pick_port_1.default)({
726
+ type: 'tcp',
727
+ ip: '0.0.0.0',
728
+ reserveTimeout: 15,
729
+ });
730
+ return new Promise((resolve, reject) => {
731
+ const server = net_1.default.createServer((socket) => {
732
+ server.close();
733
+ resolve({
734
+ socket: socket,
735
+ process: this.process,
736
+ generator: this.parseFragmentedMP4(socket),
737
+ });
738
+ });
739
+ server.listen(port, () => {
740
+ this.parameters[0].setOutput(`tcp://127.0.0.1:${port}`);
741
+ const processArgs = FFmpegParameters.getRecordingArguments(this.parameters);
742
+ this.log.debug(this.name, 'Stream command: ' + this.ffmpegExec + ' ' + processArgs.join(' '));
743
+ this.parameters.forEach((p) => {
744
+ this.log.info(this.name, p.getStreamStartText());
745
+ });
746
+ this.process = (0, child_process_1.spawn)(this.ffmpegExec, processArgs.join(' ').split(/\s+/), { env: process.env });
747
+ this.stdin = this.process.stdin;
748
+ this.stdout = this.process.stdout;
749
+ this.process.stderr.on('data', (chunk) => {
750
+ const isError = chunk.toString().indexOf('[panic]') !== -1 ||
751
+ chunk.toString().indexOf('[error]') !== -1 ||
752
+ chunk.toString().indexOf('[fatal]') !== -1;
753
+ if (this.parameters[0].debug && !isError) {
754
+ this.log.debug(this.name, 'ffmpeg log message:\n' + chunk.toString());
755
+ }
756
+ else if (isError) {
757
+ this.log.error(this.name, 'ffmpeg log message:\n' + chunk.toString());
758
+ }
759
+ });
760
+ this.process.on('error', this.onProcessError.bind(this));
761
+ this.process.on('exit', this.onProcessExit.bind(this));
762
+ });
763
+ });
764
+ }
765
+ async *parseFragmentedMP4(socket) {
766
+ while (true) {
767
+ const header = await this.readLength(socket, 8);
768
+ const length = header.readInt32BE(0) - 8;
769
+ const type = header.slice(4).toString();
770
+ const data = await this.readLength(socket, length);
771
+ yield {
772
+ header,
773
+ length,
774
+ type,
775
+ data,
776
+ };
777
+ }
778
+ }
779
+ async readLength(socket, length) {
780
+ if (length <= 0) {
781
+ return Buffer.alloc(0);
782
+ }
783
+ const value = socket.read(length);
784
+ if (value) {
785
+ return value;
786
+ }
787
+ return new Promise((resolve, reject) => {
788
+ const readHandler = () => {
789
+ const value = socket.read(length);
790
+ if (value) {
791
+ cleanup();
792
+ resolve(value);
793
+ }
794
+ };
795
+ const endHandler = () => {
796
+ cleanup();
797
+ reject(new Error(`FFMPEG socket closed during read for ${length} bytes!`));
798
+ };
799
+ const cleanup = () => {
800
+ socket.removeListener('readable', readHandler);
801
+ socket.removeListener('close', endHandler);
802
+ };
803
+ if (!socket) {
804
+ throw new Error('FFMPEG socket is closed now!');
805
+ }
806
+ socket.on('readable', readHandler);
807
+ socket.on('close', endHandler);
808
+ });
809
+ }
519
810
  stop() {
520
811
  var _a, _b, _c;
521
812
  let usesStdIn = false;
@@ -563,7 +854,8 @@ class FFmpeg extends events_1.default {
563
854
  }
564
855
  }
565
856
  else {
566
- this.log.error(this.name, message + ' (Error)');
857
+ this.emit('error', message + ' (Error)');
858
+ // this.log.error(this.name, message + ' (Error)');
567
859
  }
568
860
  }
569
861
  }