2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ipcamera.internal.servlet;
15 import java.io.IOException;
16 import java.util.concurrent.ArrayBlockingQueue;
17 import java.util.concurrent.BlockingQueue;
19 import javax.servlet.ServletOutputStream;
20 import javax.servlet.http.HttpServletResponse;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
25 * The {@link StreamOutput} Streams mjpeg out to a client
27 * @author Matthew Skinner - Initial contribution
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;
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;
48 public StreamOutput(HttpServletResponse response, String contentType) throws IOException {
50 this.contentType = contentType;
51 this.response = response;
52 output = response.getOutputStream();
53 if (!contentType.isEmpty()) {
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";
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());
70 output.write(header.getBytes());
71 output.write(currentSnapshot);
72 output.write("\r\n".getBytes());
75 public void queueFrame(byte[] frame) {
78 } catch (IllegalStateException e) {
84 public void updateContentType(String contentType) {
85 this.contentType = contentType;
92 public void sendFrame() throws IOException, InterruptedException {
93 if (isSnapshotBased) {
94 sendSnapshotBasedFrame(fifo.take());
95 } else if (connected) {
96 output.write(fifo.take());
100 private void sendInitialHeaders() {
101 response.setContentType(contentType);
102 response.setHeader("Access-Control-Allow-Origin", "*");
103 response.setHeader("Access-Control-Expose-Headers", "*");
106 public void close() {
109 } catch (IOException e) {