How to proxy a HTTP video stream to any amount of clients through a Spring Webserver

I am not sure what kind of source you are using for generating your video stream (live camera or a video file or youtube video or ..)

You can probably use StreamingResponseBody (requires Spring 4.2+). Refer following links

http://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/streaming-response-body/

http://shazsterblog.blogspot.in/2016/02/asynchronous-streaming-request.html

Try this -

    @GetMapping("/stream1")
        @ResponseBody
        public StreamingResponseBody getVidoeStream1(@RequestParam String any) throws IOException {
            /* do security check before connecting to stream hosting server */ 
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<Resource> responseEntity = restTemplate.exchange( "http://localhost:8080/stream", HttpMethod.GET, null, Resource.class );
            InputStream st = responseEntity.getBody().getInputStream();
            return (os) -> {
                readAndWrite(st, os);
            };


    }

private void readAndWrite(final InputStream is, OutputStream os)
            throws IOException {
        byte[] data = new byte[2048];
        int read = 0;
        while ((read = is.read(data)) > 0) {
            os.write(data, 0, read);
        }
        os.flush();
    }

It should work. You can write your own implementation of readAndWrite() depending on your requirements.

So, your spring proxy controller could be something like this...

@Controller
public class HttpStreamProxyController {

    @RequestMapping("/spring") 
    @ResponseBody
    public StreamingResponseBody  getSecuredHttpStream() {
       if (clientIsSecured) {
       //... Security information

    RestTemplate restTemplate = new RestTemplate();
    // get video stream by connecting to stream hosting server  like this
            ResponseEntity<Resource> responseEntity = restTemplate.exchange( "https://ur-to-stream", HttpMethod.GET, null, Resource.class );
            InputStream st = responseEntity.getBody().getInputStream();
    // Or if there is any other preferred way of getting the video stream use that. The idea is to get the video input stream    

    // now return a StreamingResponseBody  object created by following lambda 
            return (os) -> {
                readAndWrite(st, os);
            };

       } else {
          return null;
       }

   }
}

The StreamingResponseBody returned by your rest endpoint will work fine with HTML5, which could be something like ..

<video width="320" height="240" controls>
  <source src="/spring" type="video/mp4">
  Your browser does not support the video tag
</video>

I struggled with this same problem for days, attempting to migrate my node app to Spring/React. I'm using a raspberry pi running Motion, which acts as a remote stream server that I was previously able to use the node module mjpeg-proxy to easily proxy the way OP wishes. This thread was the most useful set of examples in my search to do this in Java, however none of the examples quite worked for me. I'm hoping my controller class and component will help others attempting the same.

Please take note of response.setContentType("multipart/x-mixed-replace; boundary=BoundaryString"); as the boundary=BoundaryString portion was the last critical piece. I opened the chrome debugger while connecting directly to the stream and this was in the response headers -- when I copied it, suddenly everything worked!


import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.net.URI;

@RestController
@RequestMapping(value = "/video")
public class VideoController {

    Logger log = LoggerFactory.getLogger(VideoController.class);

    @RequestMapping("/oculus")
    public void oculus(HttpServletResponse response) {
        log.info("Calling /video/oculus...");
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.execute(
                URI.create("http://oculus:8081"),
                HttpMethod.GET,
                (ClientHttpRequest request) -> {},
                responseExtractor -> {
                    response.setContentType("multipart/x-mixed-replace; boundary=BoundaryString");
                    IOUtils.copy(responseExtractor.getBody(), response.getOutputStream());
                    return null;
                }
        );
    }

    @RequestMapping("/door")
    public void door(HttpServletResponse response) {
        log.info("Calling /video/door...");

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.execute(
                URI.create("http://vox:9002"),
                HttpMethod.GET,
                clientHttpRequest -> {
                    clientHttpRequest.getHeaders().add(HttpHeaders.AUTHORIZATION, "Basic blahblahBase64encodedUserAndPass=");
                },
                responseExtractor -> {
                    response.setContentType("multipart/x-mixed-replace; boundary=BoundaryString");
                    IOUtils.copy(responseExtractor.getBody(), response.getOutputStream());
                    return null;
                }
        );
    }

}

I have included examples for using Motion's basic auth, as well as an unprotected stream.

Both streams are then available on my app using the following bit of react js


class Video extends React.Component{
    render() {
        return (
            <div className="Content">
                <p>
                    <img className="stream" src="/video/oculus"/>
                </p>
                <p>
                    <img className="stream" src="/video/door"/>
                </p>
            </div>
        );
    }
}

export default Video;```

A good way to do video streaming in a Spring Boot application (I used a different source, however — a USB camera):

@GetMapping(value = "/stream")
public ResponseEntity<StreamingResponseBody> stream() {
    StreamingResponseBody stream = outputStream -> {
        while (streaming.get()) {
            final var raw = streamImage.get();
            final var jpeg = convert(byte2Buffered(raw.getImageData(), raw.getImageWidth(), raw.getImageHeight()));
            outputStream.write(jpeg);
        }
        outputStream.flush();
    };
    final var headers = new HttpHeaders();
    headers.add("Access-Control-Allow-Origin", "*");
    headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
    headers.add("Content-Type", "multipart/x-mixed-replace;boundary=frame");
    headers.add("Expires", "0");
    headers.add("Pragma", "no-cache");
    headers.add("Max-Age", "0");
    return ResponseEntity.ok()
            .headers(headers)
            .body(stream);
}

This way you'll get a stream on URL endpoint, which you can embed into a web page using img HTML tag.