]> git.basschouten.com Git - openhab-addons.git/blob
93ed3ce318d09035d366c130ba20ed1b4cd4295f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.ipcamera.internal.servlet;
14
15 import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.util.concurrent.TimeUnit;
20
21 import javax.servlet.http.HttpServletRequest;
22 import javax.servlet.http.HttpServletResponse;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.ipcamera.internal.handler.IpCameraGroupHandler;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.thing.ChannelUID;
29 import org.osgi.service.http.HttpService;
30
31 /**
32  * The {@link GroupServlet} is responsible for serving files for a rotating feed of multiple cameras back to the Jetty
33  * server normally found on port 8080
34  *
35  * @author Matthew Skinner - Initial contribution
36  */
37 @NonNullByDefault
38 public class GroupServlet extends IpCameraServlet {
39     private static final long serialVersionUID = -234658667574L;
40     private final IpCameraGroupHandler handler;
41     public int snapshotStreamsOpen = 0;
42
43     public GroupServlet(IpCameraGroupHandler handler, HttpService httpService) {
44         super(handler, httpService);
45         this.handler = handler;
46     }
47
48     @Override
49     protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
50         if (req == null || resp == null) {
51             return;
52         }
53         String pathInfo = req.getPathInfo();
54         if (pathInfo == null) {
55             return;
56         }
57         logger.debug("GET:{}, received from {}", pathInfo, req.getRemoteHost());
58         if (!"DISABLE".equals(handler.groupConfig.getIpWhitelist())) {
59             String requestIP = "(" + req.getRemoteHost() + ")";
60             if (!handler.groupConfig.getIpWhitelist().contains(requestIP)) {
61                 logger.warn("The request made from {} was not in the whiteList and will be ignored.", requestIP);
62                 return;
63             }
64         }
65         switch (pathInfo) {
66             case "/ipcamera.m3u8":
67                 if (!handler.hlsTurnedOn) {
68                     logger.debug(
69                             "HLS requires the groups startStream channel to be turned on first. Just starting it now.");
70                     String channelPrefix = "ipcamera:" + handler.getThing().getThingTypeUID() + ":"
71                             + handler.getThing().getUID().getId() + ":";
72                     handler.handleCommand(new ChannelUID(channelPrefix + CHANNEL_START_STREAM), OnOffType.ON);
73                     try {
74                         TimeUnit.MILLISECONDS.sleep(HLS_STARTUP_DELAY_MS);
75                     } catch (InterruptedException e) {
76                         return;
77                     }
78                 }
79                 String playList = handler.getPlayList();
80                 sendString(resp, playList, "application/x-mpegURL");
81                 return;
82             case "/ipcamera.jpg":
83                 sendSnapshotImage(resp, "image/jpg", handler.getSnapshot());
84                 return;
85             case "/ipcamera.mjpeg":
86             case "/snapshots.mjpeg":
87                 req.getSession().setMaxInactiveInterval(0);
88                 snapshotStreamsOpen++;
89                 StreamOutput output = new StreamOutput(resp);
90                 do {
91                     try {
92                         output.sendSnapshotBasedFrame(handler.getSnapshot());
93                         Thread.sleep(1005);
94                     } catch (InterruptedException | IOException e) {
95                         // Never stop streaming until IOException. Occurs when browser stops the stream.
96                         snapshotStreamsOpen--;
97                         if (snapshotStreamsOpen == 0) {
98                             logger.debug("All snapshots.mjpeg streams have stopped.");
99                         }
100                         return;
101                     }
102                 } while (true);
103             default:
104                 // example is "/1ipcameraxx.ts"
105                 if (pathInfo.endsWith(".ts")) {
106                     sendFile(resp, pathInfo, "video/MP2T");
107                 }
108         }
109     }
110
111     private String resolveIndexToPath(String uri) {
112         if (!"i".equals(uri.substring(1, 2))) {
113             return handler.getOutputFolder(Integer.parseInt(uri.substring(1, 2)));
114         }
115         return "notFound";
116     }
117
118     @Override
119     protected void sendFile(HttpServletResponse response, String filename, String contentType) throws IOException {
120         // Ensure no files can be sourced from parent or child folders
121         String truncated = filename.substring(filename.lastIndexOf("/"));
122         truncated = resolveIndexToPath(truncated) + truncated.substring(2);
123         File file = new File(truncated);
124         if (!file.exists()) {
125             logger.warn(
126                     "HLS File {} was not found. Try adding a larger -hls_delete_threshold to each cameras HLS out options.",
127                     file.getName());
128             response.sendError(HttpServletResponse.SC_NOT_FOUND);
129             return;
130         }
131         super.sendFile(response, truncated, contentType);
132     }
133
134     @Override
135     protected void sendSnapshotImage(HttpServletResponse response, String contentType, byte[] snapshot) {
136         if (handler.cameraIndex >= handler.cameraOrder.size()) {
137             logger.debug("All cameras in this group are OFFLINE and a snapshot was requested.");
138             return;
139         }
140         super.sendSnapshotImage(response, contentType, snapshot);
141     }
142 }