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