How to build a video transcoder with SpringBoot and FFMPEG!
Tags: java, springboot, ffmpeg, hikvision
Published: 2021-10-13 06:14:11

I recently started working on a surveillance camera system and I want to be able to display the collected videos on web pages.
At first glance, it seemed very easy, but I quickly realized that I would have to rack my brains!


## How it should work?

The camera is connected to an NVR (Numeric Video recorder) which has an API that allows to retrieve the configuration information and the video stream. By searching a little on the Web (Yes the documentation is difficult to access..), I discover that the communication protocol used by the NVR is RTSP (Real-Time Streaming Protocol). This is where I encounter the main problem! How to use this protocol in an HTML page that does not support it? My solution is to use a server that allows to transcode the video in a more known format (MP4) and an ultra standard protocol (Http). This will also allow me to hide the access identifiers to the camera by using my server as a proxy.

![Graph it should work](https://giboow.fr/static/images/post/proxy-transcoder-ffmpeg/graph-ffmpeg-transcoder.png)

## So how do we do it?

A great tool that is well known for doing video conversion is [FFMPeg](https://www.ffmpeg.org/), so I'm starting to look into how I can use it to convert RSTP. I quickly find a command line that works:

```bash
ffmpeg -y -loglevel level+info -n -re -acodec pcm_s16le -rtsp_transport tcp -i rtsp://user:passwd@192.168.1.200:554/ISAPI/Streaming/channels/101/live -vcodec copy -af asetrate=22050 -acodec aac -b:a 96k -nostdin myvideo.mp4
```

Then how to make a proxy with SpringBoot? It's very simple actually, you just have to use the [StreamingResponseBody](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBody.html) object. This allows to return an asynchronous request processing, where the application can write directly to the response output stream without blocking the rest of my API.

Finally, I just have to use FFMPEG in my controller to send the stream via my API. I could have used " Runtime.getRuntime().exec(" ffmpeg...) " but I couldn't figure out how to get my stream. Fortunately I found a magic library [Jaffree](https://github.com/kokorin/Jaffree) : "Jaffree stands for JAva FFmpeg and FFprobe FREE command line wrapper. Jaffree supports programmatic video production and consumption (with transparency)"

## The solution

Here is the final solution and how to relay a video stream from a HikVision camera so that the format is usable by an HTML page.

```java
import com.github.kokorin.jaffree.StreamType;
import com.github.kokorin.jaffree.ffmpeg.FFmpeg;
import com.github.kokorin.jaffree.ffmpeg.PipeOutput;

@RestController
@RequestMapping("/video")
@Log4j2
public class VideoController {
	@GetMapping(value = "/live.mp4")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> livestream(@PathVariable("id") Long tipperId) throws Exception {

		String rtspUrl = "rtsp://user:passwd@192.168.1.200:554/ISAPI/Streaming/channels/101/live";

        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(os -> {
                    FFmpeg.atPath()
                            .addArgument("-re")
                            .addArguments("-acodec", "pcm_s16le")
                            .addArguments("-rtsp_transport", "tcp")
                            .addArguments("-i", rtspUrl)
                            .addArguments("-vcodec", "copy")
                            .addArguments("-af", "asetrate=22050")
                            .addArguments("-acodec", "aac")
                            .addArguments("-b:a", "96k" )
                            .addOutput(PipeOutput.pumpTo(os)
                                    .disableStream(StreamType.AUDIO)
                                    .disableStream(StreamType.SUBTITLE)
                                    .disableStream(StreamType.DATA)
                                    .setFrameCount(StreamType.VIDEO, 100L)
                                     //1 frame every 10 seconds
                                    .setFrameRate(0.1)
                                    .setDuration(1, TimeUnit.HOURS)
                                    .setFormat("ismv"))
                            .addArgument("-nostdin")
                            .execute();
                });

    }
}
```

You will also have to modify the configuration of your SpringBoot application (application.properties file) to increase the timeout for asynchronous requests.

```html
spring.mvc.async.request-timeout = 3600000
```

You just have to call your API on the web page:

```html
<div class="video">
  <video width="100%" height="auto" controls autoplay muted loop *ngIf="event?.video">
    <source src="http://localhost:8080/video/live.mp4"
            type="video/mp4">
    Sorry, your browser doesn't support embedded videos.
  </video>
</div>
```

And see the result :

![Final result](https://giboow.fr/static/images/post/proxy-transcoder-ffmpeg/result-ffmpeg-transcoder.png)

That's it!

[Find the article in French on my personal website](https://giboow.fr/posts/comment-transcoder-une-video-avec-springboot-et-ffmpeg?utm_source=dev.to)