]> git.basschouten.com Git - openhab-addons.git/blob
c2a30bd9865e6231b1dae4a0ddcca72f5e5e6f0a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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         fifo.add(frame);
77     }
78
79     public void updateContentType(String contentType) {
80         this.contentType = contentType;
81         if (!connected) {
82             sendInitialHeaders();
83             connected = true;
84         }
85     }
86
87     public void sendFrame() throws IOException, InterruptedException {
88         if (isSnapshotBased) {
89             sendSnapshotBasedFrame(fifo.take());
90         } else if (connected) {
91             output.write(fifo.take());
92         }
93     }
94
95     private void sendInitialHeaders() {
96         response.setContentType(contentType);
97         response.setHeader("Access-Control-Allow-Origin", "*");
98         response.setHeader("Access-Control-Expose-Headers", "*");
99     }
100
101     public void close() {
102         try {
103             output.close();
104         } catch (IOException e) {
105         }
106     }
107 }