]> git.basschouten.com Git - openhab-addons.git/blob
bd0c85de1378d74cf6a2cfedeb8b99b1b2c9f544
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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 java.io.IOException;
16 import java.util.concurrent.ArrayBlockingQueue;
17 import java.util.concurrent.BlockingQueue;
18
19 import javax.servlet.ServletOutputStream;
20 import javax.servlet.http.HttpServletResponse;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * The {@link StreamOutput} Streams mjpeg out to a client
28  *
29  * @author Matthew Skinner - Initial contribution
30  */
31 @NonNullByDefault
32 public class StreamOutput {
33     public final Logger logger = LoggerFactory.getLogger(getClass());
34     private final HttpServletResponse response;
35     private final String boundary;
36     private String contentType;
37     private final ServletOutputStream output;
38     private BlockingQueue<byte[]> fifo = new ArrayBlockingQueue<byte[]>(50);
39     private boolean connected = false;
40     public boolean isSnapshotBased = false;
41
42     public StreamOutput(HttpServletResponse response) throws IOException {
43         boundary = "thisMjpegStream";
44         contentType = "multipart/x-mixed-replace; boundary=" + boundary;
45         this.response = response;
46         output = response.getOutputStream();
47         isSnapshotBased = true;
48     }
49
50     public StreamOutput(HttpServletResponse response, String contentType) throws IOException {
51         boundary = "";
52         this.contentType = contentType;
53         this.response = response;
54         output = response.getOutputStream();
55         if (!contentType.isEmpty()) {
56             sendInitialHeaders();
57             connected = true;
58         }
59     }
60
61     public void sendSnapshotBasedFrame(byte[] currentSnapshot) throws IOException {
62         String header = "--" + boundary + "\r\n" + "Content-Type: image/jpeg" + "\r\n" + "Content-Length: "
63                 + currentSnapshot.length + "\r\n\r\n";
64         if (!connected) {
65             sendInitialHeaders();
66             // iOS needs to have two jpgs sent for the picture to appear instantly.
67             output.write(header.getBytes());
68             output.write(currentSnapshot);
69             output.write("\r\n".getBytes());
70             connected = true;
71         }
72         output.write(header.getBytes());
73         output.write(currentSnapshot);
74         output.write("\r\n".getBytes());
75     }
76
77     public void queueFrame(byte[] frame) {
78         try {
79             fifo.add(frame);
80         } catch (IllegalStateException e) {
81             logger.debug("FIFO buffer has run out of space: {}", e.getMessage());
82             fifo.remove();
83             fifo.add(frame);
84         }
85     }
86
87     public void updateContentType(String contentType) {
88         this.contentType = contentType;
89         if (!connected) {
90             sendInitialHeaders();
91             connected = true;
92         }
93     }
94
95     public void sendFrame() throws IOException, InterruptedException {
96         if (isSnapshotBased) {
97             sendSnapshotBasedFrame(fifo.take());
98         } else if (connected) {
99             output.write(fifo.take());
100         }
101     }
102
103     private void sendInitialHeaders() {
104         response.setContentType(contentType);
105         response.setHeader("Access-Control-Allow-Origin", "*");
106         response.setHeader("Access-Control-Expose-Headers", "*");
107     }
108
109     public void close() {
110         try {
111             output.close();
112         } catch (IOException e) {
113         }
114     }
115 }