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