homebridge-ring-hksv 14.3.5 → 14.3.7
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 +9 -0
- package/config.schema.json +87 -1
- package/lib/camera-source.js +47 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,9 @@ Big thanks to Dustin and all upstream contributors. This fork reuses and extends
|
|
|
31
31
|
| `hksvPrebufferLengthMs` | HKSV prebuffer duration (minimum 4000ms) |
|
|
32
32
|
| `hksvFragmentLengthMs` | HKSV fragment duration target |
|
|
33
33
|
| `hksvMaxRecordingSeconds` | Optional safety cap for a recording session |
|
|
34
|
+
| `hksvVideoBitrateKbps` / `hksvVideoMaxBitrateKbps` / `hksvVideoBufferSizeKbps` | HKSV recording bitrate controls for improving fast-motion quality |
|
|
35
|
+
| `hksvVideoCrf` / `hksvVideoPreset` | Optional libx264 quality and CPU tuning controls |
|
|
36
|
+
| `hksvVideoKeyframeInterval` | HKSV recording keyframe interval |
|
|
34
37
|
| `homeKitAccessoryTag` | Appends a tag to accessory names and HomeKit IDs so the same Ring device can be exposed as a distinct HomeKit accessory for debugging/testing |
|
|
35
38
|
| `cameraVideoCodec` | Preferred H.264 encoder (`h264_videotoolbox` or `libx264`) |
|
|
36
39
|
| `hideDoorbellSwitch` / `hideCameraMotionSensor` / `hideCameraSirenSwitch` | Hides specific HomeKit-exposed services |
|
|
@@ -86,6 +89,12 @@ HKSV support is experimental and actively evolving. Behavior may vary by camera
|
|
|
86
89
|
I currently am able to run 3 cameras with HKSV enabled on a Homebridge instance ran on a M4 Mac Mini 32GB of RAM.
|
|
87
90
|
Please report your experience and setup details to help improve support.
|
|
88
91
|
|
|
92
|
+
For fast-motion pixelation or stuttering in HKSV recordings, try increasing
|
|
93
|
+
`hksvVideoBitrateKbps` first. On Apple Silicon Macs,
|
|
94
|
+
`cameraVideoCodec: "h264_videotoolbox"` can reduce CPU load by using hardware
|
|
95
|
+
encoding. A good starting point is 4000-6000 kbps target bitrate, 8000-12000
|
|
96
|
+
kbps max bitrate, and the default keyframe interval of 30.
|
|
97
|
+
|
|
89
98
|
### Minimum specifications for HKSV:
|
|
90
99
|
[TBD - will be added as more users test and report their setups]
|
|
91
100
|
|
package/config.schema.json
CHANGED
|
@@ -53,6 +53,86 @@
|
|
|
53
53
|
"maximum": 300,
|
|
54
54
|
"description": "Optional safety cap for a single HKSV recording stream duration."
|
|
55
55
|
},
|
|
56
|
+
"hksvVideoBitrateKbps": {
|
|
57
|
+
"title": "HKSV Video Bitrate (kbps)",
|
|
58
|
+
"type": "integer",
|
|
59
|
+
"minimum": 256,
|
|
60
|
+
"maximum": 12000,
|
|
61
|
+
"default": 3000,
|
|
62
|
+
"description": "Target video bitrate for HKSV recording transcodes. Increase this first if fast motion becomes blocky or pixelated. 4000-6000 is a good starting range for wired cameras."
|
|
63
|
+
},
|
|
64
|
+
"hksvVideoMaxBitrateKbps": {
|
|
65
|
+
"title": "HKSV Max Video Bitrate (kbps)",
|
|
66
|
+
"type": "integer",
|
|
67
|
+
"minimum": 256,
|
|
68
|
+
"maximum": 20000,
|
|
69
|
+
"description": "Maximum burst bitrate for HKSV recording transcodes. Defaults to 2x the target bitrate. A practical starting range is 8000-12000 when target bitrate is 4000-6000."
|
|
70
|
+
},
|
|
71
|
+
"hksvVideoBufferSizeKbps": {
|
|
72
|
+
"title": "HKSV Video Buffer Size (kbps)",
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"minimum": 256,
|
|
75
|
+
"maximum": 40000,
|
|
76
|
+
"description": "FFmpeg VBV buffer size for HKSV recording transcodes. Defaults to 2x the max video bitrate. Leave unset unless you are tuning bitrate stability."
|
|
77
|
+
},
|
|
78
|
+
"hksvVideoCrf": {
|
|
79
|
+
"title": "HKSV x264 Quality (CRF)",
|
|
80
|
+
"type": "integer",
|
|
81
|
+
"minimum": 18,
|
|
82
|
+
"maximum": 35,
|
|
83
|
+
"description": "Optional libx264 quality value. Lower values improve quality and use more bitrate/CPU. Try 20-23 if you stay on libx264. Ignored when using VideoToolbox."
|
|
84
|
+
},
|
|
85
|
+
"hksvVideoKeyframeInterval": {
|
|
86
|
+
"title": "HKSV Keyframe Interval",
|
|
87
|
+
"type": "integer",
|
|
88
|
+
"minimum": 5,
|
|
89
|
+
"maximum": 240,
|
|
90
|
+
"default": 30,
|
|
91
|
+
"description": "Maximum number of frames between video keyframes in HKSV recordings. 30 is the recommended default. Shorter intervals can improve fragmenting at the cost of bitrate."
|
|
92
|
+
},
|
|
93
|
+
"hksvVideoPreset": {
|
|
94
|
+
"title": "HKSV x264 Preset",
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "libx264 speed/quality preset for HKSV recordings. veryfast is the recommended default. Move toward fast or medium for quality, or superfast for lower CPU. Ignored when using VideoToolbox.",
|
|
97
|
+
"oneOf": [
|
|
98
|
+
{
|
|
99
|
+
"title": "ultrafast",
|
|
100
|
+
"enum": ["ultrafast"]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"title": "superfast",
|
|
104
|
+
"enum": ["superfast"]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"title": "veryfast",
|
|
108
|
+
"enum": ["veryfast"]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"title": "faster",
|
|
112
|
+
"enum": ["faster"]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"title": "fast",
|
|
116
|
+
"enum": ["fast"]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"title": "medium",
|
|
120
|
+
"enum": ["medium"]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"title": "slow",
|
|
124
|
+
"enum": ["slow"]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"title": "slower",
|
|
128
|
+
"enum": ["slower"]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"title": "veryslow",
|
|
132
|
+
"enum": ["veryslow"]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
},
|
|
56
136
|
"homeKitAccessoryTag": {
|
|
57
137
|
"title": "HomeKit Accessory Tag",
|
|
58
138
|
"type": "string",
|
|
@@ -61,7 +141,7 @@
|
|
|
61
141
|
"cameraVideoCodec": {
|
|
62
142
|
"title": "Camera Video Codec",
|
|
63
143
|
"type": "string",
|
|
64
|
-
"description": "Preferred
|
|
144
|
+
"description": "Preferred FFmpeg H.264 encoder for HKSV recording transcodes. On Apple Silicon or newer Intel Macs, h264_videotoolbox is the recommended first choice because it uses hardware acceleration.",
|
|
65
145
|
"oneOf": [
|
|
66
146
|
{
|
|
67
147
|
"title": "h264_videotoolbox",
|
|
@@ -215,6 +295,12 @@
|
|
|
215
295
|
"hksvPrebufferLengthMs",
|
|
216
296
|
"hksvFragmentLengthMs",
|
|
217
297
|
"hksvMaxRecordingSeconds",
|
|
298
|
+
"hksvVideoBitrateKbps",
|
|
299
|
+
"hksvVideoMaxBitrateKbps",
|
|
300
|
+
"hksvVideoBufferSizeKbps",
|
|
301
|
+
"hksvVideoCrf",
|
|
302
|
+
"hksvVideoKeyframeInterval",
|
|
303
|
+
"hksvVideoPreset",
|
|
218
304
|
"homeKitAccessoryTag",
|
|
219
305
|
"cameraVideoCodec",
|
|
220
306
|
"hideLightGroups",
|
package/lib/camera-source.js
CHANGED
|
@@ -23,6 +23,12 @@ function getSessionConfig(srtpOptions) {
|
|
|
23
23
|
profile: 1,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
function getIntegerConfigValue(value, defaultValue, min, max) {
|
|
27
|
+
if (!Number.isFinite(value)) {
|
|
28
|
+
return defaultValue;
|
|
29
|
+
}
|
|
30
|
+
return Math.min(Math.max(Math.round(value), min), max);
|
|
31
|
+
}
|
|
26
32
|
function* parseMp4Boxes(data) {
|
|
27
33
|
let offset = 0;
|
|
28
34
|
while (offset + 8 <= data.length) {
|
|
@@ -254,6 +260,7 @@ export class CameraSource {
|
|
|
254
260
|
sessions = {};
|
|
255
261
|
cachedSnapshot;
|
|
256
262
|
ringCamera;
|
|
263
|
+
config;
|
|
257
264
|
recordingActive = false;
|
|
258
265
|
recordingConfiguration;
|
|
259
266
|
closedRecordingStreams = new Set();
|
|
@@ -261,6 +268,7 @@ export class CameraSource {
|
|
|
261
268
|
activeRecordingSessions = new Map();
|
|
262
269
|
constructor(ringCamera, config) {
|
|
263
270
|
this.ringCamera = ringCamera;
|
|
271
|
+
this.config = config;
|
|
264
272
|
const enableHksv = config.enableHksv && !(config.disableHksvOnBattery && ringCamera.hasBattery);
|
|
265
273
|
const controllerOptions = {
|
|
266
274
|
cameraStreamCount: enableHksv ? 1 : 10,
|
|
@@ -358,6 +366,33 @@ export class CameraSource {
|
|
|
358
366
|
}
|
|
359
367
|
this.controller = new hap.CameraController(controllerOptions);
|
|
360
368
|
}
|
|
369
|
+
getHksvVideoArguments() {
|
|
370
|
+
const { cameraVideoCodec = 'libx264', hksvVideoCrf, hksvVideoPreset = 'veryfast', } = this.config, bitrateKbps = getIntegerConfigValue(this.config.hksvVideoBitrateKbps, 3000, 256, 12000), maxBitrateKbps = getIntegerConfigValue(this.config.hksvVideoMaxBitrateKbps, bitrateKbps * 2, bitrateKbps, 20000), bufferSizeKbps = getIntegerConfigValue(this.config.hksvVideoBufferSizeKbps, maxBitrateKbps * 2, maxBitrateKbps, 40000), keyframeInterval = getIntegerConfigValue(this.config.hksvVideoKeyframeInterval, 30, 5, 240), videoArguments = [
|
|
371
|
+
'-vcodec',
|
|
372
|
+
cameraVideoCodec,
|
|
373
|
+
'-b:v',
|
|
374
|
+
`${bitrateKbps}k`,
|
|
375
|
+
'-maxrate',
|
|
376
|
+
`${maxBitrateKbps}k`,
|
|
377
|
+
'-bufsize',
|
|
378
|
+
`${bufferSizeKbps}k`,
|
|
379
|
+
'-pix_fmt',
|
|
380
|
+
'yuv420p',
|
|
381
|
+
'-profile:v',
|
|
382
|
+
'baseline',
|
|
383
|
+
'-level:v',
|
|
384
|
+
'3.1',
|
|
385
|
+
'-g',
|
|
386
|
+
`${keyframeInterval}`,
|
|
387
|
+
];
|
|
388
|
+
if (cameraVideoCodec === 'libx264') {
|
|
389
|
+
videoArguments.push('-preset', hksvVideoPreset, '-tune', 'zerolatency', '-keyint_min', `${keyframeInterval}`, '-sc_threshold', '0');
|
|
390
|
+
if (Number.isFinite(hksvVideoCrf)) {
|
|
391
|
+
videoArguments.push('-crf', `${getIntegerConfigValue(hksvVideoCrf, 23, 18, 35)}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return videoArguments;
|
|
395
|
+
}
|
|
361
396
|
previousLoadSnapshotPromise;
|
|
362
397
|
async loadSnapshot(imageUuid) {
|
|
363
398
|
// cache a promise of the snapshot load
|
|
@@ -533,32 +568,21 @@ export class CameraSource {
|
|
|
533
568
|
this.activeRecordingSessions.set(streamId, closeSession);
|
|
534
569
|
let liveCall;
|
|
535
570
|
let keyFrameTimer;
|
|
571
|
+
let maxRecordingTimer;
|
|
536
572
|
try {
|
|
537
573
|
liveCall = await this.ringCamera.startLiveCall();
|
|
538
574
|
liveCall.onCallEnded.pipe(take(1)).subscribe(() => {
|
|
539
575
|
closeSession();
|
|
540
576
|
});
|
|
577
|
+
const maxRecordingSeconds = getIntegerConfigValue(this.config.hksvMaxRecordingSeconds, 0, 0, 300);
|
|
578
|
+
if (maxRecordingSeconds) {
|
|
579
|
+
maxRecordingTimer = setTimeout(() => {
|
|
580
|
+
logInfo(`HKSV recording stream reached max duration for ${this.ringCamera.name} (streamId=${streamId}, maxSeconds=${maxRecordingSeconds})`);
|
|
581
|
+
closeSession();
|
|
582
|
+
}, maxRecordingSeconds * 1000);
|
|
583
|
+
}
|
|
541
584
|
await liveCall.startTranscoding({
|
|
542
|
-
video:
|
|
543
|
-
'-vcodec',
|
|
544
|
-
'libx264',
|
|
545
|
-
'-preset',
|
|
546
|
-
'veryfast',
|
|
547
|
-
'-tune',
|
|
548
|
-
'zerolatency',
|
|
549
|
-
'-pix_fmt',
|
|
550
|
-
'yuv420p',
|
|
551
|
-
'-profile:v',
|
|
552
|
-
'baseline',
|
|
553
|
-
'-level:v',
|
|
554
|
-
'3.1',
|
|
555
|
-
'-g',
|
|
556
|
-
'30',
|
|
557
|
-
'-keyint_min',
|
|
558
|
-
'30',
|
|
559
|
-
'-sc_threshold',
|
|
560
|
-
'0',
|
|
561
|
-
],
|
|
585
|
+
video: this.getHksvVideoArguments(),
|
|
562
586
|
output: [
|
|
563
587
|
'-movflags',
|
|
564
588
|
'frag_keyframe+empty_moov+default_base_moof',
|
|
@@ -650,6 +674,9 @@ export class CameraSource {
|
|
|
650
674
|
if (keyFrameTimer) {
|
|
651
675
|
clearInterval(keyFrameTimer);
|
|
652
676
|
}
|
|
677
|
+
if (maxRecordingTimer) {
|
|
678
|
+
clearTimeout(maxRecordingTimer);
|
|
679
|
+
}
|
|
653
680
|
if (liveCall) {
|
|
654
681
|
liveCall.stop();
|
|
655
682
|
}
|
package/package.json
CHANGED