From 333e7d2ef28f3d1bf20a6c6a2283045809b85374 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Wed, 11 Aug 2021 20:02:19 +1000 Subject: [PATCH] Fix IndexOutOfBoundsException and remove Sleep. (#11089) Signed-off-by: Matthew Skinner --- .../internal/handler/IpCameraHandler.java | 15 +- .../internal/onvif/OnvifConnection.java | 284 ++++++++++-------- 2 files changed, 158 insertions(+), 141 deletions(-) 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 635b51f27ec..eacb18c1ecc 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 @@ -768,6 +768,10 @@ public class IpCameraHandler extends BaseThingHandler { } } + private void openMjpegStream() { + sendHttpGET(mjpegUri); + } + // If start is true the CTX is added to the list to stream video to, false stops // the stream. public void setupMjpegStreaming(boolean start, ChannelHandlerContext ctx) { @@ -777,13 +781,8 @@ public class IpCameraHandler extends BaseThingHandler { if (mjpegUri.isEmpty() || "ffmpeg".equals(mjpegUri)) { sendMjpegFirstPacket(ctx); setupFfmpegFormat(FFmpegFormat.MJPEG); - } else { - try { - // fix Dahua reboots when refreshing a mjpeg stream. - TimeUnit.MILLISECONDS.sleep(500); - } catch (InterruptedException e) { - } - sendHttpGET(mjpegUri); + } else {// Delay fixes Dahua reboots when refreshing a mjpeg stream. + threadPool.schedule(this::openMjpegStream, 500, TimeUnit.MILLISECONDS); } } else if (ffmpegMjpeg != null) {// not first stream and we will use ffmpeg sendMjpegFirstPacket(ctx); @@ -1779,7 +1778,6 @@ public class IpCameraHandler extends BaseThingHandler { public void dispose() { isOnline = false; snapshotPolling = false; - onvifCamera.disconnect(); Future localFuture = pollCameraJob; if (localFuture != null) { localFuture.cancel(true); @@ -1832,6 +1830,7 @@ public class IpCameraHandler extends BaseThingHandler { localFfmpeg.stopConverting(); } channelTrackingMap.clear(); + onvifCamera.disconnect(); } public void setStreamServerHandler(StreamServerHandler streamServerHandler2) { 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 eb86c7081e5..6e03c9d4e4e 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 @@ -27,6 +27,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.TimeZone; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -110,6 +112,7 @@ public class OnvifConnection { } private final Logger logger = LoggerFactory.getLogger(getClass()); + private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2); private @Nullable Bootstrap bootstrap; private EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup(); private String ipAddress = ""; @@ -162,129 +165,138 @@ public class OnvifConnection { } } - String getXml(RequestType requestType) { - switch (requestType) { - case AbsoluteMove: - return "" - + mediaProfileTokens.get(mediaProfileIndex) + "\n" - + "\n" + "\n" - + "\n" + "\n" - + "\n" - + ""; - case AddPTZConfiguration: // not tested to work yet - return "" - + mediaProfileTokens.get(mediaProfileIndex) + "" - + ptzConfigToken + ""; - case ContinuousMoveLeft: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case ContinuousMoveRight: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case ContinuousMoveUp: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case ContinuousMoveDown: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case Stop: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + "truetrue"; - case ContinuousMoveIn: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case ContinuousMoveOut: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case CreatePullPointSubscription: - return "PT600S"; - case GetCapabilities: - return "All"; + private String getXml(RequestType requestType) { + try { + switch (requestType) { + case AbsoluteMove: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + "\n" + + "\n" + "\n" + + "\n" + "\n" + + "\n" + + ""; + case AddPTZConfiguration: // not tested to work yet + return "" + + mediaProfileTokens.get(mediaProfileIndex) + "" + + ptzConfigToken + ""; + case ContinuousMoveLeft: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case ContinuousMoveRight: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case ContinuousMoveUp: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case ContinuousMoveDown: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case Stop: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + "truetrue"; + case ContinuousMoveIn: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case ContinuousMoveOut: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case CreatePullPointSubscription: + return "PT600S"; + case GetCapabilities: + return "All"; - case GetDeviceInformation: - return ""; - case GetProfiles: - return ""; - case GetServiceCapabilities: - return ""; - case GetSnapshotUri: - return "" - + mediaProfileTokens.get(mediaProfileIndex) + ""; - case GetStreamUri: - return "RTP-UnicastRTSP" - + mediaProfileTokens.get(mediaProfileIndex) + ""; - case GetSystemDateAndTime: - return ""; - case Subscribe: - return "
http://" - + ipCameraHandler.hostIp + ":" + ipCameraHandler.cameraConfig.getServerPort() - + "/OnvifEvent
"; - case Unsubscribe: - return ""; - case PullMessages: - return "PT8S1"; - case GetEventProperties: - return ""; - case RelativeMoveLeft: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case RelativeMoveRight: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case RelativeMoveUp: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case RelativeMoveDown: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case RelativeMoveIn: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case RelativeMoveOut: - return "" - + mediaProfileTokens.get(mediaProfileIndex) - + ""; - case Renew: - return "PT1M"; - case GetConfigurations: - return ""; - case GetConfigurationOptions: - return "" - + ptzConfigToken + ""; - case GetConfiguration: - return "" - + ptzConfigToken + ""; - case SetConfiguration:// not tested to work yet - return "" - + ptzNodeToken - + "AbsolutePanTiltPositionSpaceAbsoluteZoomPositionSpace"; - case GetNodes: - return ""; - case GetStatus: - return "" - + mediaProfileTokens.get(mediaProfileIndex) + ""; - case GotoPreset: - return "" - + mediaProfileTokens.get(mediaProfileIndex) + "" - + presetTokens.get(presetTokenIndex) + ""; - case GetPresets: - return "" - + mediaProfileTokens.get(mediaProfileIndex) + ""; + case GetDeviceInformation: + return ""; + case GetProfiles: + return ""; + case GetServiceCapabilities: + return ""; + case GetSnapshotUri: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + ""; + case GetStreamUri: + return "RTP-UnicastRTSP" + + mediaProfileTokens.get(mediaProfileIndex) + ""; + case GetSystemDateAndTime: + return ""; + case Subscribe: + return "
http://" + + ipCameraHandler.hostIp + ":" + ipCameraHandler.cameraConfig.getServerPort() + + "/OnvifEvent
"; + case Unsubscribe: + return ""; + case PullMessages: + return "PT8S1"; + case GetEventProperties: + return ""; + case RelativeMoveLeft: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case RelativeMoveRight: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case RelativeMoveUp: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case RelativeMoveDown: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case RelativeMoveIn: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case RelativeMoveOut: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + + ""; + case Renew: + return "PT1M"; + case GetConfigurations: + return ""; + case GetConfigurationOptions: + return "" + + ptzConfigToken + ""; + case GetConfiguration: + return "" + + ptzConfigToken + ""; + case SetConfiguration:// not tested to work yet + return "" + + ptzNodeToken + + "AbsolutePanTiltPositionSpaceAbsoluteZoomPositionSpace"; + case GetNodes: + return ""; + case GetStatus: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + ""; + case GotoPreset: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + "" + + presetTokens.get(presetTokenIndex) + ""; + case GetPresets: + return "" + + mediaProfileTokens.get(mediaProfileIndex) + ""; + } + } catch (IndexOutOfBoundsException e) { + if (!isConnected) { + logger.debug("IndexOutOfBoundsException occured, camera is not connected via ONVIF: {}", + e.getMessage()); + } else { + logger.debug("IndexOutOfBoundsException occured, {}", e.getMessage()); + } } return "notfound"; } @@ -805,6 +817,10 @@ public class OnvifConnection { } public void sendPTZRequest(RequestType requestType) { + if (!isConnected) { + logger.debug("ONVIF was not connected when a PTZ request was made, connecting now"); + connect(usingEvents); + } sendOnvifRequest(requestBuilder(requestType, ptzXAddr)); } @@ -823,26 +839,28 @@ public class OnvifConnection { return isConnected; } - public void disconnect() { - if (usingEvents && isConnected) { - sendOnvifRequest(requestBuilder(RequestType.Unsubscribe, subscriptionXAddr)); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - } - } + private void cleanup() { + mainEventLoopGroup.shutdownGracefully(); isConnected = false; - presetTokens.clear(); - mediaProfileTokens.clear(); if (!mainEventLoopGroup.isShutdown()) { try { mainEventLoopGroup.awaitTermination(3, TimeUnit.SECONDS); } catch (InterruptedException e) { - logger.info("Onvif was not shutdown correctly due to being interrupted"); + logger.warn("ONVIF was not cleanly shutdown, due to being interrupted"); } finally { + logger.debug("Eventloop is shutdown:{}", mainEventLoopGroup.isShutdown()); mainEventLoopGroup = new NioEventLoopGroup(); bootstrap = null; } } + threadPool.shutdown(); + } + + public void disconnect() { + if (usingEvents && isConnected && !mainEventLoopGroup.isShuttingDown()) { + sendOnvifRequest(requestBuilder(RequestType.Unsubscribe, subscriptionXAddr)); + } + // Some cameras may continue to send event callbacks even when they cant reach a server. + threadPool.schedule(this::cleanup, 500, TimeUnit.MILLISECONDS); } }