DEV Community

Cover image for How to build a video transcoder with SpringBoot and FFMPEG!
GiBoOw
GiBoOw

Posted on

How to build a video transcoder with SpringBoot and FFMPEG!

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

So how do we do it?

A great tool that is well known for doing video conversion is FFMPeg, so I'm starting to look into how I can use it to convert RSTP. I quickly find a command line that works:

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
Enter fullscreen mode Exit fullscreen mode

Then how to make a proxy with SpringBoot? It's very simple actually, you just have to use the StreamingResponseBody 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 : "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.

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();
                });

    }
}
Enter fullscreen mode Exit fullscreen mode

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

spring.mvc.async.request-timeout = 3600000
Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode

And see the result :

Final result

That's it!

Find the article in French on my personal website

Oldest comments (7)

Collapse
 
mathivanan1803 profile image
Mathi • Edited

Hi, i am a kind of noobie for spring boot. When i try to add Request param in this livestream() method it throws the error. I couldn't find out what i did wrong.
here I upload the image of my changes. Someone help me to solve this issue, Thank you..

dev-to-uploads.s3.amazonaws.com/up...
dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
giboow profile image
GiBoOw

Hi @mathivanan1803 have you tried to play your RTSP stream with VLC?
In your error log, I see that the RTSP stream is not recognized by FFMpeg.

Collapse
 
mathivanan1803 profile image
Mathi

Hello, GiBoOw. I found the solution, the error was some unwanted strings are added in the RTSP URL.
I have another question, when i try to run this code in my client machine it throws 401 unauthorized. Even i give the user name and password in the RTSP link it didn't work.
i cant find out what i did wrong, could you help me with this.
Thank you..

Thread Thread
 
giboow profile image
GiBoOw

On my NVR (Hikvision), I have to change the security configuration...
The RTSP auth option must be set to "digest", "basic/digest" is not working

Thread Thread
 
mathivanan1803 profile image
Mathi

Thanks for the reply @giboow , actually I can't check the configuration of camera , it on client side.. anyway I will try this solution.
Once again thanks for the solution.

Collapse
 
minhsu12113 profile image
Sự • Edited

Hey GiBoOw Can I use this example to capture rtsp stream? I want to take a photo from rtsp stream.

Collapse
 
giboow profile image
GiBoOw

You can probably change ffmpeg params and use "-vframes 1"