Skip to content

Commit 22a92ca

Browse files
committed
Tweaks to videoFilter handling
1 parent 994cdd0 commit 22a92ca

File tree

7 files changed

+63
-49
lines changed

7 files changed

+63
-49
lines changed

CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## v2.6.0 (2020-??-??)
5+
## v3.0.0 (2020-08-24)
66

77
### Changes
88

9-
- This plugin now includes experimental two-way audio support. Be aware that this feature is likely to be tweaked in the future, and a configuration that works now may need to be altered in the future.
9+
- This plugin now includes __experimental__ two-way audio support. Be aware that this feature is likely to be tweaked in the future, and a configuration that works now may need to be altered in the future.
1010
- Better detection of audio and video streams. There should be very few scenarios where `mapvideo` or `mapaudio` are needed anymore, as FFmpeg's stream auto-selection is now set up.
11+
- Default `videoFilter` can be disabled by including `none` in your comma-delimited list of filters.
12+
- Further reorganization of the config UI.
1113

1214
### Bug Fixes
1315

14-
- Correct handling of inactive camera timeouts. You should no longer see timeout messages after cleanly closing a camera stream.
16+
- Corrected handling of inactive camera timeouts. You should no longer see timeout messages after cleanly closing a camera stream.
17+
- Fixed `forceMax` not applying to resolution in some scenarios.
1518

1619
### Breaking Changes
1720

1821
- `additionalCommandline` has been replaced by `encoderOptions` to better reflect it's intended use.
22+
- `preserveRatio` has been removed and is now active as long as the default `videoFilter` list is active.
1923

2024
## v2.5.0 (2020-08-23)
2125

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,20 @@ Other users have been sharing configurations that work for them on our GitHub si
9191

9292
### Optional videoConfig Parameters
9393

94-
- `returnAudioTarget`: _(EXPERIMENTAL)_ The FFmpeg output command for directing audio back to a two-way capable camera.
94+
- `returnAudioTarget`: _(EXPERIMENTAL - WIP)_ The FFmpeg output command for directing audio back to a two-way capable camera. This feature is still in development and a configuration that works today may not work in the future.
9595
- `maxStreams`: The maximum number of streams that will be allowed at once to this camera. (Default: `2`)
9696
- `maxWidth`: The maximum width used for video streamed to HomeKit. If set to 0, the resolution of the source is used. If not set, will use any size HomeKit requests.
9797
- `maxHeight`: The maximum height used for video streamed to HomeKit. If set to 0, the resolution of the source is used. If not set, will use any size HomeKit requests.
9898
- `maxFPS`: The maximum frame rate used for video streamed to HomeKit. If set to 0, the framerate of the source is used. If not set, will use any frame rate HomeKit requests.
9999
- `maxBitrate`: The maximum bitrate used for video streamed to HomeKit, in kbit/s. If not set, will use any bitrate HomeKit requests.
100100
- `forceMax`: If set, the settings requested by HomeKit will be overridden with any 'maximum' values defined in this config. (Default: `false`)
101-
- `preserveRatio`: Preserves the aspect ratio of the source video. (Default: `false`)
102101
- `vcodec`: Set the codec used for encoding video sent to HomeKit, must be H.264-based. You can change to a hardware accelerated video codec with this option, if one is available. (Default: `libx264`)
103102
- `audio`: Enables audio streaming from camera. (Default: `false`)
104103
- `packetSize`: If audio or video is choppy try a smaller value, should be set to a multiple of 188. (Default: `1316`)
105104
- `mapvideo`: Selects the stream used for video. (Default: FFmpeg [automatically selects](https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection) a video stream)
106105
- `mapaudio`: Selects the stream used for audio. (Default: FFmpeg [automatically selects](https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection) an audio stream)
107-
- `videoFilter`: Allows additional video filter options to be passed to FFmpeg. If set to 'none', all video filters are disabled.
108-
- `encoderParameters`: Options to be passed to the video encoder. (Default: `-preset ultrafast -tune zerolatency` if using libx264)
106+
- `videoFilter`: Comma-delimited list of additional video filters for FFmpeg to run on the video. If 'none' is included, the default video filters are disabled.
107+
- `encoderOptions`: Options to be passed to the video encoder. (Default: `-preset ultrafast -tune zerolatency` if using libx264)
109108
- `debug`: Includes debugging output from the main FFmpeg process in the Homebridge log. (Default: `false`)
110109
- `debugReturn`: Includes debugging output from the FFmpeg used for return audio in the Homebridge log. (Default: `false`)
111110

