homebridge-unifi-protect 6.5.1 → 6.7.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 +12 -12
- package/config.schema.json +24 -3
- package/dist/protect-camera.d.ts +1 -2
- package/dist/protect-camera.js +21 -8
- package/dist/protect-camera.js.map +1 -1
- package/dist/protect-device.d.ts +1 -0
- package/dist/protect-device.js +33 -2
- package/dist/protect-device.js.map +1 -1
- package/dist/protect-ffmpeg-codecs.d.ts +19 -0
- package/dist/protect-ffmpeg-codecs.js +141 -0
- package/dist/protect-ffmpeg-codecs.js.map +1 -0
- package/dist/protect-ffmpeg-options.d.ts +20 -0
- package/dist/protect-ffmpeg-options.js +468 -0
- package/dist/protect-ffmpeg-options.js.map +1 -0
- package/dist/protect-ffmpeg-record.js +21 -24
- package/dist/protect-ffmpeg-record.js.map +1 -1
- package/dist/protect-ffmpeg-stream.d.ts +5 -2
- package/dist/protect-ffmpeg-stream.js +8 -11
- package/dist/protect-ffmpeg-stream.js.map +1 -1
- package/dist/protect-ffmpeg.d.ts +1 -4
- package/dist/protect-ffmpeg.js +23 -10
- package/dist/protect-ffmpeg.js.map +1 -1
- package/dist/protect-nvr-events.d.ts +0 -2
- package/dist/protect-nvr-events.js +36 -6
- package/dist/protect-nvr-events.js.map +1 -1
- package/dist/protect-nvr.js +17 -3
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +2 -0
- package/dist/protect-options.js +2 -1
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.d.ts +2 -3
- package/dist/protect-platform.js +11 -50
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.js +1 -1
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-rtp.js +3 -1
- package/dist/protect-rtp.js.map +1 -1
- package/dist/protect-stream.d.ts +3 -8
- package/dist/protect-stream.js +14 -248
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-types.d.ts +0 -23
- package/dist/protect-types.js.map +1 -1
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +4 -0
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +45 -10
- package/homebridge-ui/server.js +2 -0
- package/package.json +7 -7
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import util from "node:util";
|
|
4
|
+
export class FfmpegCodecs {
|
|
5
|
+
constructor(platform) {
|
|
6
|
+
this._gpuMem = 0;
|
|
7
|
+
this.log = platform.log;
|
|
8
|
+
this.platform = platform;
|
|
9
|
+
this.videoProcessor = platform.config.videoProcessor;
|
|
10
|
+
this.videoProcessorCodecs = {};
|
|
11
|
+
this.videoProcessorHwAccels = {};
|
|
12
|
+
}
|
|
13
|
+
// Launch our configured controllers once all accessories have been loaded. Once we do, they will sustain themselves.
|
|
14
|
+
async probe() {
|
|
15
|
+
// Let's conduct our system-specific capability probes.
|
|
16
|
+
switch (this.platform.hostSystem) {
|
|
17
|
+
case "raspbian":
|
|
18
|
+
// If we're on a Raspberry Pi, let's verify that we have enough GPU memory for hardware-based decoding and encoding.
|
|
19
|
+
await this.probeRpiGpuMem();
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
// First things first - ensure we've got a working video processor before we do anything else.
|
|
25
|
+
if (!(await this.probeVideoProcessorCodecs()) || !(await this.probeVideoProcessorHwAccel())) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
// Utility to determine whether or not a specific decoder is available to the video processor for a given format.
|
|
31
|
+
hasDecoder(codec, decoder) {
|
|
32
|
+
// Normalize our lookups.
|
|
33
|
+
codec = codec.toLowerCase();
|
|
34
|
+
decoder = decoder.toLowerCase();
|
|
35
|
+
return this.videoProcessorCodecs[codec]?.decoders.some(x => x === decoder);
|
|
36
|
+
}
|
|
37
|
+
// Utility to determine whether or not a specific encoder is available to the video processor for a given format.
|
|
38
|
+
hasEncoder(codec, encoder) {
|
|
39
|
+
// Normalize our lookups.
|
|
40
|
+
codec = codec.toLowerCase();
|
|
41
|
+
encoder = encoder.toLowerCase();
|
|
42
|
+
return this.videoProcessorCodecs[codec]?.encoders.some(x => x === encoder);
|
|
43
|
+
}
|
|
44
|
+
// Utility to determine whether or not a specific decoder is available to the video processor for a given format.
|
|
45
|
+
hasHwAccel(accel) {
|
|
46
|
+
return this.videoProcessorHwAccels[accel.toLowerCase()] ? true : false;
|
|
47
|
+
}
|
|
48
|
+
get gpuMem() {
|
|
49
|
+
return this._gpuMem;
|
|
50
|
+
}
|
|
51
|
+
// Probe our video processor's hardware acceleration capabilities.
|
|
52
|
+
async probeVideoProcessorHwAccel() {
|
|
53
|
+
return this.probeCmd(this.videoProcessor, ["-hide_banner", "-hwaccels"], (stdout) => {
|
|
54
|
+
// Iterate through each line, and a build a list of encoders.
|
|
55
|
+
for (const accel of stdout.split(os.EOL)) {
|
|
56
|
+
// Skip blank lines.
|
|
57
|
+
if (!accel.length) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Skip the first line.
|
|
61
|
+
if (accel === "Hardware acceleration methods:") {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// We've found a hardware acceleration method, let's add it.
|
|
65
|
+
this.videoProcessorHwAccels[accel.toLowerCase()] = true;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Probe our video processor's encoding and decoding capabilities.
|
|
70
|
+
async probeVideoProcessorCodecs() {
|
|
71
|
+
return this.probeCmd(this.videoProcessor, ["-hide_banner", "-codecs"], (stdout) => {
|
|
72
|
+
// A regular expression to parse out the codec and it's supported decoders.
|
|
73
|
+
const decodersRegex = /\S+\s+(\S+).+\(decoders: (.*?)\s*\)/;
|
|
74
|
+
// A regular expression to parse out the codec and it's supported encoders.
|
|
75
|
+
const encodersRegex = /\S+\s+(\S+).+\(encoders: (.*?)\s*\)/;
|
|
76
|
+
// Iterate through each line, and a build a list of encoders.
|
|
77
|
+
for (const codecLine of stdout.split(os.EOL)) {
|
|
78
|
+
// Let's see if we have decoders.
|
|
79
|
+
const decodersMatch = decodersRegex.exec(codecLine);
|
|
80
|
+
// Let's see if we have encoders.
|
|
81
|
+
const encodersMatch = encodersRegex.exec(codecLine);
|
|
82
|
+
// If we found decoders, add them to our list of supported decoders for this format.
|
|
83
|
+
if (decodersMatch) {
|
|
84
|
+
this.videoProcessorCodecs[decodersMatch[1]] = { decoders: [], encoders: [] };
|
|
85
|
+
this.videoProcessorCodecs[decodersMatch[1]].decoders = decodersMatch[2].split(" ").map(x => x.toLowerCase());
|
|
86
|
+
}
|
|
87
|
+
// If we found decoders, add them to our list of supported decoders for this format.
|
|
88
|
+
if (encodersMatch) {
|
|
89
|
+
if (!this.videoProcessorCodecs[encodersMatch[1]]) {
|
|
90
|
+
this.videoProcessorCodecs[encodersMatch[1]] = { decoders: [], encoders: [] };
|
|
91
|
+
}
|
|
92
|
+
this.videoProcessorCodecs[encodersMatch[1]].encoders = encodersMatch[2].split(" ").map(x => x.toLowerCase());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Probe Raspberry Pi GPU.
|
|
98
|
+
async probeRpiGpuMem() {
|
|
99
|
+
return this.probeCmd("/usr/bin/vcgencmd", ["get_mem", "gpu"], (stdout) => {
|
|
100
|
+
// A regular expression to parse out the configured GPU memory on the Raspberry Pi.
|
|
101
|
+
const gpuRegex = /^gpu=(.*)M\n$/;
|
|
102
|
+
// Let's see what we've got.
|
|
103
|
+
const gpuMatch = gpuRegex.exec(stdout);
|
|
104
|
+
// We matched what we're looking for.
|
|
105
|
+
if (gpuMatch) {
|
|
106
|
+
// Parse the result and retrieve our allocated GPU memory.
|
|
107
|
+
this._gpuMem = parseInt(gpuMatch[1]);
|
|
108
|
+
// Something went wrong.
|
|
109
|
+
if (isNaN(this._gpuMem)) {
|
|
110
|
+
this._gpuMem = 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Utility to probe the capabilities of FFmpeg and the host platform.
|
|
116
|
+
async probeCmd(command, commandLineArgs, processOutput) {
|
|
117
|
+
try {
|
|
118
|
+
// Promisify exec to allow us to wait for it asynchronously.
|
|
119
|
+
const execAsync = util.promisify(execFile);
|
|
120
|
+
// Check for the codecs in our video processor.
|
|
121
|
+
const { stdout } = await execAsync(command, commandLineArgs);
|
|
122
|
+
processOutput(stdout);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// It's really a SystemError, but Node hides that type from us for esoteric reasons.
|
|
127
|
+
if (error instanceof Error) {
|
|
128
|
+
const execError = error;
|
|
129
|
+
if (execError.code === "ENOENT") {
|
|
130
|
+
this.log.error("Unable to find FFmpeg at: '%s'. Please make sure that you have a working version of FFmpeg installed in order to use this plugin.", execError.path);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.log.error("Error running FFmpeg: %s", error.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
this.log.error("Unable to complete plugin startup without a working version of FFmpeg.");
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=protect-ffmpeg-codecs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect-ffmpeg-codecs.js","sourceRoot":"","sources":["../src/protect-ffmpeg-codecs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { H264Level, H264Profile } from "homebridge";
|
|
2
|
+
import { ProtectCamera } from "./protect-camera.js";
|
|
3
|
+
export declare class FfmpegOptions {
|
|
4
|
+
private readonly hwPixelFormat;
|
|
5
|
+
private readonly log;
|
|
6
|
+
private readonly platform;
|
|
7
|
+
private readonly protectCamera;
|
|
8
|
+
constructor(protectCamera: ProtectCamera);
|
|
9
|
+
private configureHwAccel;
|
|
10
|
+
get audioEncoder(): string[];
|
|
11
|
+
get audioDecoder(): string;
|
|
12
|
+
get videoDecoder(): string[];
|
|
13
|
+
private defaultVideoEncoderOptions;
|
|
14
|
+
recordEncoder(width: number, height: number, fps: number, bitrate: number, profile: H264Profile, level: H264Level, idrInterval: number): string[];
|
|
15
|
+
streamEncoder(width: number, height: number, fps: number, bitrate: number, profile: H264Profile, level: H264Level, idrInterval: number, useSmartQuality?: boolean): string[];
|
|
16
|
+
get recordingDefaultChannel(): string | undefined;
|
|
17
|
+
get hostSystemMaxPixels(): number;
|
|
18
|
+
private getH264Level;
|
|
19
|
+
private getH264Profile;
|
|
20
|
+
}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { PROTECT_HOMEKIT_STREAMING_HEADROOM, PROTECT_RPI_GPU_MINIMUM } from "./settings.js";
|
|
2
|
+
export class FfmpegOptions {
|
|
3
|
+
// Create an instance of a HomeKit streaming delegate.
|
|
4
|
+
constructor(protectCamera) {
|
|
5
|
+
this.hwPixelFormat = [];
|
|
6
|
+
this.log = protectCamera.log;
|
|
7
|
+
this.platform = protectCamera.platform;
|
|
8
|
+
this.protectCamera = protectCamera;
|
|
9
|
+
// Configure our hardware acceleration support.
|
|
10
|
+
this.configureHwAccel();
|
|
11
|
+
}
|
|
12
|
+
// Determine the video encoder to use when transcoding.
|
|
13
|
+
configureHwAccel() {
|
|
14
|
+
let logMessage = "";
|
|
15
|
+
const accelCategories = this.protectCamera.hints.hardwareTranscoding ? "decoding and transcoding" : "decoding";
|
|
16
|
+
// Hardware-accelerated decoding is enabled by default, where supported. Let's select the decoder options accordingly where supported.
|
|
17
|
+
if (this.protectCamera.hints.hardwareDecoding) {
|
|
18
|
+
// Utility function to check that we have a specific decoder codec available to us.
|
|
19
|
+
const validateDecoder = (codec, pixelFormat) => {
|
|
20
|
+
if (!this.platform.codecSupport.hasDecoder("h264", codec)) {
|
|
21
|
+
this.log.error("Unable to enable hardware accelerated decoding. Your video processor does not have support for the " + codec + " decoder. " +
|
|
22
|
+
"Using software decoding instead.");
|
|
23
|
+
this.protectCamera.hints.hardwareDecoding = false;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
this.hwPixelFormat.push(...pixelFormat);
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
// Utility function to check that we have a specific decoder codec available to us.
|
|
30
|
+
const validateHwAccel = (accel, pixelFormat) => {
|
|
31
|
+
if (!this.platform.codecSupport.hasHwAccel(accel)) {
|
|
32
|
+
this.log.error("Unable to enable hardware accelerated decoding. Your video processor does not have support for the " + accel + " hardware accelerator. " +
|
|
33
|
+
"Using software decoding instead.");
|
|
34
|
+
this.protectCamera.hints.hardwareDecoding = false;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
this.hwPixelFormat.push(...pixelFormat);
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
40
|
+
switch (this.platform.hostSystem) {
|
|
41
|
+
case "macOS.Apple":
|
|
42
|
+
case "macOS.Intel":
|
|
43
|
+
// Verify that we have hardware-accelerated decoding available to us.
|
|
44
|
+
validateHwAccel("videotoolbox", ["videotoolbox_vld", "nv12", "yuv420p"]);
|
|
45
|
+
break;
|
|
46
|
+
case "raspbian":
|
|
47
|
+
// If it's less than the minimum hardware GPU memory we need on an Raspberry Pi, we revert back to our default decoder.
|
|
48
|
+
if (this.platform.codecSupport.gpuMem < PROTECT_RPI_GPU_MINIMUM) {
|
|
49
|
+
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, PROTECT_RPI_GPU_MINIMUM);
|
|
50
|
+
this.protectCamera.hints.hardwareDecoding = false;
|
|
51
|
+
this.protectCamera.hints.hardwareTranscoding = false;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Verify that we have the hardware decoder available to us.
|
|
55
|
+
validateDecoder("h264_mmal", ["mmal", "yuv420p"]);
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
// Back to software decoding unless we're on a known system that always supports hardware decoding.
|
|
59
|
+
this.protectCamera.hints.hardwareDecoding = false;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// If we've enabled hardware-accelerated transcoding, let's select the encoder options accordingly where supported.
|
|
64
|
+
if (this.protectCamera.hints.hardwareTranscoding) {
|
|
65
|
+
// Utility function to check that we have a specific encoder codec available to us.
|
|
66
|
+
const validateEncoder = (codec) => {
|
|
67
|
+
if (!this.platform.codecSupport.hasEncoder("h264", codec)) {
|
|
68
|
+
this.log.error("Unable to enable hardware accelerated transcoding. Your video processor does not have support for the " + codec + " encoder. " +
|
|
69
|
+
"Using software transcoding instead.");
|
|
70
|
+
this.protectCamera.hints.hardwareTranscoding = false;
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
75
|
+
switch (this.platform.hostSystem) {
|
|
76
|
+
case "macOS.Apple":
|
|
77
|
+
case "macOS.Intel":
|
|
78
|
+
// Verify that we have the hardware encoder available to us.
|
|
79
|
+
validateEncoder("h264_videotoolbox");
|
|
80
|
+
// Validate that we have access to the AudioToolbox AAC encoder.
|
|
81
|
+
if (!this.platform.codecSupport.hasEncoder("aac", "aac_at")) {
|
|
82
|
+
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.");
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case "raspbian":
|
|
86
|
+
// Verify that we have the hardware encoder available to us.
|
|
87
|
+
validateEncoder("h264_v4l2m2m");
|
|
88
|
+
logMessage = "Raspberry Pi hardware acceleration will be used for livestreaming. " +
|
|
89
|
+
"HomeKit Secure Video recordings are not supported by the hardware encoder and will use software transcoding instead";
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
// Let's see if we have Intel QuickSync hardware decoding available to us.
|
|
93
|
+
if (this.platform.codecSupport.hasHwAccel("qsv") &&
|
|
94
|
+
this.platform.codecSupport.hasDecoder("h264", "h264_qsv") && this.platform.codecSupport.hasEncoder("h264", "h264_qsv")) {
|
|
95
|
+
this.protectCamera.hints.hardwareDecoding = true;
|
|
96
|
+
this.hwPixelFormat.push("qsv", "yuv420p");
|
|
97
|
+
logMessage = "Intel Quick Sync Video";
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Back to software encoding.
|
|
101
|
+
this.protectCamera.hints.hardwareTranscoding = false;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Inform the user.
|
|
107
|
+
if (this.protectCamera.hints.hardwareDecoding || this.protectCamera.hints.hardwareTranscoding) {
|
|
108
|
+
this.log.info("Hardware accelerated %s enabled%s.", accelCategories, logMessage.length ? ": " + logMessage : "");
|
|
109
|
+
}
|
|
110
|
+
return this.protectCamera.hints.hardwareTranscoding;
|
|
111
|
+
}
|
|
112
|
+
// Return the audio encoder options to use when transcoding.
|
|
113
|
+
get audioEncoder() {
|
|
114
|
+
// If we don't have libfdk_aac available to us, we're essentially dead in the water.
|
|
115
|
+
let encoderOptions = [];
|
|
116
|
+
// Utility function to return a default audio encoder codec.
|
|
117
|
+
const defaultAudioEncoderOptions = () => {
|
|
118
|
+
if (this.platform.codecSupport.hasEncoder("aac", "libfdk_aac")) {
|
|
119
|
+
// Default to libfdk_aac since FFmpeg doesn't natively support AAC-ELD. We use the following options by default:
|
|
120
|
+
//
|
|
121
|
+
// -acodec libfdk_aac Use the libfdk_aac encoder.
|
|
122
|
+
// -afterburner 1 Increases audio quality at the expense of needing a little bit more computational power in libfdk_aac.
|
|
123
|
+
// -eld_sbr 1 Use spectral band replication to further enhance audio.
|
|
124
|
+
// -eld_v2 1 Use the enhanced low delay v2 standard for better audio characteristics.
|
|
125
|
+
return [
|
|
126
|
+
"-acodec", "libfdk_aac",
|
|
127
|
+
"-afterburner", "1",
|
|
128
|
+
"-eld_sbr", "1",
|
|
129
|
+
"-eld_v2", "1"
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
switch (this.platform.hostSystem) {
|
|
137
|
+
case "macOS.Apple":
|
|
138
|
+
case "macOS.Intel":
|
|
139
|
+
// If we don't have audiotoolbox available, let's default back to libfdk_aac.
|
|
140
|
+
if (!this.platform.codecSupport.hasEncoder("aac", "aac_at")) {
|
|
141
|
+
encoderOptions = defaultAudioEncoderOptions();
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
// aac_at is the macOS audio encoder API. We use the following options:
|
|
145
|
+
//
|
|
146
|
+
// -acodec aac_at Use the aac_at encoder on macOS.
|
|
147
|
+
// -aac_at_mode cvbr Use the constrained variable bitrate setting to allow the encoder to optimize audio, while remaining within the requested bitrates.
|
|
148
|
+
encoderOptions = [
|
|
149
|
+
"-acodec", "aac_at",
|
|
150
|
+
"-aac_at_mode", "cvbr"
|
|
151
|
+
];
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
encoderOptions = defaultAudioEncoderOptions();
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
return encoderOptions;
|
|
158
|
+
}
|
|
159
|
+
// Return the audio encoder to use when decoding.
|
|
160
|
+
get audioDecoder() {
|
|
161
|
+
return "libfdk_aac";
|
|
162
|
+
}
|
|
163
|
+
// Return the video decoder options to use when decoding video.
|
|
164
|
+
get videoDecoder() {
|
|
165
|
+
// Default to no special decoder options for inbound streams.
|
|
166
|
+
let decoderOptions = [];
|
|
167
|
+
// If we've enabled hardware-accelerated transcoding, let's select decoder options accordingly where supported.
|
|
168
|
+
if (this.protectCamera.hints.hardwareDecoding) {
|
|
169
|
+
switch (this.platform.hostSystem) {
|
|
170
|
+
case "macOS.Apple":
|
|
171
|
+
case "macOS.Intel":
|
|
172
|
+
// h264_videotoolbox is the macOS hardware decoder and encoder API. We use the following options for decoding video:
|
|
173
|
+
//
|
|
174
|
+
// -hwaccel videotoolbox Select Video Toolbox for hardware accelerated H.264 decoding.
|
|
175
|
+
decoderOptions = [
|
|
176
|
+
"-hwaccel", "videotoolbox"
|
|
177
|
+
];
|
|
178
|
+
break;
|
|
179
|
+
case "raspbian":
|
|
180
|
+
// h264_mmal is the preferred Raspberry Pi hardware decoder codec. We use the following options for decoding video:
|
|
181
|
+
//
|
|
182
|
+
// -c:v h264_mmal Select the Multimedia Abstraction Layer codec for hardware accelerated H.264 processing.
|
|
183
|
+
decoderOptions = [
|
|
184
|
+
"-c:v", "h264_mmal"
|
|
185
|
+
];
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
// h264_qsv is the Intel Quick Sync Video hardware encoder and decoder.
|
|
189
|
+
//
|
|
190
|
+
// -hwaccel qsv Select Quick Sync Video to enable hardware accelerated H.264 decoding.
|
|
191
|
+
// -c:v h264_qsv Select the Quick Sync Video codec for hardware accelerated H.264 processing.
|
|
192
|
+
decoderOptions = [
|
|
193
|
+
"-hwaccel", "qsv",
|
|
194
|
+
"-hwaccel_output_format", "qsv",
|
|
195
|
+
"-c:v", "h264_qsv"
|
|
196
|
+
];
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return decoderOptions;
|
|
201
|
+
}
|
|
202
|
+
// Utility function to provide our default encoder options.
|
|
203
|
+
defaultVideoEncoderOptions(width, height, fps, bitrate, profile, level, idrInterval, useSmartQuality = true) {
|
|
204
|
+
// Default to the tried-and-true libx264. We use the following options by default:
|
|
205
|
+
//
|
|
206
|
+
// -c:v libx264 Use the excellent libx264 H.264 encoder by default, unless the user explicitly overrides it.
|
|
207
|
+
// -preset veryfast Use the veryfast encoding preset in libx264, which provides a good balance of encoding speed and quality.
|
|
208
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
209
|
+
// -level:v Use the H.264 profile level that HomeKit is requesting when encoding.
|
|
210
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
211
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
212
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final
|
|
213
|
+
// dimensions are a power of two.
|
|
214
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
215
|
+
// livestreamng exerience.
|
|
216
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
217
|
+
// -maxrate bitrate The maximum bitrate tolerance, used with -bufsize. This provides an upper bound on bitrate, with a little bit extra to
|
|
218
|
+
// allow encoders some variation in order to maximize quality while honoring bandwidth constraints.
|
|
219
|
+
const encoderOptions = [
|
|
220
|
+
// If the user has specified a video encoder, let's use it instead.
|
|
221
|
+
"-c:v", this.platform.config.videoEncoder ?? "libx264",
|
|
222
|
+
"-preset", "veryfast",
|
|
223
|
+
"-profile:v", this.getH264Profile(profile),
|
|
224
|
+
"-level:v", this.getH264Level(level),
|
|
225
|
+
"-noautoscale",
|
|
226
|
+
"-bf", "0",
|
|
227
|
+
"-filter:v", "format=" + [...this.hwPixelFormat, "yuvj420p"].join("|") + ", scale=-2:min(ih\\," + height.toString() + ")",
|
|
228
|
+
"-g:v", (fps * idrInterval).toString(),
|
|
229
|
+
"-bufsize", (2 * bitrate).toString() + "k",
|
|
230
|
+
"-maxrate", (bitrate + (useSmartQuality ? PROTECT_HOMEKIT_STREAMING_HEADROOM : 0)).toString() + "k"
|
|
231
|
+
];
|
|
232
|
+
// Using libx264's constant rate factor mode produces generally better results across the board. We use a capped CRF approach, allowing libx264 to
|
|
233
|
+
// make intelligent choices about how to adjust bitrate to achieve a certain quality level depending on the complexity of the scene being encoded, but
|
|
234
|
+
// constraining it to a maximum bitrate to stay within the bandwidth constraints HomeKit is requesting.
|
|
235
|
+
if (useSmartQuality) {
|
|
236
|
+
// -crf 20 Use a constant rate factor of 20, to allow libx264 the ability to vary bitrates to achieve the visual quality we
|
|
237
|
+
// want, constrained by our maximum bitrate.
|
|
238
|
+
encoderOptions.push("-crf", "20");
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// 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
|
|
242
|
+
// 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
|
|
243
|
+
// HKSV typically requests bitrates of around 2000kbps, which results in a reasonably high quality recording, as opposed to the typical 2-300kbps
|
|
244
|
+
// that livestreaming from the Home app itself generates. Those lower bitrates in livestreaming really benefit from the magic that using a good CRF value
|
|
245
|
+
// can produce in libx264.
|
|
246
|
+
encoderOptions.push("-b:v", bitrate.toString() + "k");
|
|
247
|
+
}
|
|
248
|
+
return encoderOptions;
|
|
249
|
+
}
|
|
250
|
+
// Return the video encoder options to use for HKSV.
|
|
251
|
+
recordEncoder(width, height, fps, bitrate, profile, level, idrInterval) {
|
|
252
|
+
// Generaly, we default to using the same encoding options we use to transcode livestreams, unless we have platform-specific quirks we need to address,
|
|
253
|
+
// such as where we can have hardware accelerated transcoded livestreaming, but not hardware-accelerated HKSV event recording. The other noteworthy
|
|
254
|
+
// 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
|
|
255
|
+
// address quality. When we call our encoders, we also let them know we don't want any additional quality optimizations when transcoding HKSV events.
|
|
256
|
+
switch (this.platform.hostSystem) {
|
|
257
|
+
case "raspbian":
|
|
258
|
+
// Raspberry Pi struggles with hardware accelerated HKSV event recording due to issues in the FFmpeg codec driver, currently. We hope this improves
|
|
259
|
+
// over time and can offer it to Pi users, or develop a workaround. For now, we default to libx264.
|
|
260
|
+
return this.defaultVideoEncoderOptions(width, height, fps, bitrate, profile, level, idrInterval, false);
|
|
261
|
+
break;
|
|
262
|
+
default:
|
|
263
|
+
// By default, we use the same options for HKSV and streaming.
|
|
264
|
+
return this.streamEncoder(width, height, fps, bitrate, profile, level, idrInterval, false);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Return the video encoder options to use when transcoding.
|
|
268
|
+
streamEncoder(width, height, fps, bitrate, profile, level, idrInterval, useSmartQuality = true) {
|
|
269
|
+
const encoderOptions = [];
|
|
270
|
+
// Adjust the maximum bitrate tolerance used with -bufsize. This provides an upper bound on bitrate, with a little bit extra to allow encoders some
|
|
271
|
+
// variation in order to maximize quality while honoring bandwidth constraints.
|
|
272
|
+
const adjustedMaxBitrate = bitrate + (useSmartQuality ? PROTECT_HOMEKIT_STREAMING_HEADROOM : 0);
|
|
273
|
+
// If we've enabled hardware-accelerated transcoding, let's select encoder options accordingly where supported.
|
|
274
|
+
if (this.protectCamera.hints.hardwareTranscoding) {
|
|
275
|
+
switch (this.platform.hostSystem) {
|
|
276
|
+
case "macOS.Apple":
|
|
277
|
+
// h264_videotoolbox is the macOS hardware encoder API. We use the following options on Apple Silicon:
|
|
278
|
+
//
|
|
279
|
+
// -c:v Specify the macOS hardware encoder, h264_videotoolbox.
|
|
280
|
+
// -allow_sw 1 Allow the use of the software encoder if the hardware encoder is occupied or unavailable.
|
|
281
|
+
// This allows us to scale when we get multiple streaming requests simultaneously that might consume all the available encode engines.
|
|
282
|
+
// -realtime 1 We prefer speed over quality - if the encoder has to make a choice, sacrifice one for the other.
|
|
283
|
+
// -coder cabac Use the cabac encoder for better video quality with the encoding profiles we use in HBUP.
|
|
284
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
285
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level on macOS when we're using hardware accelerated transcoding because
|
|
286
|
+
// the hardware encoder is particular about how to use levels. Setting it to 0 allows the encoder to decide for itself.
|
|
287
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
288
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
289
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final dimensions are a
|
|
290
|
+
// power of two.
|
|
291
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
292
|
+
// livestreamng exerience.
|
|
293
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
294
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
295
|
+
encoderOptions.push("-c:v", "h264_videotoolbox", "-allow_sw", "1", "-realtime", "1", "-coder", "cabac", "-profile:v", this.getH264Profile(profile), "-level:v", "0", "-bf", "0", "-noautoscale", "-filter:v", "format=" + this.hwPixelFormat.join("|") + ", scale=-2:min(ih\\," + height.toString() + ")", "-g:v", (fps * idrInterval).toString(), "-bufsize", (2 * bitrate).toString() + "k", "-maxrate", adjustedMaxBitrate.toString() + "k");
|
|
296
|
+
if (useSmartQuality) {
|
|
297
|
+
// -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,
|
|
298
|
+
// constrained by our maximum bitrate. This is an Apple Silicon-specific feature.
|
|
299
|
+
encoderOptions.push("-q:v", "90");
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// -b:v Average bitrate that's being requested by HomeKit.
|
|
303
|
+
encoderOptions.push("-b:v", bitrate.toString() + "k");
|
|
304
|
+
}
|
|
305
|
+
return encoderOptions;
|
|
306
|
+
break;
|
|
307
|
+
case "macOS.Intel":
|
|
308
|
+
// h264_videotoolbox is the macOS hardware encoder API. We use the following options on Intel-based Macs:
|
|
309
|
+
//
|
|
310
|
+
// -c:v Specify the macOS hardware encoder, h264_videotoolbox.
|
|
311
|
+
// -allow_sw 1 Allow the use of the software encoder if the hardware encoder is occupied or unavailable.
|
|
312
|
+
// This allows us to scale when we get multiple streaming requests simultaneously that might consume all the available encode engines.
|
|
313
|
+
// -realtime 1 We prefer speed over quality - if the encoder has to make a choice, sacrifice one for the other.
|
|
314
|
+
// -coder cabac Use the cabac encoder for better video quality with the encoding profiles we use in HBUP.
|
|
315
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
316
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level on macOS when we're using hardware accelerated transcoding because
|
|
317
|
+
// the hardware encoder is particular about how to use levels. Setting it to 0 allows the encoder to decide for itself.
|
|
318
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
319
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
320
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final dimensions are a
|
|
321
|
+
// power of two.
|
|
322
|
+
// -b:v Average bitrate that's being requested by HomeKit. We can't use a quality constraint and allow for more optimization of the bitrate
|
|
323
|
+
// on Intel-based Macs due to hardware / API limitations.
|
|
324
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
325
|
+
// livestreamng exerience.
|
|
326
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
327
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
328
|
+
return [
|
|
329
|
+
"-c:v", "h264_videotoolbox",
|
|
330
|
+
"-allow_sw", "1",
|
|
331
|
+
"-realtime", "1",
|
|
332
|
+
"-coder", "cabac",
|
|
333
|
+
"-profile:v", this.getH264Profile(profile),
|
|
334
|
+
"-level:v", "0",
|
|
335
|
+
"-bf", "0",
|
|
336
|
+
"-noautoscale",
|
|
337
|
+
"-filter:v", "format=" + this.hwPixelFormat.join("|") + ", scale=-2:min(ih\\," + height.toString() + ")",
|
|
338
|
+
"-b:v", bitrate.toString() + "k",
|
|
339
|
+
"-g:v", (fps * idrInterval).toString(),
|
|
340
|
+
"-bufsize", (2 * bitrate).toString() + "k",
|
|
341
|
+
"-maxrate", adjustedMaxBitrate.toString() + "k"
|
|
342
|
+
];
|
|
343
|
+
break;
|
|
344
|
+
case "raspbian":
|
|
345
|
+
// h264_v4l2m2m is the preferred interface to the Raspberry Pi hardware encoder API. We use the following options:
|
|
346
|
+
//
|
|
347
|
+
// -c:v Specify the Raspberry Pi hardware encoder, h264_v4l2m2m.
|
|
348
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
349
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final dimensions are a
|
|
350
|
+
// power of two.
|
|
351
|
+
// -b:v Average bitrate that's being requested by HomeKit. We can't use a quality constraint and allow for more optimization of the bitrate
|
|
352
|
+
// due to v4l2m2m limitations.
|
|
353
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
354
|
+
// livestreamng exerience.
|
|
355
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
356
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
357
|
+
return [
|
|
358
|
+
"-c:v", "h264_v4l2m2m",
|
|
359
|
+
"-profile:v", this.getH264Profile(profile, true),
|
|
360
|
+
"-bf", "0",
|
|
361
|
+
"-noautoscale",
|
|
362
|
+
"-reset_timestamps", "1",
|
|
363
|
+
"-filter:v", "format=" + this.hwPixelFormat.join("|") + ", scale=-2:min(ih\\," + height.toString() + ")",
|
|
364
|
+
"-b:v", bitrate.toString() + "k",
|
|
365
|
+
"-g:v", (fps * idrInterval).toString(),
|
|
366
|
+
"-bufsize", (2 * bitrate).toString() + "k",
|
|
367
|
+
"-maxrate", adjustedMaxBitrate.toString() + "k"
|
|
368
|
+
];
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
// h264_qsv is the Intel Quick Sync Video hardware encoder API. We use the following options:
|
|
372
|
+
//
|
|
373
|
+
// -c:v Specify the macOS hardware encoder, h264_videotoolbox.
|
|
374
|
+
// -profile:v Use the H.264 profile that HomeKit is requesting when encoding.
|
|
375
|
+
// -level:v 0 We override what HomeKit requests for the H.264 profile level when we're using hardware accelerated transcoding because
|
|
376
|
+
// the hardware encoder will determine which levels to use. Setting it to 0 allows the encoder to decide for itself.
|
|
377
|
+
// -bf 0 Disable B-frames when encoding to increase compatibility against occasionally finicky HomeKit clients.
|
|
378
|
+
// -noautoscale Don't attempt to scale the video stream automatically.
|
|
379
|
+
// -init_hw_device Initialize our hardware accelerator and assign it a name to be used in the FFmpeg command line.
|
|
380
|
+
// -filter_hw_device Specify the hardware accelerator to be used with our video filter pipeline.
|
|
381
|
+
// -filter:v Set the pixel format and scale the video to the size we want while respecting aspect ratios and ensuring our final dimensions are a
|
|
382
|
+
// power of two.
|
|
383
|
+
// -g:v Set the group of pictures to the number of frames per second * the interval in between keyframes to ensure a solid
|
|
384
|
+
// livestreamng exerience.
|
|
385
|
+
// -bufsize size This is the decoder buffer size, which drives the variability / quality of the output bitrate.
|
|
386
|
+
// -maxrate bitrate The maximum bitrate tolerance used in concert with -bufsize to constrain the maximum bitrate permitted.
|
|
387
|
+
encoderOptions.push("-c:v", "h264_qsv", "-profile:v", this.getH264Profile(profile), "-level:v", "0", "-bf", "0", "-noautoscale", "-init_hw_device", "qsv=hw", "-filter_hw_device", "hw", "-filter:v", "vpp_qsv=format=same:w=min(iw\\, (iw / ih) * " + height.toString() + "):h=min(ih\\, " + height.toString() + ")", "-g:v", (fps * idrInterval).toString(), "-bufsize", (2 * bitrate).toString() + "k", "-maxrate", adjustedMaxBitrate.toString() + "k");
|
|
388
|
+
if (useSmartQuality) {
|
|
389
|
+
// -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,
|
|
390
|
+
// constrained by our maximum bitrate. This leverages a QSV-specific feature known as intelligent constant quality.
|
|
391
|
+
encoderOptions.push("-global_quality", "20");
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// -b:v Average bitrate that's being requested by HomeKit.
|
|
395
|
+
encoderOptions.push("-b:v", bitrate.toString() + "k");
|
|
396
|
+
}
|
|
397
|
+
return encoderOptions;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// If we aren't hardware accelerated, we default to libx264.
|
|
402
|
+
return this.defaultVideoEncoderOptions(width, height, fps, bitrate, profile, level, idrInterval, useSmartQuality);
|
|
403
|
+
}
|
|
404
|
+
// Use the host system information to determine which recording channel to use by default for HKSV.
|
|
405
|
+
get recordingDefaultChannel() {
|
|
406
|
+
switch (this.platform.hostSystem) {
|
|
407
|
+
case "raspbian":
|
|
408
|
+
// For constrained CPU environments like Raspberry Pi, we default to recording from the highest quality channel we can, that's at or below 1080p.
|
|
409
|
+
// That provides a reasonable default, while still allowing users who really want to, to be able to specify something else.
|
|
410
|
+
return this.protectCamera.findRtsp(1920, 1080, undefined, undefined, this.hostSystemMaxPixels)?.channel.name ?? undefined;
|
|
411
|
+
break;
|
|
412
|
+
default:
|
|
413
|
+
// We default to no preference for the default Protect camera channel.
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Return the maximum pixel count supported by a specific hardware encoder on the host system.
|
|
418
|
+
get hostSystemMaxPixels() {
|
|
419
|
+
if (this.protectCamera.hints.hardwareTranscoding) {
|
|
420
|
+
switch (this.platform.hostSystem) {
|
|
421
|
+
case "raspbian":
|
|
422
|
+
// For constrained environments like Raspberry Pi, when hardware transcoding has been selected for a camera, we limit the available source
|
|
423
|
+
// streams to no more than 1080p. In practice, that means that devices like the G4 Pro can't use their highest quality stream for
|
|
424
|
+
// transcoding due to the limitations of the Raspberry Pi GPU that cannot support higher pixel counts.
|
|
425
|
+
return 1920 * 1080;
|
|
426
|
+
break;
|
|
427
|
+
default:
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return 0;
|
|
432
|
+
}
|
|
433
|
+
// Translate HomeKit H.264 level information for FFmpeg.
|
|
434
|
+
getH264Level(level, numeric = false) {
|
|
435
|
+
switch (level) {
|
|
436
|
+
case 0 /* H264Level.LEVEL3_1 */:
|
|
437
|
+
return numeric ? "31" : "3.1";
|
|
438
|
+
break;
|
|
439
|
+
case 1 /* H264Level.LEVEL3_2 */:
|
|
440
|
+
return numeric ? "32" : "3.2";
|
|
441
|
+
break;
|
|
442
|
+
case 2 /* H264Level.LEVEL4_0 */:
|
|
443
|
+
return numeric ? "40" : "4.0";
|
|
444
|
+
break;
|
|
445
|
+
default:
|
|
446
|
+
return numeric ? "31" : "3.1";
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Translate HomeKit H.264 profile information for FFmpeg.
|
|
451
|
+
getH264Profile(profile, numeric = false) {
|
|
452
|
+
switch (profile) {
|
|
453
|
+
case 0 /* H264Profile.BASELINE */:
|
|
454
|
+
return numeric ? "66" : "baseline";
|
|
455
|
+
break;
|
|
456
|
+
case 2 /* H264Profile.HIGH */:
|
|
457
|
+
return numeric ? "100" : "high";
|
|
458
|
+
break;
|
|
459
|
+
case 1 /* H264Profile.MAIN */:
|
|
460
|
+
return numeric ? "77" : "main";
|
|
461
|
+
break;
|
|
462
|
+
default:
|
|
463
|
+
return numeric ? "77" : "main";
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=protect-ffmpeg-options.js.map
|