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.
- package/README.md +4 -1
- package/build/eslint-rules.mjs +1 -0
- package/dist/featureoptions.d.ts +83 -5
- package/dist/featureoptions.js +65 -6
- package/dist/featureoptions.js.map +1 -1
- package/dist/ffmpeg/codecs.d.ts +172 -0
- package/dist/ffmpeg/codecs.js +374 -0
- package/dist/ffmpeg/codecs.js.map +1 -0
- package/dist/ffmpeg/exec.d.ts +108 -0
- package/dist/ffmpeg/exec.js +122 -0
- package/dist/ffmpeg/exec.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +8 -0
- package/dist/ffmpeg/index.js +13 -0
- package/dist/ffmpeg/index.js.map +1 -0
- package/dist/ffmpeg/options.d.ts +345 -0
- package/dist/ffmpeg/options.js +750 -0
- package/dist/ffmpeg/options.js.map +1 -0
- package/dist/ffmpeg/process.d.ts +155 -0
- package/dist/ffmpeg/process.js +344 -0
- package/dist/ffmpeg/process.js.map +1 -0
- package/dist/ffmpeg/record.d.ts +230 -0
- package/dist/ffmpeg/record.js +504 -0
- package/dist/ffmpeg/record.js.map +1 -0
- package/dist/ffmpeg/rtp.d.ts +205 -0
- package/dist/ffmpeg/rtp.js +335 -0
- package/dist/ffmpeg/rtp.js.map +1 -0
- package/dist/ffmpeg/settings.d.ts +6 -0
- package/dist/ffmpeg/settings.js +17 -0
- package/dist/ffmpeg/settings.js.map +1 -0
- package/dist/ffmpeg/stream.d.ts +143 -0
- package/dist/ffmpeg/stream.js +186 -0
- package/dist/ffmpeg/stream.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mqttclient.d.ts +161 -1
- package/dist/mqttclient.js +161 -9
- package/dist/mqttclient.js.map +1 -1
- package/dist/service.d.ts +9 -2
- package/dist/service.js +6 -0
- package/dist/service.js.map +1 -1
- package/dist/ui/featureoptions.js +65 -6
- package/dist/ui/featureoptions.js.map +1 -1
- package/dist/ui/webUi-featureoptions.mjs +5 -4
- package/dist/util.d.ts +203 -12
- package/dist/util.js +95 -12
- package/dist/util.js.map +1 -1
- package/package.json +13 -9
- package/dist/rtp.d.ts +0 -32
- package/dist/rtp.js +0 -178
- package/dist/rtp.js.map +0 -1
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
/* Copyright(C) 2023-2025, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* ffmpeg/options.ts: FFmpeg decoder and encoder options with hardware-accelerated codec support where available.
|
|
4
|
+
*/
|
|
5
|
+
import { HOMEKIT_STREAMING_HEADROOM, RPI_GPU_MINIMUM } from "./settings.js";
|
|
6
|
+
/**
|
|
7
|
+
* Provides Homebridge FFmpeg transcoding, decoding, and encoding options, selecting codecs, pixel formats, and hardware acceleration for the host system.
|
|
8
|
+
*
|
|
9
|
+
* This class generates and adapts FFmpeg command-line arguments for livestreaming and event recording, optimizing for system hardware and codec availability.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const ffmpegOpts = new FfmpegOptions(optionsConfig);
|
|
15
|
+
*
|
|
16
|
+
* // Generate video encoder arguments for streaming.
|
|
17
|
+
* const encoderOptions: EncoderOptions = {
|
|
18
|
+
*
|
|
19
|
+
* bitrate: 3000,
|
|
20
|
+
* fps: 30,
|
|
21
|
+
* height: 1080,
|
|
22
|
+
* idrInterval: 2,
|
|
23
|
+
* inputFps: 30,
|
|
24
|
+
* level: H264Level.LEVEL4_0,
|
|
25
|
+
* profile: H264Profile.HIGH,
|
|
26
|
+
* useSmartQuality: true,
|
|
27
|
+
* width: 1920
|
|
28
|
+
* };
|
|
29
|
+
* const args = ffmpegOpts.streamEncoder(encoderOptions);
|
|
30
|
+
*
|
|
31
|
+
* // Generate crop filter string, if cropping is enabled.
|
|
32
|
+
* const crop = ffmpegOpts.cropFilter;
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @see EncoderOptions
|
|
36
|
+
* @see FfmpegCodecs
|
|
37
|
+
* @see {@link https://ffmpeg.org/ffmpeg.html | FFmpeg Documentation}
|
|
38
|
+
*
|
|
39
|
+
* @category FFmpeg
|
|
40
|
+
*/
|
|
41
|
+
export class FfmpegOptions {
|
|
42
|
+
/**
|
|
43
|
+
* FFmpeg codec and hardware capabilities for the current host.
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
codecSupport;
|
|
47
|
+
/**
|
|
48
|
+
* Indicates if debug logging is enabled.
|
|
49
|
+
*/
|
|
50
|
+
debug;
|
|
51
|
+
/**
|
|
52
|
+
* Logging interface for output and errors.
|
|
53
|
+
*/
|
|
54
|
+
log;
|
|
55
|
+
/**
|
|
56
|
+
* Function returning the name for this options instance to be used for logging.
|
|
57
|
+
*/
|
|
58
|
+
name;
|
|
59
|
+
/**
|
|
60
|
+
* The original options used to initialize this instance.
|
|
61
|
+
*/
|
|
62
|
+
options;
|
|
63
|
+
hwPixelFormat;
|
|
64
|
+
/**
|
|
65
|
+
* Creates an instance of Homebridge FFmpeg encoding and decoding options.
|
|
66
|
+
*
|
|
67
|
+
* @param options - FFmpeg options configuration.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* const ffmpegOpts = new FfmpegOptions(optionsConfig);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
constructor(options) {
|
|
76
|
+
this.codecSupport = options.codecSupport;
|
|
77
|
+
this.debug = options.debug ?? false;
|
|
78
|
+
this.hwPixelFormat = [];
|
|
79
|
+
this.log = options.log;
|
|
80
|
+
this.name = options.name;
|
|
81
|
+
this.options = options;
|
|
82
|
+
// Configure our hardware acceleration support.
|
|
83
|
+
this.configureHwAccel();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Determines and configures hardware-accelerated video decoding and transcoding for the host system.
|
|
87
|
+
*
|
|
88
|
+
* This internal method checks for the availability of hardware codecs and accelerators based on the host platform and updates
|
|
89
|
+
* FFmpeg options to use the best available hardware or falls back to software processing when necessary.
|
|
90
|
+
* It logs warnings or errors if required codecs or hardware acceleration are unavailable.
|
|
91
|
+
*
|
|
92
|
+
* This method is called automatically by the `FfmpegOptions` constructor and is not intended to be called directly.
|
|
93
|
+
*
|
|
94
|
+
* @returns `true` if hardware-accelerated transcoding is enabled after configuration, otherwise `false`.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* // This method is invoked by the FfmpegOptions constructor:
|
|
100
|
+
* const ffmpegOpts = new FfmpegOptions(optionsConfig);
|
|
101
|
+
*
|
|
102
|
+
* // Hardware acceleration configuration occurs automatically.
|
|
103
|
+
* // Developers typically do not need to call configureHwAccel() directly.
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @see FfmpegCodecs
|
|
107
|
+
* @see FfmpegOptions
|
|
108
|
+
*/
|
|
109
|
+
configureHwAccel() {
|
|
110
|
+
let logMessage = "";
|
|
111
|
+
// Utility to return which hardware acceleration features are currently available to us.
|
|
112
|
+
const accelCategories = () => {
|
|
113
|
+
const categories = [];
|
|
114
|
+
if (this.options.hardwareDecoding) {
|
|
115
|
+
categories.push("decoding");
|
|
116
|
+
}
|
|
117
|
+
if (this.options.hardwareTranscoding) {
|
|
118
|
+
categories.push("transcoding");
|
|
119
|
+
}
|
|
120
|
+
return categories.join(" and ");
|
|
121
|
+
};
|
|
122
|
+
// Hardware-accelerated decoding is enabled by default, where supported. Let's select the decoder options accordingly where supported.
|
|
123
|
+
if (this.options.hardwareDecoding) {
|
|
124
|
+
// Utility function to check that we have a specific decoder codec available to us.
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
126
|
+
const validateDecoder = (codec, pixelFormat) => {
|
|
127
|
+
if (!this.options.codecSupport.hasDecoder("h264", codec)) {
|
|
128
|
+
this.log.error("Unable to enable hardware-accelerated decoding. Your video processor does not have support for the " + codec + " decoder. " +
|
|
129
|
+
"Using software decoding instead.");
|
|
130
|
+
this.options.hardwareDecoding = false;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
this.hwPixelFormat.push(...pixelFormat);
|
|
134
|
+
return true;
|
|
135
|
+
};
|
|
136
|
+
// Utility function to check that we have a specific decoder codec available to us.
|
|
137
|
+
const validateHwAccel = (accel, pixelFormat) => {
|
|
138
|
+
if (!this.options.codecSupport.hasHwAccel(accel)) {
|
|
139
|
+
this.log.error("Unable to enable hardware-accelerated decoding. Your video processor does not have support for the " + accel + " hardware accelerator. " +
|
|
140
|
+
"Using software decoding instead.");
|
|
141
|
+
this.options.hardwareDecoding = false;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
this.hwPixelFormat.push(...pixelFormat);
|
|
145
|
+
return true;
|
|
146
|
+
};
|
|
147
|
+
switch (this.codecSupport.hostSystem) {
|
|
148
|
+
case "macOS.Apple":
|
|
149
|
+
case "macOS.Intel":
|
|
150
|
+
// Verify that we have hardware-accelerated decoding available to us.
|
|
151
|
+
validateHwAccel("videotoolbox", ["videotoolbox_vld", "nv12", "yuv420p"]);
|
|
152
|
+
break;
|
|
153
|
+
case "raspbian":
|
|
154
|
+
// If it's less than the minimum hardware GPU memory we need on an Raspberry Pi, we revert back to our default decoder.
|
|
155
|
+
if (this.options.codecSupport.gpuMem < RPI_GPU_MINIMUM) {
|
|
156
|
+
this.log.info("Disabling hardware-accelerated %s. Adjust the GPU memory configuration on your Raspberry Pi to at least %s MB to enable it.", accelCategories(), RPI_GPU_MINIMUM);
|
|
157
|
+
this.options.hardwareDecoding = false;
|
|
158
|
+
this.options.hardwareTranscoding = false;
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
// Verify that we have the hardware decoder available to us. Unfortunately, at the moment, it seems that hardware decoding is flaky, at best, on Raspberry Pi.
|
|
162
|
+
// validateDecoder("h264_mmal", [ "mmal", "yuv420p" ]);
|
|
163
|
+
// validateDecoder("h264_v4l2m2ml", [ "yuv420p" ]);
|
|
164
|
+
this.options.hardwareDecoding = false;
|
|
165
|
+
break;
|
|
166
|
+
default:
|
|
167
|
+
// Back to software decoding unless we're on a known system that always supports hardware decoding.
|
|
168
|
+
this.options.hardwareDecoding = false;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// If we've enabled hardware-accelerated transcoding, let's select the encoder options accordingly where supported.
|
|
173
|
+
if (this.options.hardwareTranscoding) {
|
|
174
|
+
// Utility function to check that we have a specific encoder codec available to us.
|
|
175
|
+
const validateEncoder = (codec) => {
|
|
176
|
+
if (!this.options.codecSupport.hasEncoder("h264", codec)) {
|
|
177
|
+
this.log.error("Unable to enable hardware-accelerated transcoding. Your video processor does not have support for the " + codec + " encoder. " +
|
|
178
|
+
"Using software transcoding instead.");
|
|
179
|
+
this.options.hardwareTranscoding = false;
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
};
|
|
184
|
+
switch (this.codecSupport.hostSystem) {
|
|
185
|
+
case "macOS.Apple":
|
|
186
|
+
case "macOS.Intel":
|
|
187
|
+
// Verify that we have the hardware encoder available to us.
|
|
188
|
+
validateEncoder("h264_videotoolbox");
|
|
189
|
+
// Validate that we have access to the AudioToolbox AAC encoder.
|
|
190
|
+
if (!this.options.codecSupport.hasEncoder("aac", "aac_at")) {
|
|
191
|
+
this.log.error("Your video processor does not have support for the native macOS AAC encoder, aac_at. Will attempt to use libfdk_aac instead.");
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case "raspbian":
|
|
195
|
+
// Verify that we have the hardware encoder available to us.
|
|
196
|
+
validateEncoder("h264_v4l2m2m");
|
|
197
|
+
logMessage = "Raspberry Pi hardware acceleration will be used for livestreaming. " +
|
|
198
|
+
"HomeKit Secure Video recordings are not supported by the hardware encoder and will use software transcoding instead";
|
|
199
|
+
// Ensure we have the pixel format the Raspberry Pi GPU is expecting available to us, if it isn't already.
|
|
200
|
+
if (!this.hwPixelFormat.includes("yuv420p")) {
|
|
201
|
+
this.hwPixelFormat.push("yuv420p");
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
// Let's see if we have Intel QuickSync hardware decoding available to us.
|
|
206
|
+
if (this.options.codecSupport.hasHwAccel("qsv") &&
|
|
207
|
+
this.options.codecSupport.hasDecoder("h264", "h264_qsv") && this.options.codecSupport.hasEncoder("h264", "h264_qsv") &&
|
|
208
|
+
this.options.codecSupport.hasDecoder("hevc", "hevc_qsv") && this.options.codecSupport.hasEncoder("hevc", "hevc_qsv")) {
|
|
209
|
+
this.options.hardwareDecoding = true;
|
|
210
|
+
this.hwPixelFormat.push("qsv", "yuv420p");
|
|
211
|
+
logMessage = "Intel Quick Sync Video";
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// Back to software encoding.
|
|
215
|
+
this.options.hardwareDecoding = false;
|
|
216
|
+
this.options.hardwareTranscoding = false;
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Inform the user.
|
|
222
|
+
if (this.options.hardwareDecoding || this.options.hardwareTranscoding) {
|
|
223
|
+
this.log.info("Hardware-accelerated " + accelCategories() + " enabled" + (logMessage.length ? ": " + logMessage : "") + ".");
|
|
224
|
+
}
|
|
225
|
+
return this.options.hardwareTranscoding;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Returns the audio encoder arguments to use when transcoding.
|
|
229
|
+
*
|
|
230
|
+
* @returns Array of FFmpeg command-line arguments for audio encoding.
|
|
231
|
+
*/
|
|
232
|
+
get audioEncoder() {
|
|
233
|
+
// If we don't have libfdk_aac available to us, we're essentially dead in the water.
|
|
234
|
+
let encoderOptions = [];
|
|
235
|
+
// Utility function to return a default audio encoder codec.
|
|
236
|
+
const defaultAudioEncoderOptions = () => {
|
|
237
|
+
const audioOptions = [];
|
|
238
|
+
if (this.options.codecSupport.hasEncoder("aac", "libfdk_aac")) {
|
|
239
|
+
// Default to libfdk_aac since FFmpeg doesn't natively support AAC-ELD. We use the following options by default:
|
|
240
|
+
//
|
|
241
|
+
// -acodec libfdk_aac Use the libfdk_aac encoder.
|
|
242
|
+
// -afterburner 1 Increases audio quality at the expense of needing a little bit more computational power in libfdk_aac.
|
|
243
|
+
// -eld_v2 1 Use the enhanced low delay v2 standard for better audio characteristics.
|
|
244
|
+
audioOptions.push("-acodec", "libfdk_aac", "-afterburner", "1", "-eld_v2", "1");
|
|
245
|
+
// If we're using Jellyfin's FFmpeg, it's libfdk_aac is broken and crashes when using spectral band replication.
|
|
246
|
+
if (!/-Jellyfin$/i.test(this.options.codecSupport.ffmpegVersion)) {
|
|
247
|
+
// -eld_sbr 1 Use spectral band replication to further enhance audio.
|
|
248
|
+
audioOptions.push("-eld_sbr", "1");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return audioOptions;
|
|
252
|
+
};
|
|
253
|
+
switch (this.codecSupport.hostSystem) {
|
|
254
|
+
case "macOS.Apple":
|
|
255
|
+
case "macOS.Intel":
|
|
256
|
+
// If we don't have audiotoolbox available, let's default back to libfdk_aac.
|
|
257
|
+
if (!this.options.codecSupport.hasEncoder("aac", "aac_at")) {
|
|
258
|
+
encoderOptions = defaultAudioEncoderOptions();
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
// aac_at is the macOS audio encoder API. We use the following options:
|
|
262
|
+
//
|
|
263
|
+
// -acodec aac_at Use the aac_at encoder on macOS.
|
|
264
|
+
// -aac_at_mode cvbr Use the constrained variable bitrate setting to allow the encoder to optimize audio within the requested bitrates.
|
|
265
|
+
encoderOptions = [
|
|
266
|
+
"-acodec", "aac_at",
|
|
267
|
+
"-aac_at_mode", "cvbr"
|
|
268
|
+
];
|
|
269
|
+
break;
|
|
270
|
+
default:
|
|
271
|
+
encoderOptions = defaultAudioEncoderOptions();
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
return encoderOptions;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns the audio decoder to use when decoding.
|
|
278
|
+
*
|
|
279
|
+
* @returns The FFmpeg audio decoder string.
|
|
280
|
+
*/
|
|
281
|
+
get audioDecoder() {
|
|
282
|
+
return "libfdk_aac";
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Returns the video decoder arguments to use for decoding video.
|
|
286
|
+
*
|
|
287
|
+
* @param codec - Optional. Codec to decode ("h264" or "hevc").
|
|
288
|
+
* @returns Array of FFmpeg command-line arguments for video decoding.
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
*
|
|
292
|
+
* ```ts
|
|
293
|
+
* const args = ffmpegOpts.videoDecoder("h264");
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
videoDecoder(codec = "h264") {
|
|
297
|
+
codec = codec.toLowerCase();
|
|
298
|
+
switch (codec) {
|
|
299
|
+
case "h265":
|
|
300
|
+
case "hevc":
|
|
301
|
+
codec = "hevc";
|
|
302
|
+
break;
|
|
303
|
+
default:
|
|
304
|
+
codec = "h264";
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
// Default to no special decoder options for inbound streams.
|
|
308
|
+
let decoderOptions = [];
|
|
309
|
+
// If we've enabled hardware-accelerated transcoding, let's select decoder options accordingly where supported.
|
|
310
|
+
if (this.options.hardwareDecoding) {
|
|
311
|
+
switch (this.codecSupport.hostSystem) {
|
|
312
|
+
case "macOS.Apple":
|
|
313
|
+
case "macOS.Intel":
|
|
314
|
+
// h264_videotoolbox is the macOS hardware decoder and encoder API. We use the following options for decoding video:
|
|
315
|
+
//
|
|
316
|
+
// -hwaccel videotoolbox Select Video Toolbox for hardware-accelerated H.264 decoding.
|
|
317
|
+
decoderOptions = [
|
|
318
|
+
"-hwaccel", "videotoolbox"
|
|
319
|
+
];
|
|
320
|
+
break;
|
|
321
|
+
case "raspbian":
|
|
322
|
+
// h264_mmal is the preferred Raspberry Pi hardware decoder codec. We use the following options for decoding video:
|
|
323
|
+
//
|
|
324
|
+
// -c:v h264_mmal Select the Multimedia Abstraction Layer codec for hardware-accelerated H.264 processing.
|
|
325
|
+
decoderOptions = [
|
|
326
|
+
// "-c:v", "h264_mmal"
|
|
327
|
+
];
|
|
328
|
+
break;
|
|
329
|
+
default:
|
|
330
|
+
// h264_qsv is the Intel Quick Sync Video hardware encoder and decoder.
|
|
331
|
+
//
|
|
332
|
+
// -hwaccel qsv Select Quick Sync Video to enable hardware-accelerated H.264 decoding.
|
|
333
|
+
// -c:v h264_qsv or hevc_qsv Select the Quick Sync Video codec for hardware-accelerated H.264 or HEVC processing.
|
|
334
|
+
decoderOptions = [
|
|
335
|
+
"-hwaccel", "qsv",
|
|
336
|
+
"-hwaccel_output_format", "qsv",
|
|
337
|
+
"-c:v", (codec === "hevc") ? "hevc_qsv" : "h264_qsv"
|
|
338
|
+
];
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return decoderOptions;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Returns the FFmpeg crop filter string, or a default no-op filter if cropping is disabled.
|
|
346
|
+
*
|
|
347
|
+
* @returns The crop filter string for FFmpeg.
|
|
348
|
+
*/
|
|
349
|
+
get cropFilter() {
|
|
350
|
+
// If we haven't enabled cropping, tell the crop filter to do nothing.
|
|
351
|
+
if (!this.options.crop) {
|
|
352
|
+
return "crop=w=iw*100:h=ih*100:x=iw*0:y=ih*0";
|
|
353
|
+
}
|
|
354
|
+
// Generate our crop filter based on what the user has configured.
|
|
355
|
+
return "crop=" + [
|
|
356
|
+
"w=iw*" + this.options.crop.width.toString(),
|
|
357
|
+
"h=ih*" + this.options.crop.height.toString(),
|
|
358
|
+
"x=iw*" + this.options.crop.x.toString(),
|
|
359
|
+
"y=ih*" + this.options.crop.y.toString()
|
|
360
|
+
].join(":");
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Generates the default set of FFmpeg video encoder arguments for software transcoding using libx264.
|
|
364
|
+
*
|
|
365
|
+
* This method builds command-line options for the FFmpeg libx264 encoder based on the provided encoder options, including bitrate, H.264 profile and level, pixel
|
|
366
|
+
* format, frame rate, buffer size, and optional smart quality settings. It is used internally when hardware-accelerated transcoding is not enabled or supported.
|
|
367
|
+
*
|
|
368
|
+
* @param options - The encoder options to use for generating FFmpeg arguments.
|
|
369
|
+
*
|
|
370
|
+
* @returns An array of FFmpeg command-line arguments for software video encoding.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
*
|
|
374
|
+
* ```ts
|
|
375
|
+
* const encoderOptions: EncoderOptions = {
|
|
376
|
+
*
|
|
377
|
+
* bitrate: 2000,
|
|
378
|
+
* fps: 30,
|
|
379
|
+
* height: 720,
|
|
380
|
+
* idrInterval: 2,
|
|
381
|
+
* inputFps: 30,
|
|
382
|
+
* level: H264Level.LEVEL3_1,
|
|
383
|
+
* profile: H264Profile.MAIN,
|
|
384
|
+
* useSmartQuality: true,
|
|
385
|
+
* width: 1280
|
|
386
|
+
* };
|
|
387
|
+
*
|
|
388
|
+
* const args = ffmpegOpts['defaultVideoEncoderOptions'](encoderOptions);
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* @see EncoderOptions
|
|
392
|
+
*/
|
|
393
|
+
defaultVideoEncoderOptions(options) {
|
|
394
|
+
const videoFilters = [];
|
|
395
|
+
// Default smart quality to true unless specified.
|
|
396
|
+
options = Object.assign({}, { useSmartQuality: true }, options);
|
|
397
|
+
// Set our FFmpeg video filter options:
|
|
398
|
+
//
|
|
399
|
+
// format= Set the pixel formats we want to target for output.
|
|
400
|
+
videoFilters.push("format=" + [...new Set([...this.hwPixelFormat, "yuvj420p"])].join("|"));
|
|
401
|
+
// scale=-2:min(ih\,height) Scale the video to the size that's being requested while respecting aspect ratios and ensuring our final dimensions are
|
|
402
|
+
// a power of two.
|
|
403
|
+
videoFilters.push("scale=-2:min(ih\\," + options.height.toString() + ")");
|
|
404
|
+
// fps=fps= Use the fps filter to provide the frame rate requested by HomeKit. This has better performance characteristics rather than using
|
|
405
|
+
// "-r". We only need to apply this filter if our input and output frame rates aren't already identical.
|
|
406
|
+
if (options.fps !== options.inputFps) {
|
|
407
|
+
videoFilters.push("fps=fps=" + options.fps.toString());
|
|
408
|
+
}
|
|
409
|
+
// Default to the tried-and-true libx264. We use the following options by default:
|
|
410
|
+
//
|
|
411
|
+
// -c:v libx264 Use the excellent libx264 H.264 encoder.
|
|
412
|
+
// -preset veryfast Use the veryfast encoding preset in libx264, which provides a good balance of encoding speed and quality.
|
|
413
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
414
|
+
// -level:v Use the H.264 profile level that HomeKit is requesting when encoding.
|
|
415
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
416
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
417
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final
|
|
418
|
+
// dimensions are a power of two.
|
|
419
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
420
|
+
// livestreamng exerience.
|
|
421
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
422
|
+
// -maxrate bitrate The maximum bitrate tolerance, used with -bufsize. This provides an upper bound on bitrate, with a little bit extra to
|
|
423
|
+
// allow encoders some variation in order to maximize quality while honoring bandwidth constraints.
|
|
424
|
+
const encoderOptions = [
|
|
425
|
+
"-c:v", "libx264",
|
|
426
|
+
"-preset", "veryfast",
|
|
427
|
+
"-profile:v", this.getH264Profile(options.profile),
|
|
428
|
+
"-level:v", this.getH264Level(options.level),
|
|
429
|
+
"-noautoscale",
|
|
430
|
+
"-bf", "0",
|
|
431
|
+
"-filter:v", videoFilters.join(", "),
|
|
432
|
+
"-g:v", (options.fps * options.idrInterval).toString(),
|
|
433
|
+
"-bufsize", (2 * options.bitrate).toString() + "k",
|
|
434
|
+
"-maxrate", (options.bitrate + (options.useSmartQuality ? HOMEKIT_STREAMING_HEADROOM : 0)).toString() + "k"
|
|
435
|
+
];
|
|
436
|
+
// Using libx264's constant rate factor mode produces generally better results across the board. We use a capped CRF approach, allowing libx264 to
|
|
437
|
+
// make intelligent choices about how to adjust bitrate to achieve a certain quality level depending on the complexity of the scene being encoded, but
|
|
438
|
+
// constraining it to a maximum bitrate to stay within the bandwidth constraints HomeKit is requesting.
|
|
439
|
+
if (options.useSmartQuality) {
|
|
440
|
+
// -crf 20 Use a constant rate factor of 20, to allow libx264 the ability to vary bitrates to achieve the visual quality we
|
|
441
|
+
// want, constrained by our maximum bitrate.
|
|
442
|
+
encoderOptions.push("-crf", "20");
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
// For recording HKSV, we really want to maintain a tight rein on bitrate and don't want to freelance with perceived quality for two reasons - HKSV
|
|
446
|
+
// is very latency sensitive and it's also very particular about bitrates and the specific format of the stream it receives. The second reason is that
|
|
447
|
+
// HKSV typically requests bitrates of around 2000kbps, which results in a reasonably high quality recording, as opposed to the typical 2-300kbps
|
|
448
|
+
// that livestreaming from the Home app itself generates. Those lower bitrates in livestreaming really benefit from the magic that using a good CRF value
|
|
449
|
+
// can produce in libx264.
|
|
450
|
+
encoderOptions.push("-b:v", options.bitrate.toString() + "k");
|
|
451
|
+
}
|
|
452
|
+
return encoderOptions;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Returns the video encoder options to use for HomeKit Secure Video (HKSV) event recording.
|
|
456
|
+
*
|
|
457
|
+
* @param options - Encoder options to use.
|
|
458
|
+
* @returns Array of FFmpeg command-line arguments for video encoding.
|
|
459
|
+
*/
|
|
460
|
+
recordEncoder(options) {
|
|
461
|
+
// We always disable smart quality when recording due to HomeKit's strict requirements here.
|
|
462
|
+
options.useSmartQuality = false;
|
|
463
|
+
// Generaly, we default to using the same encoding options we use to transcode livestreams, unless we have platform-specific quirks we need to address,
|
|
464
|
+
// such as where we can have hardware-accelerated transcoded livestreaming, but not hardware-accelerated HKSV event recording. The other noteworthy
|
|
465
|
+
// aspect here is that HKSV is quite specific in what it wants, and isn't vary tolerant of creative license in how you may choose to alter bitrate to
|
|
466
|
+
// address quality. When we call our encoders, we also let them know we don't want any additional quality optimizations when transcoding HKSV events.
|
|
467
|
+
switch (this.codecSupport.hostSystem) {
|
|
468
|
+
case "raspbian":
|
|
469
|
+
// Raspberry Pi struggles with hardware-accelerated HKSV event recording due to issues in the FFmpeg codec driver, currently. We hope this improves
|
|
470
|
+
// over time and can offer it to Pi users, or develop a workaround. For now, we default to libx264.
|
|
471
|
+
return this.defaultVideoEncoderOptions(options);
|
|
472
|
+
default:
|
|
473
|
+
// By default, we use the same options for HKSV and streaming.
|
|
474
|
+
return this.streamEncoder(options);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Returns the video encoder options to use when transcoding for livestreaming.
|
|
479
|
+
*
|
|
480
|
+
* @param options - Encoder options to use.
|
|
481
|
+
* @returns Array of FFmpeg command-line arguments for video encoding.
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
*
|
|
485
|
+
* ```ts
|
|
486
|
+
* const args = ffmpegOpts.streamEncoder(encoderOptions);
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
streamEncoder(options) {
|
|
490
|
+
// Default smart quality to true.
|
|
491
|
+
if (options.useSmartQuality === undefined) {
|
|
492
|
+
options.useSmartQuality = true;
|
|
493
|
+
}
|
|
494
|
+
// In case we don't have a defined pixel format.
|
|
495
|
+
if (!this.hwPixelFormat.length) {
|
|
496
|
+
this.hwPixelFormat.push("yuvj420p");
|
|
497
|
+
}
|
|
498
|
+
// If we aren't hardware-accelerated, we default to libx264.
|
|
499
|
+
if (!this.options.hardwareTranscoding) {
|
|
500
|
+
return this.defaultVideoEncoderOptions(options);
|
|
501
|
+
}
|
|
502
|
+
// If we've enabled hardware-accelerated transcoding, let's select encoder options accordingly.
|
|
503
|
+
//
|
|
504
|
+
// We begin by adjusting the maximum bitrate tolerance used with -bufsize. This provides an upper bound on bitrate, with a little bit extra to allow encoders some
|
|
505
|
+
// variation in order to maximize quality while honoring bandwidth constraints.
|
|
506
|
+
const adjustedMaxBitrate = options.bitrate + (options.useSmartQuality ? HOMEKIT_STREAMING_HEADROOM : 0);
|
|
507
|
+
// Check the input and output frame rates to see if we need to change it.
|
|
508
|
+
const useFpsFilter = options.fps !== options.inputFps;
|
|
509
|
+
// Initialize our options.
|
|
510
|
+
const encoderOptions = [];
|
|
511
|
+
let videoFilters = [];
|
|
512
|
+
// Set our FFmpeg video filter options:
|
|
513
|
+
//
|
|
514
|
+
// format= Set the pixel formats we want to target for output.
|
|
515
|
+
videoFilters.push("format=" + this.hwPixelFormat.join("|"));
|
|
516
|
+
// scale=-2:min(ih\,height) Scale the video to the size that's being requested while respecting aspect ratios and ensuring our final dimensions are
|
|
517
|
+
// a power of two.
|
|
518
|
+
videoFilters.push("scale=-2:min(ih\\," + options.height.toString() + ")");
|
|
519
|
+
// Crop the stream, if the user has requested it.
|
|
520
|
+
if (this.options.crop) {
|
|
521
|
+
videoFilters.push(this.cropFilter);
|
|
522
|
+
}
|
|
523
|
+
// fps=fps= Use the fps filter to provide the frame rate requested by HomeKit. This has better performance characteristics rather than using
|
|
524
|
+
// "-r". We only need to apply this filter if our input and output frame rates aren't already identical.
|
|
525
|
+
if (useFpsFilter) {
|
|
526
|
+
videoFilters.push("fps=fps=" + options.fps.toString());
|
|
527
|
+
}
|
|
528
|
+
switch (this.codecSupport.hostSystem) {
|
|
529
|
+
case "macOS.Apple":
|
|
530
|
+
// h264_videotoolbox is the macOS hardware encoder API. We use the following options on Apple Silicon:
|
|
531
|
+
//
|
|
532
|
+
// -c:v Specify the macOS hardware encoder, h264_videotoolbox.
|
|
533
|
+
// -allow_sw 1 Allow the use of the software encoder if the hardware encoder is occupied or unavailable.
|
|
534
|
+
// This allows us to scale when we get multiple streaming requests simultaneously and consume all the available encode engines.
|
|
535
|
+
// -realtime 1 We prefer speed over quality - if the encoder has to make a choice, sacrifice one for the other.
|
|
536
|
+
// -coder cabac Use the cabac encoder for better video quality with the encoding profiles we use for HomeKit.
|
|
537
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
538
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level on macOS when we're using hardware-accelerated transcoding because
|
|
539
|
+
// the hardware encoder is particular about how to use levels. Setting it to 0 allows the encoder to decide for itself.
|
|
540
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
541
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
542
|
+
// -filter:v Set the pixel format, adjust the frame rate if needed, and scale the video to the size we want while respecting aspect ratios and
|
|
543
|
+
// ensuring our final dimensions are a power of two.
|
|
544
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
545
|
+
// livestreamng exerience.
|
|
546
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
547
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
548
|
+
encoderOptions.push("-c:v", "h264_videotoolbox", "-allow_sw", "1", "-realtime", "1", "-coder", "cabac", "-profile:v", this.getH264Profile(options.profile), "-level:v", "0", "-bf", "0", "-noautoscale", "-filter:v", videoFilters.join(", "), "-g:v", (options.fps * options.idrInterval).toString(), "-bufsize", (2 * options.bitrate).toString() + "k", "-maxrate", adjustedMaxBitrate.toString() + "k");
|
|
549
|
+
if (options.useSmartQuality) {
|
|
550
|
+
// -q:v 90 Use a fixed quality scale of 90, to allow videotoolbox the ability to vary bitrates to achieve the visual quality we want,
|
|
551
|
+
// constrained by our maximum bitrate. This is an Apple Silicon-specific feature.
|
|
552
|
+
encoderOptions.push("-q:v", "90");
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
// -b:v Average bitrate that's being requested by HomeKit.
|
|
556
|
+
encoderOptions.push("-b:v", options.bitrate.toString() + "k");
|
|
557
|
+
}
|
|
558
|
+
return encoderOptions;
|
|
559
|
+
case "macOS.Intel":
|
|
560
|
+
// h264_videotoolbox is the macOS hardware encoder API. We use the following options on Intel-based Macs:
|
|
561
|
+
//
|
|
562
|
+
// -c:v Specify the macOS hardware encoder, h264_videotoolbox.
|
|
563
|
+
// -allow_sw 1 Allow the use of the software encoder if the hardware encoder is occupied or unavailable.
|
|
564
|
+
// This allows us to scale when we get multiple streaming requests simultaneously that can consume all the available encode engines.
|
|
565
|
+
// -realtime 1 We prefer speed over quality - if the encoder has to make a choice, sacrifice one for the other.
|
|
566
|
+
// -coder cabac Use the cabac encoder for better video quality with the encoding profiles we use for HomeKit.
|
|
567
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
568
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level on macOS when we're using hardware-accelerated transcoding because
|
|
569
|
+
// the hardware encoder is particular about how to use levels. Setting it to 0 allows the encoder to decide for itself.
|
|
570
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
571
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
572
|
+
// -filter:v Set the pixel format, adjust the frame rate if needed, and scale the video to the size we want while respecting aspect ratios and
|
|
573
|
+
// ensuring our final dimensions are a power of two.
|
|
574
|
+
// -b:v Average bitrate that's being requested by HomeKit. We can't use a quality constraint and allow for more optimization of the
|
|
575
|
+
// bitrate on Intel-based Macs due to hardware / API limitations.
|
|
576
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
577
|
+
// livestreaming exerience.
|
|
578
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
579
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
580
|
+
return [
|
|
581
|
+
"-c:v", "h264_videotoolbox",
|
|
582
|
+
"-allow_sw", "1",
|
|
583
|
+
"-realtime", "1",
|
|
584
|
+
"-coder", "cabac",
|
|
585
|
+
"-profile:v", this.getH264Profile(options.profile),
|
|
586
|
+
"-level:v", "0",
|
|
587
|
+
"-bf", "0",
|
|
588
|
+
"-noautoscale",
|
|
589
|
+
"-filter:v", videoFilters.join(", "),
|
|
590
|
+
"-b:v", options.bitrate.toString() + "k",
|
|
591
|
+
"-g:v", (options.fps * options.idrInterval).toString(),
|
|
592
|
+
"-bufsize", (2 * options.bitrate).toString() + "k",
|
|
593
|
+
"-maxrate", adjustedMaxBitrate.toString() + "k"
|
|
594
|
+
];
|
|
595
|
+
case "raspbian":
|
|
596
|
+
// h264_v4l2m2m is the preferred interface to the Raspberry Pi hardware encoder API. We use the following options:
|
|
597
|
+
//
|
|
598
|
+
// -c:v Specify the Raspberry Pi hardware encoder, h264_v4l2m2m.
|
|
599
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
600
|
+
// -filter:v Set the pixel format, adjust the frame rate if needed, and scale the video to the size we want while respecting aspect ratios and
|
|
601
|
+
// ensuring our final dimensions are a power of two.
|
|
602
|
+
// -b:v Average bitrate that's being requested by HomeKit. We can't use a quality constraint and allow for more optimization of the
|
|
603
|
+
// bitrate due to v4l2m2m limitations.
|
|
604
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
605
|
+
// livestreamng exerience.
|
|
606
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
607
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
608
|
+
return [
|
|
609
|
+
"-c:v", "h264_v4l2m2m",
|
|
610
|
+
"-profile:v", this.getH264Profile(options.profile, true),
|
|
611
|
+
"-bf", "0",
|
|
612
|
+
"-noautoscale",
|
|
613
|
+
"-reset_timestamps", "1",
|
|
614
|
+
"-filter:v", videoFilters.join(", "),
|
|
615
|
+
"-b:v", options.bitrate.toString() + "k",
|
|
616
|
+
"-g:v", (options.fps * options.idrInterval).toString(),
|
|
617
|
+
"-bufsize", (2 * options.bitrate).toString() + "k",
|
|
618
|
+
"-maxrate", adjustedMaxBitrate.toString() + "k"
|
|
619
|
+
];
|
|
620
|
+
default:
|
|
621
|
+
// Clear out any prior video filters.
|
|
622
|
+
videoFilters = [];
|
|
623
|
+
// We execute the following GPU-accelerated operations using the Quick Sync Video post-processing filter:
|
|
624
|
+
//
|
|
625
|
+
// format=same Set the output pixel format to the same as the input, since it's already in the GPU.
|
|
626
|
+
// w=...:h... Scale the video to the size that's being requested while respecting aspect ratios.
|
|
627
|
+
videoFilters.push("vpp_qsv=" + [
|
|
628
|
+
"format=same",
|
|
629
|
+
"w=min(iw\\, (iw / ih) * " + options.height.toString() + ")",
|
|
630
|
+
"h=min(ih\\, " + options.height.toString() + ")"
|
|
631
|
+
].join(":"));
|
|
632
|
+
// fps=fps= Use the fps filter to provide the frame rate requested by HomeKit. This has better performance characteristics rather than using
|
|
633
|
+
// "-r". We only need to apply this filter if our input and output frame rates aren't already identical.
|
|
634
|
+
if (useFpsFilter) {
|
|
635
|
+
videoFilters.push("fps=fps=" + options.fps.toString());
|
|
636
|
+
}
|
|
637
|
+
// h264_qsv is the Intel Quick Sync Video hardware encoder API. We use the following options:
|
|
638
|
+
//
|
|
639
|
+
// -c:v Specify the Intel Quick Sync Video hardware encoder, h264_qsv.
|
|
640
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
641
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level when we're using hardware-accelerated transcoding because
|
|
642
|
+
// the hardware encoder will determine which levels to use. Setting it to 0 allows the encoder to decide for itself.
|
|
643
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
644
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
645
|
+
// -init_hw_device Initialize our hardware accelerator and assign it a name to be used in the FFmpeg command line.
|
|
646
|
+
// -filter_hw_device Specify the hardware accelerator to be used with our video filter pipeline.
|
|
647
|
+
// -filter:v Set the pixel format, adjust the frame rate if needed, and scale the video to the size we want while respecting aspect ratios and
|
|
648
|
+
// ensuring our final dimensions are a power of two.
|
|
649
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
650
|
+
// livestreamng exerience.
|
|
651
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
652
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
653
|
+
encoderOptions.push("-c:v", "h264_qsv", "-profile:v", this.getH264Profile(options.profile), "-level:v", "0", "-bf", "0", "-noautoscale", "-init_hw_device", "qsv=hw", "-filter_hw_device", "hw", "-filter:v", videoFilters.join(", "), "-g:v", (options.fps * options.idrInterval).toString(), "-bufsize", (2 * options.bitrate).toString() + "k", "-maxrate", adjustedMaxBitrate.toString() + "k");
|
|
654
|
+
if (options.useSmartQuality) {
|
|
655
|
+
// -global_quality 20 Use a global quality setting of 20, to allow QSV the ability to vary bitrates to achieve the visual quality we want,
|
|
656
|
+
// constrained by our maximum bitrate. This leverages a QSV-specific feature known as intelligent constant quality.
|
|
657
|
+
encoderOptions.push("-global_quality", "20");
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
// -b:v Average bitrate that's being requested by HomeKit.
|
|
661
|
+
encoderOptions.push("-b:v", options.bitrate.toString() + "k");
|
|
662
|
+
}
|
|
663
|
+
return encoderOptions;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Returns the maximum pixel count supported by a specific hardware encoder on the host system, or `Infinity` if not limited.
|
|
668
|
+
*
|
|
669
|
+
* @returns Maximum supported pixel count.
|
|
670
|
+
*/
|
|
671
|
+
get hostSystemMaxPixels() {
|
|
672
|
+
if (this.options.hardwareTranscoding) {
|
|
673
|
+
switch (this.codecSupport.hostSystem) {
|
|
674
|
+
case "raspbian":
|
|
675
|
+
// For constrained environments like Raspberry Pi, when hardware transcoding has been selected for a camera, we limit the available source streams to no more
|
|
676
|
+
// than 1080p. In practice, that means that devices like the G4 Pro can't use their highest quality stream for transcoding due to the limitations of the
|
|
677
|
+
// Raspberry Pi GPU that cannot support higher pixel counts.
|
|
678
|
+
return 1920 * 1080;
|
|
679
|
+
default:
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return Infinity;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Converts a HomeKit H.264 level enum value to the corresponding FFmpeg string or numeric representation.
|
|
687
|
+
*
|
|
688
|
+
* This helper is used to translate between HomeKit’s `H264Level` enum and the string or numeric format expected by FFmpeg’s `-level:v` argument.
|
|
689
|
+
*
|
|
690
|
+
* @param level - The H.264 level to translate.
|
|
691
|
+
* @param numeric - Optional. If `true`, returns the numeric representation (e.g., "31"). If `false` or omitted, returns the standard string format (e.g., "3.1").
|
|
692
|
+
*
|
|
693
|
+
* @returns The FFmpeg-compatible H.264 level string or numeric value.
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
*
|
|
697
|
+
* ```ts
|
|
698
|
+
* ffmpegOpts['getH264Level'](H264Level.LEVEL3_1); // "3.1"
|
|
699
|
+
*
|
|
700
|
+
* ffmpegOpts['getH264Level'](H264Level.LEVEL4_0, true); // "40"
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @see H264Level
|
|
704
|
+
*/
|
|
705
|
+
getH264Level(level, numeric = false) {
|
|
706
|
+
switch (level) {
|
|
707
|
+
case 0 /* H264Level.LEVEL3_1 */:
|
|
708
|
+
return numeric ? "31" : "3.1";
|
|
709
|
+
case 1 /* H264Level.LEVEL3_2 */:
|
|
710
|
+
return numeric ? "32" : "3.2";
|
|
711
|
+
case 2 /* H264Level.LEVEL4_0 */:
|
|
712
|
+
return numeric ? "40" : "4.0";
|
|
713
|
+
default:
|
|
714
|
+
return numeric ? "31" : "3.1";
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Converts a HomeKit H.264 profile enum value to the corresponding FFmpeg string or numeric representation.
|
|
719
|
+
*
|
|
720
|
+
* This helper is used to translate between HomeKit’s `H264Profile` enum and the string or numeric format expected by FFmpeg’s `-profile:v` argument.
|
|
721
|
+
*
|
|
722
|
+
* @param profile - The H.264 profile to translate.
|
|
723
|
+
* @param numeric - Optional. If `true`, returns the numeric representation (e.g., "100"). If `false` or omitted, returns the standard string format (e.g., "high").
|
|
724
|
+
*
|
|
725
|
+
* @returns The FFmpeg-compatible H.264 profile string or numeric value.
|
|
726
|
+
*
|
|
727
|
+
* @example
|
|
728
|
+
*
|
|
729
|
+
* ```ts
|
|
730
|
+
* ffmpegOpts['getH264Profile'](H264Profile.HIGH); // "high"
|
|
731
|
+
*
|
|
732
|
+
* ffmpegOpts['getH264Profile'](H264Profile.BASELINE, true); // "66"
|
|
733
|
+
* ```
|
|
734
|
+
*
|
|
735
|
+
* @see H264Profile
|
|
736
|
+
*/
|
|
737
|
+
getH264Profile(profile, numeric = false) {
|
|
738
|
+
switch (profile) {
|
|
739
|
+
case 0 /* H264Profile.BASELINE */:
|
|
740
|
+
return numeric ? "66" : "baseline";
|
|
741
|
+
case 2 /* H264Profile.HIGH */:
|
|
742
|
+
return numeric ? "100" : "high";
|
|
743
|
+
case 1 /* H264Profile.MAIN */:
|
|
744
|
+
return numeric ? "77" : "main";
|
|
745
|
+
default:
|
|
746
|
+
return numeric ? "77" : "main";
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
//# sourceMappingURL=options.js.map
|