config.schema.json

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@
141141
"description": "If your camera also provides a URL for a still image, that can be defined here with the same syntax as 'source'. If not set, the plugin will grab one frame from 'source'."
142142
},
143143
"returnAudioTarget": {
144-
"title": "Return Audio Target (EXPERIMENTAL)",
144+
"title": "Two-way Audio Target (EXPERIMENTAL - WIP)",
145145
"type": "string",
146-
"description": "The FFmpeg output command for directing audio back to a two-way capable camera."
146+
"description": "The FFmpeg output command for directing audio back to a two-way capable camera. This feature is still in development and a configuration that works today may not work in the future."
147147
},
148148
"maxStreams": {
149149
"title": "Maximum Concurrent Streams",
@@ -215,11 +215,11 @@
215215
"description": "If audio or video is choppy try a smaller value."
216216
},
217217
"videoFilter": {
218-
"title": "Additional Video Filter Options",
218+
"title": "Additional Video Filters",
219219
"type": "string",
220-
"description": "Allows additional video filter options to be passed to FFmpeg. If set to 'none', all video filters are disabled."
220+
"description": "Comma-delimited list of additional video filters for FFmpeg to run on the video. If 'none' is included, the default video filters are disabled."
221221
},
222-
"encoderParameters": {
222+
"encoderOptions": {
223223
"title": "Encoder Options",
224224
"type": "string",
225225
"placeholder": "-preset ultrafast -tune zerolatency",
@@ -241,14 +241,14 @@
241241
"description": "Enables audio streaming from camera."
242242
},
243243
"debug": {
244-
"title": "Debug Logging",
244+
"title": "FFmpeg Debug Logging",
245245
"type": "boolean",
246246
"description": "Includes debugging output from the main FFmpeg process in the Homebridge log."
247247
},
248248
"debugReturn": {
249-
"title": "Return Audio Debug Logging",
249+
"title": "Two-way FFmpeg Debug Logging",
250250
"type": "boolean",
251-
"description": "Includes debugging output from the FFmpeg used for return audio in the Homebridge log."
251+
"description": "Includes debugging output from the FFmpeg process used for two-way audio in the Homebridge log."
252252
}
253253
}
254254
}
@@ -293,14 +293,13 @@
293293
"expandable": true,
294294
"expanded": false,
295295
"items": [
296+
"cameras[].unbridge",
297+
"cameras[].videoConfig.maxStreams",
296298
"cameras[].videoConfig.maxWidth",
297299
"cameras[].videoConfig.maxHeight",
298300
"cameras[].videoConfig.maxFPS",
299301
"cameras[].videoConfig.maxBitrate",
300-
"cameras[].videoConfig.forceMax",
301-
"cameras[].videoConfig.preserveRatio",
302-
"cameras[].videoConfig.videoFilter",
303-
"cameras[].videoConfig.additionalCommandline"
302+
"cameras[].videoConfig.forceMax"
304303
]
305304
},
306305
{
@@ -310,15 +309,24 @@
310309
"expandable": true,
311310
"expanded": false,
312311
"items": [
313-
"cameras[].videoConfig.returnAudioTarget",
312+
{
313+
"key": "cameras[]",
314+
"type": "section",
315+
"title": "EXPERIMENTAL - WIP",
316+
"expandable": true,
317+
"expanded": false,
318+
"items": [
319+
"cameras[].videoConfig.returnAudioTarget",
320+
"cameras[].videoConfig.debugReturn"
321+
]
322+
},
314323
"cameras[].videoConfig.vcodec",
324+
"cameras[].videoConfig.packetSize",
315325
"cameras[].videoConfig.mapvideo",
316326
"cameras[].videoConfig.mapaudio",
317-
"cameras[].videoConfig.maxStreams",
318-
"cameras[].videoConfig.packetSize",
319-
"cameras[].unbridge",
320-
"cameras[].videoConfig.debug",
321-
"cameras[].videoConfig.debugReturn"
327+
"cameras[].videoConfig.videoFilter",
328+
"cameras[].videoConfig.encoderOptions",
329+
"cameras[].videoConfig.debug"
322330
]
323331
},
324332
{

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"displayName": "Homebridge Camera FFmpeg",
33
"name": "homebridge-camera-ffmpeg",
4-
"version": "2.5.0",
4+
"version": "3.0.0",
55
"description": "Homebridge Plugin Providing FFmpeg-based Camera Support",
66
"main": "dist/index.js",
77
"license": "ISC",

src/configTypes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@ export type VideoConfig = {
3636
maxFPS: number;
3737
maxBitrate: number;
3838
forceMax: boolean;
39-
preserveRatio: boolean;
4039
vcodec: string;
4140
packetSize: number;
4241
videoFilter: string;
43-
encoderParameters: string;
42+
encoderOptions: string;
4443
mapvideo: string;
4544
mapaudio: string;
4645
audio: boolean;

src/streamingDelegate.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ export class StreamingDelegate implements CameraStreamingDelegate {
128128
this.controller = new hap.CameraController(options);
129129
}
130130

131-
private determineResolution(request: SnapshotRequest | VideoInfo, isSnapshot = false): ResolutionInfo {
132-
let width = Math.floor(request.width / 2) * 2;
133-
let height = Math.floor(request.height / 2) * 2;
131+
private determineResolution(request: SnapshotRequest | VideoInfo, isSnapshot: boolean): ResolutionInfo {
132+
let width = request.width;
133+
let height = request.height;
134134
if (!isSnapshot) {
135135
if ((this.videoConfig.forceMax && this.videoConfig.maxWidth) ||
136136
(request.width > this.videoConfig.maxWidth)) {
@@ -142,26 +142,29 @@ export class StreamingDelegate implements CameraStreamingDelegate {
142142
}
143143
}
144144

145-
const vf: Array<string> = [];
146-
if (this.videoConfig.videoFilter !== 'none') {
147-
if (this.videoConfig.videoFilter) {
148-
vf.push(this.videoConfig.videoFilter);
149-
}
145+
const filters: Array<string> = this.videoConfig.videoFilter?.split(',') || [];
146+
const noneFilter = filters.indexOf('none');
147+
if (noneFilter >= 0) {
148+
filters.splice(noneFilter, 1);
149+
}
150+
if (noneFilter < 0) {
150151
if (width > 0 || height > 0) {
151-
vf.push('scale=' + (width > 0 ? '\'min(' + width + ',iw)\'' : 'iw') + ':' +
152+
filters.push('scale=' + (width > 0 ? '\'min(' + width + ',iw)\'' : 'iw') + ':' +
152153
(height > 0 ? '\'min(' + height + ',ih)\'' : 'ih') +
153-
(this.videoConfig.preserveRatio ? ':force_original_aspect_ratio=decrease' : ''));
154-
if (this.videoConfig.preserveRatio) {
155-
vf.push('scale=trunc(iw/2)*2:trunc(ih/2)*2'); // Force to fit encoder restrictions
156-
}
154+
':force_original_aspect_ratio=decrease');
155+
filters.push('scale=trunc(iw/2)*2:trunc(ih/2)*2'); // Force to fit encoder restrictions
157156
}
158157
}
159158

160-
return {width: width, height: height, videoFilter: vf.join(',')};
159+
return {
160+
width: width,
161+
height: height,
162+
videoFilter: filters.join(',')
163+
};
161164
}
162165

163166
handleSnapshotRequest(request: SnapshotRequest, callback: SnapshotRequestCallback): void {
164-
const resolution = this.determineResolution(request);
167+
const resolution = this.determineResolution(request, true);
165168

166169
this.log.debug('Snapshot requested: ' + request.width + ' x ' + request.height, this.cameraName, this.videoConfig.debug);
167170
this.log.debug('Sending snapshot: ' + (resolution.width > 0 ? resolution.width : 'native') + ' x ' +
@@ -277,12 +280,12 @@ export class StreamingDelegate implements CameraStreamingDelegate {
277280
const sessionInfo = this.pendingSessions[request.sessionID];
278281
const vcodec = this.videoConfig.vcodec || 'libx264';
279282
const mtu = this.videoConfig.packetSize || 1316; // request.video.mtu is not used
280-
let encoderParameters = this.videoConfig.encoderParameters;
281-
if (!encoderParameters && vcodec === 'libx264') {
282-
encoderParameters = '-preset ultrafast -tune zerolatency';
283+
let encoderOptions = this.videoConfig.encoderOptions;
284+
if (!encoderOptions && vcodec === 'libx264') {
285+
encoderOptions = '-preset ultrafast -tune zerolatency';
283286
}
284287

285-
const resolution = this.determineResolution(request.video, this.videoConfig.forceMax);
288+
const resolution = this.determineResolution(request.video, false);
286289

287290
let fps = (this.videoConfig.forceMax && this.videoConfig.maxFPS) ||
288291
(request.video.fps > this.videoConfig.maxFPS) ?
@@ -314,7 +317,7 @@ export class StreamingDelegate implements CameraStreamingDelegate {
314317
' -color_range mpeg' +
315318
(fps > 0 ? ' -r ' + fps : '') +
316319
' -f rawvideo' +
317-
(encoderParameters ? ' ' + encoderParameters : '') +
320+
(encoderOptions ? ' ' + encoderOptions : '') +
318321
(resolution.videoFilter.length > 0 ? ' -filter:v ' + resolution.videoFilter : '') +
319322
(videoBitrate > 0 ? ' -b:v ' + videoBitrate + 'k' : '') +
320323
' -payload_type ' + request.video.pt;
@@ -399,6 +402,7 @@ export class StreamingDelegate implements CameraStreamingDelegate {
399402
'm=audio ' + sessionInfo.audioReturnPort + ' RTP/AVP 110\r\n' +
400403
'b=AS:24\r\n' +
401404
'a=rtpmap:110 MPEG4-GENERIC/16000/1\r\n' +
405+
'a=rtcp-mux\r\n' + // FFmpeg ignores this, but might as well
402406
'a=fmtp:110 ' +
403407
'profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; ' +
404408
'config=F8F0212C00BC00\r\n' +

0 commit comments

Comments
 (0)