diff --git a/bundles/org.openhab.binding.ipcamera/README.md b/bundles/org.openhab.binding.ipcamera/README.md
index ff25ddce0..cfdbe24d7 100644
--- a/bundles/org.openhab.binding.ipcamera/README.md
+++ b/bundles/org.openhab.binding.ipcamera/README.md
@@ -196,6 +196,7 @@ If you do not specify any of these, the binding will use the default which shoul
| `hlsOutOptions`| This gives you direct access to specify your own FFmpeg options to be used. Default: `-strict -2 -f lavfi -i aevalsrc=0 -acodec aac -vcodec copy -hls_flags delete_segments -hls_time 2 -hls_list_size 4` |
| `gifOutOptions`| This gives you direct access to specify your own FFmpeg options to be used for animated GIF files. Default: `-r 2 -filter_complex scale=-2:360:flags=lanczos,setpts=0.5*PTS,split[o1][o2];[o1]palettegen[p];[o2]fifo[o3];[o3][p]paletteuse` |
| `mjpegOptions` | Allows you to change the settings for creating a MJPEG stream from RTSP using FFmpeg. Possible reasons to change this would be to rotate or re-scale the picture from the camera, change the JPG compression for better quality or the FPS rate. |
+| `snapshotOptions` | Specify your own FFmpeg options to be used when creating snapshots from RTSP. Default: `-an -vsync vfr -q:v 2 -update 1` |
| `motionOptions` | This gives access to the FFmpeg parameters for detecting motion alarms from a RTSP stream. One possible use for this is to use the CROP feature to ignore any trees that move in the wind or a timecode stamp. Crop will not remove the trees from your picture, it only ignores the movement of the tree. |
| `gifPreroll`| Store this many snapshots from BEFORE you trigger a GIF creation. Default: `0` will not use snapshots and will instead use a realtime stream from the ffmpegInput URL |
| `ipWhitelist`| Enter any IPs inside brackets that you wish to allow to access the video stream. `DISABLE` the default value will turn this feature off. Example: `ipWhitelist="(127.0.0.1)(192.168.0.99)"` |
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java
index 687453ec5..3cedaff61 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
@@ -206,19 +205,6 @@ public class AmcrestHandler extends ChannelDuplexHandler {
ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=0");
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString());
- ipCameraHandler.motionThreshold = ipCameraHandler.motionThreshold / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/CameraConfig.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/CameraConfig.java
index 313ed8cae..b3c2722a6 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/CameraConfig.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/CameraConfig.java
@@ -45,6 +45,7 @@ public class CameraConfig {
private String gifOutOptions = "";
private String mp4OutOptions = "";
private String mjpegOptions = "";
+ private String snapshotOptions = "";
private String motionOptions = "";
private boolean ptzContinuous;
private int gifPreroll;
@@ -61,6 +62,10 @@ public class CameraConfig {
return mjpegOptions;
}
+ public String getSnapshotOptions() {
+ return snapshotOptions;
+ }
+
public String getMotionOptions() {
return motionOptions;
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java
index a5918be9b..35c418bb9 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
@@ -252,18 +251,6 @@ public class DahuaHandler extends ChannelDuplexHandler {
ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=0");
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString()) / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java
index 0208037f8..777f00124 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java
@@ -19,9 +19,7 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
-import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.binding.ThingHandler;
@@ -99,19 +97,6 @@ public class DoorBirdHandler extends ChannelDuplexHandler {
ipCameraHandler.sendHttpGET("/bha-api/light-on.cgi");
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString());
- ipCameraHandler.motionThreshold = ipCameraHandler.motionThreshold / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java
index 6f61ee15a..6e353933a 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java
@@ -75,23 +75,20 @@ public class Ffmpeg {
commandArrayList.add(0, ffmpegLocation);
}
- public void setKeepAlive(int seconds) {
- if (seconds == -1) {
- keepAlive = -1;
- } else {// We now poll every 8 seconds due to mjpeg stream requirement.
- keepAlive = 8; // 64 seconds approx.
+ public void setKeepAlive(int numberOfEightSeconds) {
+ // We poll every 8 seconds due to mjpeg stream requirement.
+ if (keepAlive == -1 && numberOfEightSeconds > 1) {
+ return;// When set to -1 this will not auto turn off stream.
}
+ keepAlive = numberOfEightSeconds;
}
public void checkKeepAlive() {
if (keepAlive <= -1) {
return;
- } else if (keepAlive == 0) {
+ } else if (--keepAlive == 0) {
stopConverting();
- } else {
- keepAlive--;
}
- return;
}
private class IpCameraFfmpegThread extends Thread {
@@ -119,8 +116,9 @@ public class Ffmpeg {
public void run() {
try {
process = Runtime.getRuntime().exec(commandArrayList.toArray(new String[commandArrayList.size()]));
- if (process != null) {
- InputStream errorStream = process.getErrorStream();
+ Process localProcess = process;
+ if (localProcess != null) {
+ InputStream errorStream = localProcess.getErrorStream();
InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
BufferedReader bufferedReader = new BufferedReader(errorStreamReader);
String line = null;
@@ -189,10 +187,11 @@ public class Ffmpeg {
public void stopConverting() {
if (ipCameraFfmpegThread.isAlive()) {
- logger.debug("Stopping ffmpeg {} now", format);
- running = false;
- if (process != null) {
- process.destroyForcibly();
+ logger.debug("Stopping ffmpeg {} now when keepalive is:{}", format, keepAlive);
+ Process localProcess = process;
+ if (localProcess != null) {
+ localProcess.destroyForcibly();
+ running = false;
}
if (format.equals(FFmpegFormat.HLS)) {
if (keepAlive == -1) {
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java
index 6766a7fbf..dd0dc5704 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
@@ -215,19 +214,6 @@ public class FoscamHandler extends ChannelDuplexHandler {
+ username + "&pwd=" + password);
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString());
- ipCameraHandler.motionThreshold = ipCameraHandler.motionThreshold / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java
index 8521f97e4..74580916f 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java
@@ -20,9 +20,7 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
-import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
@@ -441,19 +439,6 @@ public class HikvisionHandler extends ChannelDuplexHandler {
"\r\n low\r\n\r\n");
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString());
- ipCameraHandler.motionThreshold = ipCameraHandler.motionThreshold / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java
index 80a7b8bbd..86a1d583b 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java
@@ -19,9 +19,7 @@ import java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
-import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
@@ -200,19 +198,6 @@ public class InstarHandler extends ChannelDuplexHandler {
ipCameraHandler.sendHttpGET("/param.cgi?cmd=setioattr&-io_enable=0");
}
return;
- case CHANNEL_FFMPEG_MOTION_CONTROL:
- if (OnOffType.ON.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = true;
- } else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
- ipCameraHandler.motionAlarmEnabled = false;
- ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
- } else {
- ipCameraHandler.motionAlarmEnabled = true;
- ipCameraHandler.motionThreshold = Double.valueOf(command.toString());
- ipCameraHandler.motionThreshold = ipCameraHandler.motionThreshold / 10000;
- }
- ipCameraHandler.setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
- return;
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraActions.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraActions.java
index 1ea92adaf..d042d93bc 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraActions.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraActions.java
@@ -45,15 +45,14 @@ public class IpCameraActions implements ThingActions {
return handler;
}
- @RuleAction(label = "record an MP4", description = "Record MP4 to a set filename if given, or if filename is null to ipcamera.mp4")
+ @RuleAction(label = "record a MP4", description = "Record MP4 to a set filename if given, or if filename is null to ipcamera.mp4")
public void recordMP4(
@ActionInput(name = "filename", label = "Filename", description = "Name that the recording will have once created, don't include the .mp4.") @Nullable String filename,
@ActionInput(name = "secondsToRecord", label = "Seconds to Record", description = "Enter a number of how many seconds to record.") int secondsToRecord) {
logger.debug("Recording {}.mp4 for {} seconds.", filename, secondsToRecord);
- if (filename == null && handler != null) {
- handler.recordMp4("ipcamera", secondsToRecord);
- } else if (handler != null && filename != null) {
- handler.recordMp4(filename, secondsToRecord);
+ IpCameraHandler localHandler = handler;
+ if (localHandler != null) {
+ localHandler.recordMp4(filename != null ? filename : "ipcamera", secondsToRecord);
}
}
@@ -66,10 +65,9 @@ public class IpCameraActions implements ThingActions {
@ActionInput(name = "filename", label = "Filename", description = "Name that the recording will have once created, don't include the .mp4.") @Nullable String filename,
@ActionInput(name = "secondsToRecord", label = "Seconds to Record", description = "Enter a number of how many seconds to record.") int secondsToRecord) {
logger.debug("Recording {}.gif for {} seconds.", filename, secondsToRecord);
- if (filename == null && handler != null) {
- handler.recordGif("ipcamera", secondsToRecord);
- } else if (handler != null && filename != null) {
- handler.recordGif(filename, secondsToRecord);
+ IpCameraHandler localHandler = handler;
+ if (localHandler != null) {
+ localHandler.recordGif(filename != null ? filename : "ipcamera", secondsToRecord);
}
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java
index 676c8c1b0..ee272259e 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java
@@ -71,37 +71,7 @@ public class IpCameraBindingConstants {
// List of all Thing Config items
public static final String CONFIG_IPADDRESS = "ipAddress";
- public static final String CONFIG_PORT = "port";
public static final String CONFIG_ONVIF_PORT = "onvifPort";
- public static final String CONFIG_SERVER_PORT = "serverPort";
- public static final String CONFIG_USERNAME = "username";
- public static final String CONFIG_PASSWORD = "password";
- public static final String CONFIG_ONVIF_PROFILE_NUMBER = "onvifMediaProfile";
- public static final String CONFIG_POLL_TIME = "pollTime";
- public static final String CONFIG_FFMPEG_INPUT = "ffmpegInput";
- public static final String CONFIG_SNAPSHOT_URL_OVERRIDE = "snapshotUrl";
- public static final String CONFIG_MJPEG_URL = "mjpegUrl";
- public static final String CONFIG_FFMPEG_MOTION_INPUT = "alarmInputUrl";
- public static final String CONFIG_MOTION_URL_OVERRIDE = "customMotionAlarmUrl";
- public static final String CONFIG_AUDIO_URL_OVERRIDE = "customAudioAlarmUrl";
- public static final String CONFIG_IMAGE_UPDATE_WHEN = "updateImageWhen";
- public static final String CONFIG_NVR_CHANNEL = "nvrChannel";
- public static final String CONFIG_IP_WHITELIST = "ipWhitelist";
- public static final String CONFIG_FFMPEG_LOCATION = "ffmpegLocation";
- public static final String CONFIG_FFMPEG_OUTPUT = "ffmpegOutput";
- public static final String CONFIG_FFMPEG_HLS_OUT_ARGUMENTS = "hlsOutOptions";
- public static final String CONFIG_FFMPEG_GIF_OUT_ARGUMENTS = "gifOutOptions";
- public static final String CONFIG_FFMPEG_MP4_OUT_ARGUMENTS = "mp4OutOptions";
- public static final String CONFIG_FFMPEG_MJPEG_ARGUMENTS = "mjpegOptions";
- public static final String CONFIG_FFMPEG_MOTION_ARGUMENTS = "motionOptions";
- public static final String CONFIG_PTZ_CONTINUOUS = "ptzContinuous";
- public static final String CONFIG_GIF_PREROLL = "gifPreroll";
- // group thing configs
- public static final String CONFIG_FIRST_CAM = "firstCamera";
- public static final String CONFIG_SECOND_CAM = "secondCamera";
- public static final String CONFIG_THIRD_CAM = "thirdCamera";
- public static final String CONFIG_FORTH_CAM = "forthCamera";
- public static final String CONFIG_MOTION_CHANGES_ORDER = "motionChangesOrder";
// List of all Channel ids
public static final String CHANNEL_POLL_IMAGE = "pollImage";
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDynamicStateDescriptionProvider.java
new file mode 100644
index 000000000..3f259c970
--- /dev/null
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDynamicStateDescriptionProvider.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.ipcamera.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
+import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
+import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link IpCameraDynamicStateDescriptionProvider} Allows the dynamic updating of the ONVIF
+ * preset names and tokens that can change at any time.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@Component(service = { DynamicStateDescriptionProvider.class, IpCameraDynamicStateDescriptionProvider.class })
+@NonNullByDefault
+public class IpCameraDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
+ @Activate
+ public IpCameraDynamicStateDescriptionProvider(
+ final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
+ this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
+ }
+}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraHandlerFactory.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraHandlerFactory.java
index 73f7378e4..bbd032acd 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraHandlerFactory.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraHandlerFactory.java
@@ -40,10 +40,13 @@ import org.osgi.service.component.annotations.Reference;
public class IpCameraHandlerFactory extends BaseThingHandlerFactory {
private final @Nullable String openhabIpAddress;
private final GroupTracker groupTracker = new GroupTracker();
+ private final IpCameraDynamicStateDescriptionProvider stateDescriptionProvider;
@Activate
- public IpCameraHandlerFactory(final @Reference NetworkAddressService networkAddressService) {
+ public IpCameraHandlerFactory(final @Reference NetworkAddressService networkAddressService,
+ final @Reference IpCameraDynamicStateDescriptionProvider stateDescriptionProvider) {
openhabIpAddress = networkAddressService.getPrimaryIpv4HostAddress();
+ this.stateDescriptionProvider = stateDescriptionProvider;
}
@Override
@@ -59,7 +62,7 @@ public class IpCameraHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
- return new IpCameraHandler(thing, openhabIpAddress, groupTracker);
+ return new IpCameraHandler(thing, openhabIpAddress, groupTracker, stateDescriptionProvider);
} else if (GROUP_SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new IpCameraGroupHandler(thing, openhabIpAddress, groupTracker);
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/StreamServerHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/StreamServerHandler.java
index 194827f8e..65ecd9e1f 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/StreamServerHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/StreamServerHandler.java
@@ -17,6 +17,7 @@ import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -96,20 +97,19 @@ public class StreamServerHandler extends ChannelInboundHandlerAdapter {
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(httpRequest.uri());
switch (queryStringDecoder.path()) {
case "/ipcamera.m3u8":
- if (ipCameraHandler.ffmpegHLS != null) {
- if (!ipCameraHandler.ffmpegHLS.getIsAlive()) {
- if (ipCameraHandler.ffmpegHLS != null) {
- ipCameraHandler.ffmpegHLS.startConverting();
- }
- }
- } else {
+ Ffmpeg localFfmpeg = ipCameraHandler.ffmpegHLS;
+ if (localFfmpeg == null) {
ipCameraHandler.setupFfmpegFormat(FFmpegFormat.HLS);
+ } else if (!localFfmpeg.getIsAlive()) {
+ localFfmpeg.startConverting();
+ } else {
+ localFfmpeg.setKeepAlive(8);
+ sendFile(ctx, httpRequest.uri(), "application/x-mpegurl");
+ return;
}
- if (ipCameraHandler.ffmpegHLS != null) {
- ipCameraHandler.ffmpegHLS.setKeepAlive(8);
- }
+ // Allow files to be created, or you get old m3u8 from the last time this ran.
+ TimeUnit.MILLISECONDS.sleep(4500);
sendFile(ctx, httpRequest.uri(), "application/x-mpegurl");
- ctx.close();
return;
case "/ipcamera.mpd":
sendFile(ctx, httpRequest.uri(), "application/dash+xml");
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java
index 3420c65f3..6fc3f2e1e 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java
@@ -23,6 +23,7 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -361,9 +362,9 @@ public class IpCameraGroupHandler extends BaseThingHandler {
public void dispose() {
startStreamServer(false);
groupTracker.listOfGroupHandlers.remove(this);
- if (pollCameraGroupJob != null) {
- pollCameraGroupJob.cancel(true);
- pollCameraGroupJob = null;
+ Future> future = pollCameraGroupJob;
+ if (future != null) {
+ future.cancel(true);
}
cameraOrder.clear();
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java
index 2e16e0419..4994cfb3e 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java
@@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -53,6 +54,7 @@ import org.openhab.binding.ipcamera.internal.HttpOnlyHandler;
import org.openhab.binding.ipcamera.internal.InstarHandler;
import org.openhab.binding.ipcamera.internal.IpCameraActions;
import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
+import org.openhab.binding.ipcamera.internal.IpCameraDynamicStateDescriptionProvider;
import org.openhab.binding.ipcamera.internal.MyNettyAuthHandler;
import org.openhab.binding.ipcamera.internal.StreamServerHandler;
import org.openhab.binding.ipcamera.internal.onvif.OnvifConnection;
@@ -125,9 +127,10 @@ import io.netty.util.concurrent.GlobalEventExecutor;
@NonNullByDefault
public class IpCameraHandler extends BaseThingHandler {
public final Logger logger = LoggerFactory.getLogger(getClass());
+ public final IpCameraDynamicStateDescriptionProvider stateDescriptionProvider;
private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
private GroupTracker groupTracker;
- public CameraConfig cameraConfig;
+ public CameraConfig cameraConfig = new CameraConfig();
// ChannelGroup is thread safe
public final ChannelGroup mjpegChannelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@@ -391,9 +394,10 @@ public class IpCameraHandler extends BaseThingHandler {
}
}
- public IpCameraHandler(Thing thing, @Nullable String ipAddress, GroupTracker groupTracker) {
+ public IpCameraHandler(Thing thing, @Nullable String ipAddress, GroupTracker groupTracker,
+ IpCameraDynamicStateDescriptionProvider stateDescriptionProvider) {
super(thing);
- cameraConfig = getConfigAs(CameraConfig.class);
+ this.stateDescriptionProvider = stateDescriptionProvider;
if (ipAddress != null) {
hostIp = ipAddress;
} else {
@@ -759,16 +763,13 @@ public class IpCameraHandler extends BaseThingHandler {
mjpegChannelGroup.remove(ctx.channel());
if (mjpegChannelGroup.isEmpty()) {
logger.debug("All ipcamera.mjpeg streams have stopped.");
- if (mjpegUri.equals("ffmpeg")) {
- if (ffmpegMjpeg != null) {
- ffmpegMjpeg.stopConverting();
+ if (mjpegUri.equals("ffmpeg") || mjpegUri.isEmpty()) {
+ Ffmpeg localMjpeg = ffmpegMjpeg;
+ if (localMjpeg != null) {
+ localMjpeg.stopConverting();
}
- } else if (!mjpegUri.isEmpty()) {
- closeChannel(getTinyUrl(mjpegUri));
} else {
- if (ffmpegMjpeg != null) {
- ffmpegMjpeg.stopConverting();
- }
+ closeChannel(getTinyUrl(mjpegUri));
}
}
}
@@ -887,8 +888,6 @@ public class IpCameraHandler extends BaseThingHandler {
if (rtspUri.toLowerCase().contains("rtsp")) {
if (inputOptions.isEmpty()) {
inputOptions = "-rtsp_transport tcp";
- } else {
- inputOptions = inputOptions + " -rtsp_transport tcp";
}
}
@@ -909,8 +908,9 @@ public class IpCameraHandler extends BaseThingHandler {
cameraConfig.getPassword());
}
}
- if (ffmpegHLS != null) {
- ffmpegHLS.startConverting();
+ Ffmpeg localHLS = ffmpegHLS;
+ if (localHLS != null) {
+ localHLS.startConverting();
}
break;
case GIF:
@@ -934,8 +934,9 @@ public class IpCameraHandler extends BaseThingHandler {
if (cameraConfig.getGifPreroll() > 0) {
storeSnapshots();
}
- if (ffmpegGIF != null) {
- ffmpegGIF.startConverting();
+ Ffmpeg localGIF = ffmpegGIF;
+ if (localGIF != null) {
+ localGIF.startConverting();
if (gifHistory.isEmpty()) {
gifHistory = gifFilename;
} else if (!gifFilename.equals("ipcamera")) {
@@ -957,21 +958,25 @@ public class IpCameraHandler extends BaseThingHandler {
ffmpegRecord = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, rtspUri,
cameraConfig.getMp4OutOptions(), cameraConfig.getFfmpegOutput() + mp4Filename + ".mp4",
cameraConfig.getUser(), cameraConfig.getPassword());
- ffmpegRecord.startConverting();
- if (mp4History.isEmpty()) {
- mp4History = mp4Filename;
- } else if (!mp4Filename.equals("ipcamera")) {
- mp4History = mp4Filename + "," + mp4History;
- if (mp4HistoryLength > 49) {
- int endIndex = mp4History.lastIndexOf(",");
- mp4History = mp4History.substring(0, endIndex);
+ Ffmpeg localRecord = ffmpegRecord;
+ if (localRecord != null) {
+ localRecord.startConverting();
+ if (mp4History.isEmpty()) {
+ mp4History = mp4Filename;
+ } else if (!mp4Filename.equals("ipcamera")) {
+ mp4History = mp4Filename + "," + mp4History;
+ if (mp4HistoryLength > 49) {
+ int endIndex = mp4History.lastIndexOf(",");
+ mp4History = mp4History.substring(0, endIndex);
+ }
}
}
setChannelState(CHANNEL_MP4_HISTORY, new StringType(mp4History));
break;
case RTSP_ALARMS:
- if (ffmpegRtspHelper != null) {
- ffmpegRtspHelper.stopConverting();
+ Ffmpeg localAlarms = ffmpegRtspHelper;
+ if (localAlarms != null) {
+ localAlarms.stopConverting();
if (!audioAlarmEnabled && !motionAlarmEnabled) {
return;
}
@@ -996,40 +1001,45 @@ public class IpCameraHandler extends BaseThingHandler {
ffmpegRtspHelper = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, input,
filterOptions + cameraConfig.getMotionOptions(), outputOptions, cameraConfig.getUser(),
cameraConfig.getPassword());
- ffmpegRtspHelper.startConverting();
+ localAlarms = ffmpegRtspHelper;
+ if (localAlarms != null) {
+ localAlarms.startConverting();
+ }
break;
case MJPEG:
if (ffmpegMjpeg == null) {
if (inputOptions.isEmpty()) {
inputOptions = "-hide_banner -loglevel warning";
} else {
- inputOptions = inputOptions + " -hide_banner -loglevel warning";
+ inputOptions += " -hide_banner -loglevel warning";
}
ffmpegMjpeg = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, rtspUri,
cameraConfig.getMjpegOptions(),
"http://127.0.0.1:" + cameraConfig.getServerPort() + "/ipcamera.jpg",
cameraConfig.getUser(), cameraConfig.getPassword());
}
- if (ffmpegMjpeg != null) {
- ffmpegMjpeg.startConverting();
+ Ffmpeg localMjpeg = ffmpegMjpeg;
+ if (localMjpeg != null) {
+ localMjpeg.startConverting();
}
break;
case SNAPSHOT:
- // if mjpeg stream you can use ffmpeg -i input.h264 -codec:v copy -bsf:v mjpeg2jpeg output%03d.jpg
+ // if mjpeg stream you can use 'ffmpeg -i input -codec:v copy -bsf:v mjpeg2jpeg output.jpg'
if (ffmpegSnapshot == null) {
if (inputOptions.isEmpty()) {
// iFrames only
inputOptions = "-threads 1 -skip_frame nokey -hide_banner -loglevel warning";
} else {
- inputOptions = inputOptions + " -threads 1 -skip_frame nokey -hide_banner -loglevel warning";
+ inputOptions += " -threads 1 -skip_frame nokey -hide_banner -loglevel warning";
}
ffmpegSnapshot = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, rtspUri,
- "-an -vsync vfr -update 1",
+ cameraConfig.getSnapshotOptions(),
"http://127.0.0.1:" + cameraConfig.getServerPort() + "/snapshot.jpg",
cameraConfig.getUser(), cameraConfig.getPassword());
}
- if (ffmpegSnapshot != null) {
- ffmpegSnapshot.startConverting();
+ Ffmpeg localSnaps = ffmpegSnapshot;
+ if (localSnaps != null) {
+ localSnaps.startConverting();
}
break;
}
@@ -1185,7 +1195,7 @@ public class IpCameraHandler extends BaseThingHandler {
motionAlarmEnabled = true;
} else if (OnOffType.OFF.equals(command) || DecimalType.ZERO.equals(command)) {
motionAlarmEnabled = false;
- noMotionDetected(CHANNEL_MOTION_ALARM);
+ noMotionDetected(CHANNEL_FFMPEG_MOTION_ALARM);
} else {
motionAlarmEnabled = true;
motionThreshold = Double.valueOf(command.toString());
@@ -1194,14 +1204,22 @@ public class IpCameraHandler extends BaseThingHandler {
setupFfmpegFormat(FFmpegFormat.RTSP_ALARMS);
return;
case CHANNEL_START_STREAM:
+ Ffmpeg localHLS;
if (OnOffType.ON.equals(command)) {
- setupFfmpegFormat(FFmpegFormat.HLS);
- if (ffmpegHLS != null) {
- ffmpegHLS.setKeepAlive(-1);// will keep running till manually stopped.
+ localHLS = ffmpegHLS;
+ if (localHLS == null) {
+ setupFfmpegFormat(FFmpegFormat.HLS);
+ localHLS = ffmpegHLS;
+ }
+ if (localHLS != null) {
+ localHLS.setKeepAlive(-1);// Now will run till manually stopped.
+ localHLS.startConverting();
}
} else {
- if (ffmpegHLS != null) {
- ffmpegHLS.setKeepAlive(1);
+ localHLS = ffmpegHLS;
+ if (localHLS != null) {
+ // Still runs but will be able to auto stop when the HLS stream is no longer used.
+ localHLS.setKeepAlive(1);
}
}
return;
@@ -1228,8 +1246,9 @@ public class IpCameraHandler extends BaseThingHandler {
sendHttpGET(snapshotUri);// Allows this to change Image FPS on demand
}
} else {
- if (ffmpegSnapshot != null) {
- ffmpegSnapshot.stopConverting();
+ Ffmpeg localSnaps = ffmpegSnapshot;
+ if (localSnaps != null) {
+ localSnaps.stopConverting();
ffmpegSnapshotGeneration = false;
}
updateImageChannel = false;
@@ -1376,8 +1395,9 @@ public class IpCameraHandler extends BaseThingHandler {
updateStatus(ThingStatus.ONLINE);
groupTracker.listOfOnlineCameraHandlers.add(this);
groupTracker.listOfOnlineCameraUID.add(getThing().getUID().getId());
- if (cameraConnectionJob != null) {
- cameraConnectionJob.cancel(false);
+ Future> localFuture = cameraConnectionJob;
+ if (localFuture != null) {
+ localFuture.cancel(false);
}
if (cameraConfig.getGifPreroll() > 0 || cameraConfig.getUpdateImageWhen().contains("1")) {
@@ -1482,16 +1502,19 @@ public class IpCameraHandler extends BaseThingHandler {
}
public void stopSnapshotPolling() {
+ Future> localFuture;
if (!streamingSnapshotMjpeg && cameraConfig.getGifPreroll() == 0
&& !cameraConfig.getUpdateImageWhen().contains("1")) {
snapshotPolling = false;
- if (snapshotJob != null) {
- snapshotJob.cancel(true);
+ localFuture = snapshotJob;
+ if (localFuture != null) {
+ localFuture.cancel(true);
}
} else if (cameraConfig.getUpdateImageWhen().contains("4")) { // only during Motion Alarms
snapshotPolling = false;
- if (snapshotJob != null) {
- snapshotJob.cancel(true);
+ localFuture = snapshotJob;
+ if (localFuture != null) {
+ localFuture.cancel(true);
}
}
}
@@ -1575,8 +1598,9 @@ public class IpCameraHandler extends BaseThingHandler {
}
break;
}
- if (ffmpegHLS != null) {
- ffmpegHLS.checkKeepAlive();
+ Ffmpeg localHLS = ffmpegHLS;
+ if (localHLS != null) {
+ localHLS.checkKeepAlive();
}
if (openChannels.size() > 18) {
logger.debug("There are {} open Channels being tracked.", openChannels.size());
@@ -1681,17 +1705,17 @@ public class IpCameraHandler extends BaseThingHandler {
isOnline = false;
snapshotPolling = false;
onvifCamera.disconnect();
- if (pollCameraJob != null) {
- pollCameraJob.cancel(true);
- pollCameraJob = null;
+ Future> localFuture = pollCameraJob;
+ if (localFuture != null) {
+ localFuture.cancel(true);
}
- if (snapshotJob != null) {
- snapshotJob.cancel(true);
- snapshotJob = null;
+ localFuture = snapshotJob;
+ if (localFuture != null) {
+ localFuture.cancel(true);
}
- if (cameraConnectionJob != null) {
- cameraConnectionJob.cancel(true);
- cameraConnectionJob = null;
+ localFuture = cameraConnectionJob;
+ if (localFuture != null) {
+ localFuture.cancel(true);
}
threadPool.shutdown();
threadPool = Executors.newScheduledThreadPool(4);
@@ -1707,29 +1731,29 @@ public class IpCameraHandler extends BaseThingHandler {
stopStreamServer();
openChannels.close();
- if (ffmpegHLS != null) {
- ffmpegHLS.stopConverting();
- ffmpegHLS = null;
+ Ffmpeg localFfmpeg = ffmpegHLS;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
- if (ffmpegRecord != null) {
- ffmpegRecord.stopConverting();
- ffmpegRecord = null;
+ localFfmpeg = ffmpegRecord;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
- if (ffmpegGIF != null) {
- ffmpegGIF.stopConverting();
- ffmpegGIF = null;
+ localFfmpeg = ffmpegGIF;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
- if (ffmpegRtspHelper != null) {
- ffmpegRtspHelper.stopConverting();
- ffmpegRtspHelper = null;
+ localFfmpeg = ffmpegRtspHelper;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
- if (ffmpegMjpeg != null) {
- ffmpegMjpeg.stopConverting();
- ffmpegMjpeg = null;
+ localFfmpeg = ffmpegMjpeg;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
- if (ffmpegSnapshot != null) {
- ffmpegSnapshot.stopConverting();
- ffmpegSnapshot = null;
+ localFfmpeg = ffmpegSnapshot;
+ if (localFfmpeg != null) {
+ localFfmpeg.stopConverting();
}
channelTrackingMap.clear();
}
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java
index 402725db5..fdad134c1 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java
+++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java
@@ -21,9 +21,11 @@ import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.LinkedList;
+import java.util.List;
import java.util.Random;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -33,6 +35,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ipcamera.internal.Helper;
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.types.StateOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -145,8 +149,9 @@ public class OnvifConnection {
private String ptzNodeToken = "000";
private String ptzConfigToken = "000";
private int presetTokenIndex = 0;
- private LinkedList presetTokens = new LinkedList<>();
- private LinkedList mediaProfileTokens = new LinkedList<>();
+ private List presetTokens = new LinkedList<>();
+ private List presetNames = new LinkedList<>();
+ private List mediaProfileTokens = new LinkedList<>();
private boolean ptzDevice = true;
public OnvifConnection(IpCameraHandler ipCameraHandler, String ipAddress, String user, String password) {
@@ -277,8 +282,7 @@ public class OnvifConnection {
case GotoPreset:
return ""
+ mediaProfileTokens.get(mediaProfileIndex) + ""
- + presetTokens.get(presetTokenIndex)
- + "";
+ + presetTokens.get(presetTokenIndex) + "";
case GetPresets:
return ""
+ mediaProfileTokens.get(mediaProfileIndex) + "";
@@ -326,7 +330,7 @@ public class OnvifConnection {
} else if (message.contains("GetStatusResponse")) {
processPTZLocation(message);
} else if (message.contains("GetPresetsResponse")) {
- presetTokens = listOfResults(message, "http://" + ipAddress + xAddr + "";
+ extraEnvelope = " xmlns:a=\"http://www.w3.org/2005/08/addressing\"";
}
- if (!password.isEmpty()) {
+ String headers;
+ if (!password.isEmpty() && !requestType.equals(RequestType.GetSystemDateAndTime)) {
String nonce = createNonce();
String dateTime = getUTCdateTime();
String digest = createDigest(nonce, dateTime);
@@ -376,17 +382,15 @@ public class OnvifConnection {
+ encodeBase64(nonce)
+ ""
+ dateTime + "";
- }
- String headers = "" + security + headerTo + "";
-
- if (requestType.equals(RequestType.GetSystemDateAndTime)) {
- extraEnvelope = "";
+ headers = "" + security + headerTo + "";
+ } else {// GetSystemDateAndTime must not be password protected as per spec.
headers = "";
}
-
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("POST"), xAddr);
- request.headers().add("Content-Type", "application/soap+xml");
- request.headers().add("charset", "utf-8");
+ String actionString = Helper.fetchXML(getXmlCache, requestType.toString(), "xmlns=\"");
+ request.headers().add("Content-Type",
+ "application/soap+xml; charset=utf-8; action=\"" + actionString + "/" + requestType + "\"");
+ request.headers().add("Charset", "utf-8");
if (onvifPort != 80) {
request.headers().set("Host", ipAddress + ":" + onvifPort);
} else {
@@ -398,7 +402,6 @@ public class OnvifConnection {
+ headers
+ ""
+ getXmlCache + "";
- String actionString = Helper.fetchXML(getXmlCache, requestType.toString(), "xmlns=\"");
request.headers().add("SOAPAction", "\"" + actionString + "/" + requestType + "\"");
ByteBuf bbuf = Unpooled.copiedBuffer(fullXml, StandardCharsets.UTF_8);
request.headers().set("Content-Length", bbuf.readableBytes());
@@ -408,15 +411,14 @@ public class OnvifConnection {
/**
* The {@link removeIPfromUrl} Will throw away all text before the cameras IP, also removes the IP and the PORT
- * leaving just the
- * URL.
+ * leaving just the URL.
*
* @author Matthew Skinner - Initial contribution
*/
String removeIPfromUrl(String url) {
- int index = url.indexOf(ipAddress);
+ int index = url.indexOf("//");
if (index != -1) {// now remove the :port
- index = url.indexOf("/", index + ipAddress.length());
+ index = url.indexOf("/", index + 2);
}
if (index == -1) {
logger.debug("We hit an issue parsing url:{}", url);
@@ -456,11 +458,10 @@ public class OnvifConnection {
String minute = Helper.fetchXML(message, "UTCDateTime", "Minute>");
String hour = Helper.fetchXML(message, "UTCDateTime", "Hour>");
String second = Helper.fetchXML(message, "UTCDateTime", "Second>");
- logger.debug("Cameras UTC time is : {}:{}:{}", hour, minute, second);
String day = Helper.fetchXML(message, "UTCDateTime", "Day>");
String month = Helper.fetchXML(message, "UTCDateTime", "Month>");
String year = Helper.fetchXML(message, "UTCDateTime", "Year>");
- logger.debug("Cameras UTC date is : {}-{}-{}", year, month, day);
+ logger.debug("Cameras UTC dateTime is:{}-{}-{}T{}:{}:{}", year, month, day, hour, minute, second);
}
private String getUTCdateTime() {
@@ -718,8 +719,8 @@ public class OnvifConnection {
this.mediaProfileIndex = mediaProfileIndex;
}
- LinkedList listOfResults(String message, String heading, String key) {
- LinkedList results = new LinkedList();
+ List listOfResults(String message, String heading, String key) {
+ List results = new LinkedList<>();
String temp = "";
for (int startLookingFromIndex = 0; startLookingFromIndex != -1;) {
startLookingFromIndex = message.indexOf(heading, startLookingFromIndex);
@@ -728,13 +729,31 @@ public class OnvifConnection {
if (!temp.isEmpty()) {
logger.trace("String was found:{}", temp);
results.add(temp);
- ++startLookingFromIndex;
+ } else {
+ return results;// key string must not exist so stop looking.
}
+ startLookingFromIndex += temp.length();
}
}
return results;
}
+ void parsePresets(String message) {
+ List presets = new ArrayList<>();
+ int counter = 1;// Presets start at 1 not 0. HOME may be added to index 0.
+ presetTokens = listOfResults(message, "");
+ if (presetTokens.size() != presetNames.size()) {
+ logger.warn("Camera did not report the same number of Tokens and Names for PTZ presets");
+ return;
+ }
+ for (String value : presetNames) {
+ presets.add(new StateOption(Integer.toString(counter++), value));
+ }
+ ipCameraHandler.stateDescriptionProvider
+ .setStateOptions(new ChannelUID(ipCameraHandler.getThing().getUID(), CHANNEL_GOTO_PRESET), presets);
+ }
+
void parseProfiles(String message) {
mediaProfileTokens = listOfResults(message, "= mediaProfileTokens.size()) {
diff --git a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml
index c88b0ad28..d7ed31478 100644
--- a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml
+++ b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml
@@ -241,6 +241,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -479,6 +487,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -722,6 +738,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -1006,6 +1030,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -1272,6 +1304,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -1535,6 +1575,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -1823,6 +1871,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url
@@ -2098,6 +2154,14 @@
true
+
+
+ Specify your own FFmpeg options to be used when creating snapshots from RTSP.
+
+ -an -vsync vfr -q:v 2 -update 1
+ true
+
+
url