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