Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ipcamera] Improve FFmpeg motion detection to support wider FPS range #11067

Merged
merged 15 commits into from
Aug 1, 2021
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -65,105 +65,105 @@ private void processEvent(String content) {
String action = content.substring(startIndex, endIndex);
switch (code) {
case "VideoMotion":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
}
break;
case "TakenAwayDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN);
}
break;
case "LeftDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT);
}
break;
case "SmartMotionVehicle":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_CAR_ALARM);
}
break;
case "SmartMotionHuman":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_HUMAN_ALARM);
}
break;
case "CrossLineDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
}
break;
case "AudioMutation":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.audioDetected();
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noAudioDetected();
}
break;
case "FaceDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED);
}
break;
case "ParkingDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.ON);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.OFF);
}
break;
case "CrossRegionDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
}
break;
case "VideoLoss":
case "VideoBlind":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
}
break;
case "VideoAbnormalDetection":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
}
break;
case "VideoUnFocus":
if (action.equals("Start")) {
if ("Start".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
}
break;
case "AlarmLocal":
if (action.equals("Start")) {
if ("Start".equals(action)) {
if (content.contains("index=0")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON);
}
} else if (action.equals("Stop")) {
} else if ("Stop".equals(action)) {
if (content.contains("index=0")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,29 +127,46 @@ public void run() {
BufferedReader bufferedReader = new BufferedReader(errorStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
logger.debug("{}", line);
if (format.equals(FFmpegFormat.RTSP_ALARMS)) {
logger.debug("{}", line);
if (line.contains("lavfi.")) {
if (countOfMotions == 4) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else {
// When the number of pixels that change are below the noise floor we need to look
// across frames to confirm it is motion and not noise.
if (countOfMotions < 10) {// Stop increasing otherwise it will take too long to go OFF.
countOfMotions++;
}
if (countOfMotions > 9) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else if (countOfMotions > 4 && ipCameraHandler.motionThreshold.intValue() > 10) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else if (countOfMotions > 3 && ipCameraHandler.motionThreshold.intValue() > 15) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else if (countOfMotions > 2 && ipCameraHandler.motionThreshold.intValue() > 30) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else if (countOfMotions > 0 && ipCameraHandler.motionThreshold.intValue() > 89) {
ipCameraHandler.motionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
countOfMotions = 4;// Used to debounce the Alarm.
}
} else if (line.contains("speed=")) {
if (countOfMotions > 0) {
countOfMotions--;
countOfMotions--;
if (ipCameraHandler.motionThreshold.intValue() > 89) {
countOfMotions--;
}
if (ipCameraHandler.motionThreshold.intValue() > 10) {
countOfMotions -= 2;
} else {
countOfMotions -= 4;
}
if (countOfMotions <= 0) {
ipCameraHandler.noMotionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
countOfMotions = 0;
}
}
} else if (line.contains("silence_start")) {
ipCameraHandler.noAudioDetected();
} else if (line.contains("silence_end")) {
ipCameraHandler.audioDetected();
}
} else {
logger.debug("{}", line);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.openhab.binding.ipcamera.internal;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -42,6 +43,8 @@ public static enum FFmpegFormat {
SNAPSHOT
}

public static final BigDecimal BIG_DECIMAL_SCALE_MOTION = new BigDecimal(5000);

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");
public static final String GENERIC_THING = "generic";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ public IpCameraHandlerFactory(final @Reference NetworkAddressService networkAddr

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
if (SUPPORTED_THING_TYPES.contains(thingTypeUID) || GROUP_SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return true;
}
return false;
return (SUPPORTED_THING_TYPES.contains(thingTypeUID) || GROUP_SUPPORTED_THING_TYPES.contains(thingTypeUID));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void processAuth(String authenticate, String httpMethod, String requestUR
}

String stale = Helper.searchString(authenticate, "stale=\"");
if (stale.equalsIgnoreCase("true")) {
if ("true".equalsIgnoreCase(stale)) {
logger.debug("Camera reported stale=true which normally means the NONCE has expired.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void handlerAdded(@Nullable ChannelHandlerContext ctx) {
}

private String resolveIndexToPath(String uri) {
if (!uri.substring(1, 2).equals("i")) {
if (!"i".equals(uri.substring(1, 2))) {
return ipCameraGroupHandler.getOutputFolder(Integer.parseInt(uri.substring(1, 2)));
}
return "notFound";
Expand All @@ -87,7 +87,7 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
HttpRequest httpRequest = (HttpRequest) msg;
String requestIP = "("
+ ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress() + ")";
if (!whiteList.contains(requestIP) && !whiteList.equals("DISABLE")) {
if (!whiteList.contains(requestIP) && !"DISABLE".equals(whiteList)) {
logger.warn("The request made from {} was not in the whitelist and will be ignored.", requestIP);
return;
} else if (HttpMethod.GET.equals(httpRequest.method())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
try {
if (msg instanceof HttpRequest) {
HttpRequest httpRequest = (HttpRequest) msg;
if (!whiteList.equals("DISABLE")) {
if (!"DISABLE".equals(whiteList)) {
String requestIP = "("
+ ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress() + ")";
if (!whiteList.contains(requestIP)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
Expand Down Expand Up @@ -189,7 +190,7 @@ public class IpCameraHandler extends BaseThingHandler {
private boolean isOnline = false; // Used so only 1 error is logged when a network issue occurs.
private boolean firstAudioAlarm = false;
private boolean firstMotionAlarm = false;
public Double motionThreshold = 0.0016;
public BigDecimal motionThreshold = BigDecimal.ZERO;
public int audioThreshold = 35;
@SuppressWarnings("unused")
private @Nullable StreamServerHandler streamServerHandler;
Expand Down Expand Up @@ -1035,15 +1036,15 @@ public void setupFfmpegFormat(FFmpegFormat format) {
String usersMotionOptions = cameraConfig.getMotionOptions();
if (usersMotionOptions.startsWith("-")) {
// Need to put the users custom options first in the chain before the motion is detected
filterOptions += " " + usersMotionOptions + ",select='gte(scene," + motionThreshold
+ ")',metadata=print";
filterOptions += " " + usersMotionOptions + ",select='gte(scene,"
+ motionThreshold.divide(BIG_DECIMAL_SCALE_MOTION) + ")',metadata=print";
} else {
filterOptions = filterOptions + " " + usersMotionOptions + " -vf select='gte(scene,"
+ motionThreshold + ")',metadata=print";
+ motionThreshold.divide(BIG_DECIMAL_SCALE_MOTION) + ")',metadata=print";
}
} else if (motionAlarmEnabled) {
filterOptions = filterOptions
.concat(" -vf select='gte(scene," + motionThreshold + ")',metadata=print");
filterOptions = filterOptions.concat(" -vf select='gte(scene,"
+ motionThreshold.divide(BIG_DECIMAL_SCALE_MOTION) + ")',metadata=print");
}
ffmpegRtspHelper = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, input,
filterOptions, "-f null -", cameraConfig.getUser(), cameraConfig.getPassword());
Expand Down Expand Up @@ -1262,10 +1263,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
} else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
motionAlarmEnabled = false;
noMotionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else {
} else if (command instanceof PercentType) {
motionAlarmEnabled = true;
motionThreshold = Double.valueOf(command.toString());
motionThreshold = motionThreshold / 10000;
motionThreshold = ((PercentType) command).toBigDecimal();
}
setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
return;
Expand Down
Loading