diff --git a/bundles/org.openhab.binding.ipcamera/README.md b/bundles/org.openhab.binding.ipcamera/README.md index e0abbe6d8274e..b6dd83885688d 100644 --- a/bundles/org.openhab.binding.ipcamera/README.md +++ b/bundles/org.openhab.binding.ipcamera/README.md @@ -128,8 +128,10 @@ Thing ipcamera:hikvision:West "West Camera" ### Reolink -- NVR's made by Reolink have ONVIF disabled by default and may require a screen connected to the hardware to enable ONVIF or newer firmwares may be able to do this via their app or web UI. +- NVR's made by Reolink have ONVIF disabled by default and may require a screen connected to the hardware to enable ONVIF, or newer firmwares may be able to do this via their app or web UI. - This binding will use the Reolink API for polling the alarms if the `nvrChannel` is 1 or higher and does not need ONVIF to be enabled. To use ONVIF event methods for the alarms, you can set `nvrChannel` to 0. +- Cameras have ONVIF, RTSP and HTTP disabled by default, to enable these required features, do the following: DEVICE SETTINGS>NETWORK>ADVANCED>PORT SETTINGS> then turn these on leaving the default port number alone. +- Consider setting the substream of the camera to be 4 FPS and to 640*360 as this will lower CPU load if using the ipcamera.mjpeg stream. ## Discovery @@ -194,7 +196,7 @@ If you do not specify any of these, the binding will use the default which shoul | `alarmInputUrl` | A URL you can use for the FFmpeg created Audio and Motion Alarms as they don't require high res feeds as they are not seen. | | `customMotionAlarmUrl`| Foscam only, for custom enable motion alarm use. More info found in Foscam's setup steps. | | `customAudioAlarmUrl`| Foscam only, for custom enable audio alarm use. More info found in Foscam's setup steps. | -| `mjpegUrl`| A HTTP URL for MJPEG format streams. If you enter 'ffmpeg' the stream can be generated from the RTSP URL. | +| `mjpegUrl`| A HTTP or RTSP URL to use for MJPEG streams. If you enter 'ffmpeg' the stream can be generated using the RTSP URL. | | `ffmpegInput`| Best if this stream is in H.264 format and can be RTSP or HTTP URLs. Leave this blank to use the auto detected RTSP address for ONVIF cameras. | | `ffmpegInputOptions` | Allows you to specify any options before the -i on the commands for FFmpeg. If you have an ESP32 camera that only has a mjpeg stream then make this equal `-f mjpeg`. | | `ffmpegLocation`| The full path including the filename for where you have installed FFmpeg. The default should work for most Linux installs but if using windows use this format: `c:\ffmpeg\bin\ffmpeg.exe` | @@ -434,14 +436,15 @@ You can cast it which can be handy to show a moving picture that keeps repeating ## MJPEG Streams Cameras that have built in MJPEG abilities can stream to openHAB with the MJPEG format with next to no CPU load, less than 1 second lag, and FFmpeg does not need to be installed. -Cameras without this ability can still use this binding to convert their RTSP H.264 format to MJPEG (keep reading for more on this below) and this will take a lot of CPU power to handle the conversion. +Cameras without this ability can still use this binding to convert their RTSP H.264 format to MJPEG (keep reading for more on this below) and this may use a lot of CPU power to handle the conversion. +The lower the resolution of the stream, the lower the CPU load, so consider using a substream of the camera in the `mjpegUrl` configuration field. The alternative is to use HLS format which does not need the conversion and does not use any CPU to speak of. For video without a delay, you need MJPEG and without a camera that can create it, you will need to use a lot of CPU power. This can be done in a dedicated video server which will be the only way with lots of cameras, unless you purchase cameras that have the ability built in. -An alternative way to keep the CPU load low is to use the `snapshots.mjpeg` feature of the binding to create a stream from the cameras snapshots instead of the RTSP stream. +An alternative way to keep the CPU load low is to use the `snapshots.mjpeg` or `autofps.mjpeg` feature of the binding to create a stream from the cameras snapshots instead of the RTSP stream. -The main cameras that can do MJPEG with very low CPU load are Amcrest, Dahua, Hikvision, Foscam HD and Instar HD. +The main cameras that can do MJPEG with very low CPU load are Amcrest, Dahua, ESP32 Camera, Hikvision, Foscam HD and Instar HD. To set this up, see [Special Notes for Different Brands](#special-notes-for-different-brands). The binding can then distribute this stream to many devices around your home whilst the camera only sees a single open stream. @@ -597,7 +600,7 @@ Webview url="http://192.168.6.4:8080/static/html/file.html" height=5 There are two ways to cast a camera. 1. openHAB Cloud Connector and using metadata/tags. -1. Chromecast Bindings `playuri` channel. +2. Chromecast Bindings `playuri` channel. The first method once setup allows you to ask "OK Google show X camera", or "OK Google show X camera on Y display". By optionally naming the display that you wish to use, it can be cast directly to your Chromecast (connected to your TV) by speaking to a Google Nest Mini. 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 0ea900151e2b2..11b84a92d0937 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 @@ -63,7 +63,7 @@ public Ffmpeg(IpCameraHandler handle, FFmpegFormat format, String ffmpegLocation ipCameraHandler = handle; String altInput = input; // Input can be snapshots not just rtsp or http - if (!password.isEmpty() && !input.contains("@") && input.contains("rtsp")) { + if (!password.isEmpty() && !input.contains("@") && input.toLowerCase().startsWith("rtsp")) { String credentials = username + ":" + this.password + "@"; // will not work for https: but currently binding does not use https altInput = input.substring(0, 7) + credentials + input.substring(7); 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 9707ae0ec748c..a83dbe6135ad9 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 @@ -712,7 +712,7 @@ public void startStreamServer() { } public void openCamerasStream() { - if (mjpegUri.isEmpty() || "ffmpeg".equals(mjpegUri)) { + if (usingRtspForMjpeg()) { setupFfmpegFormat(FFmpegFormat.MJPEG); return; } @@ -811,7 +811,7 @@ public void setupFfmpegFormat(FFmpegFormat format) { logger.warn("The camera tried to use a FFmpeg feature when the location for FFmpeg is not known."); return; } - if (rtspUri.toLowerCase().contains("rtsp")) { + if (mjpegUri.toLowerCase().startsWith("rtsp://") || rtspUri.toLowerCase().startsWith("rtsp://")) { if (inputOptions.isEmpty()) { inputOptions = "-rtsp_transport tcp"; } @@ -938,10 +938,17 @@ public void setupFfmpegFormat(FFmpegFormat format) { } else { inputOptions += " -hide_banner"; } - ffmpegMjpeg = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, rtspUri, - cameraConfig.getMjpegOptions(), "http://127.0.0.1:" + SERVLET_PORT + "/ipcamera/" - + getThing().getUID().getId() + "/ipcamera.jpg", - cameraConfig.getUser(), cameraConfig.getPassword()); + if (mjpegUri.toLowerCase().startsWith("rtsp://")) { + ffmpegMjpeg = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, mjpegUri, + cameraConfig.getMjpegOptions(), "http://127.0.0.1:" + SERVLET_PORT + "/ipcamera/" + + getThing().getUID().getId() + "/ipcamera.jpg", + cameraConfig.getUser(), cameraConfig.getPassword()); + } else { + ffmpegMjpeg = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, rtspUri, + cameraConfig.getMjpegOptions(), "http://127.0.0.1:" + SERVLET_PORT + "/ipcamera/" + + getThing().getUID().getId() + "/ipcamera.jpg", + cameraConfig.getUser(), cameraConfig.getPassword()); + } } Ffmpeg localMjpeg = ffmpegMjpeg; if (localMjpeg != null) { @@ -1644,7 +1651,10 @@ public void initialize() { threadPool = Executors.newScheduledThreadPool(2); mainEventLoopGroup = new NioEventLoopGroup(3); snapshotUri = getCorrectUrlFormat(cameraConfig.getSnapshotUrl()); - mjpegUri = getCorrectUrlFormat(cameraConfig.getMjpegUrl()); + mjpegUri = cameraConfig.getMjpegUrl(); + if (!mjpegUri.toLowerCase().startsWith("rtsp://")) { + mjpegUri = getCorrectUrlFormat(mjpegUri); + } rtspUri = cameraConfig.getFfmpegInput(); if (cameraConfig.getFfmpegOutput().isEmpty()) { cameraConfig @@ -1734,6 +1744,10 @@ public void initialize() { rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0" + (cameraConfig.getNvrChannel() + 1) + "_main"; } + if (mjpegUri.isEmpty()) { + mjpegUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0" + + (cameraConfig.getNvrChannel() + 1) + "_sub"; + } break; } // for poll times 9 seconds and above don't display a warning about the Image channel. @@ -1773,7 +1787,7 @@ private boolean supportsOnvifEvents() { private void keepMjpegRunning() { CameraServlet localServlet = servlet; if (localServlet != null && !localServlet.openStreams.isEmpty()) { - if (!mjpegUri.isEmpty() && !"ffmpeg".equals(mjpegUri)) { + if (!usingRtspForMjpeg()) { localServlet.openStreams.queueFrame(("--" + localServlet.openStreams.boundary + "\r\n\r\n").getBytes()); } localServlet.openStreams.queueFrame(getSnapshot()); @@ -1871,6 +1885,10 @@ public String getWhiteList() { return cameraConfig.getIpWhitelist(); } + public boolean usingRtspForMjpeg() { + return (mjpegUri.isEmpty() || "ffmpeg".equals(mjpegUri) || mjpegUri.toLowerCase().startsWith("rtsp://")); + } + @Override public Collection> getServices() { return Set.of(IpCameraActions.class); 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 bbe4457e073a4..269480e1c8eea 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 @@ -368,7 +368,7 @@ public void processReply(RequestType requestType, String message) { if (xml != null) { rtspUri = xml; logger.debug("GetStreamUri: {}", rtspUri); - if (ipCameraHandler.cameraConfig.getFfmpegInput().isEmpty()) { + if (ipCameraHandler.rtspUri.isEmpty()) {// only use if not hard coded in initialize() ipCameraHandler.rtspUri = rtspUri; } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/servlet/CameraServlet.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/servlet/CameraServlet.java index 0aa15ceeb6575..f37f4716fe664 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/servlet/CameraServlet.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/servlet/CameraServlet.java @@ -184,12 +184,12 @@ public void run() { if (openStreams.isEmpty()) { logger.debug("First stream requested, opening up stream from camera"); handler.openCamerasStream(); - if (handler.mjpegUri.isEmpty() || "ffmpeg".equals(handler.mjpegUri)) { + if (handler.usingRtspForMjpeg()) { output = new StreamOutput(resp); } else { output = new StreamOutput(resp, handler.mjpegContentType); } - } else if (handler.mjpegUri.isEmpty() || "ffmpeg".equals(handler.mjpegUri)) { + } else if (handler.usingRtspForMjpeg()) { output = new StreamOutput(resp); } else { ChannelTracking tracker = handler.channelTrackingMap.get(handler.getTinyUrl(handler.mjpegUri)); 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 c4c0acc6966de..8a52f9f8a6eb4 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 @@ -2419,7 +2419,7 @@ This gives you direct access to specify your own FFmpeg options to be used for MJPEG streams. - -q:v 5 -r 2 -vf scale=640:-2 -update 1 + -q:v 4 -r 4 -update 1 true