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;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * The {@link StreamOutput} Streams mjpeg out to a client
29 * @author Matthew Skinner - Initial contribution
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;
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;
51 public StreamOutput(HttpServletResponse response, String contentType) throws IOException {
53 this.contentType = contentType;
54 this.response = response;
55 output = response.getOutputStream();
56 if (!contentType.isEmpty()) {
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";
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());
73 output.write(header.getBytes());
74 output.write(currentSnapshot);
75 output.write("\r\n".getBytes());
78 public void queueFrame(byte[] frame) {
81 } catch (IllegalStateException e) {
82 logger.debug("FIFO buffer has run out of space:{}", e.getMessage());
88 public void updateContentType(String contentType) {
89 this.contentType = contentType;
96 public void sendFrame() throws IOException, InterruptedException {
97 if (isSnapshotBased) {
98 sendSnapshotBasedFrame(fifo.take());
99 } else if (connected) {
100 output.write(fifo.take());
104 private void sendInitialHeaders() {
105 response.setContentType(contentType);
106 response.setHeader("Access-Control-Allow-Origin", "*");
107 response.setHeader("Access-Control-Expose-Headers", "*");
110 public void close() {
113 } catch (IOException e) {