FFMPEG Frame grabber on RTSP stream IPcamera

Hi all,

I have tried to follow the link, But i have not find any answer.
I am also looking for the similar kind of solution.

can anybody guide me how to make it work using FFMPEG

Regards
Ramesh

At least in the past, the FFMPEG package didn't ship with the necessary libs for RTSP stream handling.

I ended up compiling by own binary, which is pretty much what your link is saying.

You don't really need to create the whole image, the binary is enough.

3 Likes

Hi Frollic,

Thanks for the reply.

I am new to work on openwrt.
Can you give me some guidance how can I do that?

Regards
Ramesh

And like I said, you don't really need the whole image, just the specific binary/package.

1 Like

Hi Frollic,

can you please give the details how to update the specific binary/package.
where can i find the package.

regards
ramesh

you can't,

you have to build it (and modify the current build params) yourself, that's the whole thing.

But I think the only config you need to enable/change is libffmpeg-full=y, in the config file.

I'm pretty sure I didn't use ffmpeg4, but ffmpeg.

1 Like

the main issue is that OpenWrt does not provide patented functionality enabled in official packages, so the only way is recompiling with that option enabled.

Might be cool to have a separate unofficial repository with pre-compiled packages, since this is a problem only for USA, while most other nations (like say the member states of EU) don't even recognize software patents as valid.

2 Likes

offshore repository, instead of offshore account :wink:

Some Linux distros do that, like Fedora and OpenSUSE, and Ubuntu also.

I also needed ffmpeg with common modules enabled to change the RTSP into a HLS stream to have:

  • Adaptive bandwidth that changes automatically
  • Browser support for Firefox, Safari and Chromium
  • Runs with minimal RAM and CPU (suitable for embedded devices)

The webserver uhttpd can be used to serve the HLS stream. It even supports TLS this way. I decided to have a subdirectory video for the HLS video stream.

Since my router has a CPU supported by Debian (TP-Link C2600, RPi should work as well) and enough storage for recordings I created a chroot environment there as well. I decided to use a chroot, because to update ffmpeg I can then use apt and the common features are enabled.

debootstrap --arch armhf bullseye /mnt/usbstick/debian

To reduce wear of flash-storage, the directory where the temporary HLS files are kept, is a RAM (tmpfs) folder.

To run ffmpeg in this chroot I use this script:

#!/bin/bash

#
# Script to convert RTSP to HLS with adaptive bandwidth
# My cameras support multiple h.264 streams. One stream is configured to 1M, the other to 256k bandwidth
#

#Put HLS output into $WEBFOLDER
WEBFOLDER="/www/video"

#ffmpeg is installed in this chroot
THECHROOT="/mnt/usbstick/debian"

#High resolution RTSP stream
STREAMHIGH="rtsp://user:password123@camera.lan:554/ISAPI/streaming/channels/101/?transportmode=unicast"

#Low resolution RTSP stream
STREAMLOW="rtsp://user:password123@camera.lan:554/ISAPI/streaming/channels/102/?transportmode=unicast"


########################################################################
function cleanup () {
	echo "cleaning up..."
	umount -l "$THECHROOT$WEBFOLDER"
	umount -l "$WEBFOLDER"
}
trap cleanup INT

cd

#is there a tmp-directory in the webroot?
mount | grep "none on $WEBFOLDER type tmpfs (rw,relatime)"
if [ $? -ne 0 ]; then
	echo "directory not mounted"
	mkdir -p "$WEBFOLDER"
	mount -t tmpfs none "$WEBFOLDER" || exit 1

	#download most recent hls.js if not present
	if [ ! -f "$WEBFOLDER/hls.js" ]; then
		wget "https://cdn.jsdelivr.net/npm/hls.js@latest" -O "$WEBFOLDER/hls.js" || exit 1
	fi

#this is a simple webpage with the HLS video.
#Safari plays it without Javascript, Firefox gets hls.js to support it
cat << 'EOM' > "$WEBFOLDER/index.html"
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<script src="hls.js"></script>
		<style>
			body {
				margin: 0;
				padding: 0;
			}
			video {
				height: 100vh;
				width: 100hw;
			}
		</style>
	</head>
	<body>
		<video id="video" controls autoplay>
			<source src="stream.m3u8" type="application/x-mpegURL">
		</video>
		<script>
		if (Hls.isSupported()) {
			var video = document.getElementById('video');
			
			var config = {
				autoStartLoad: true,
				startPosition: 30,
				backBufferLength: 60
			};
			var hls = new Hls(config);

			hls.loadSource('stream.m3u8');
			hls.attachMedia(video);

			//autoplay, many browsers are configured to only allow autoplay when muted
			hls.on(Hls.Events.MANIFEST_PARSED, function() {
				video.muted = true;
				video.volume = 0;
				video.play();
			});
		}
		</script>
	</body>
 </html>
EOM
fi

#Since the chroot needs to access the real webfolder, bind mount it
mount | grep "none on $THECHROOT$WEBFOLDER type tmpfs"
if [ $? -ne 0 ]; then
	echo "Bind mounting $THECHROOT$WEBFOLDER..."

	mkdir -p "$THECHROOT$WEBFOLDER"
	mount --bind "$WEBFOLDER" "$THECHROOT$WEBFOLDER" || exit 1
fi

# https://ffmpeg.org/ffmpeg.html#Advanced-options
# https://ffmpeg.org/ffmpeg-formats.html#hls-2
# -hide_banner: Hide the infos at startup
# -rtsp_transport tcp: Default is to use UDP, which reveals skipped packets in my network
# -i $STREAMHIGH: the input at high resolution
# -i $STREAMLOW: the input at low resultion 
# -codec copy: do not transcode, just remux - this is essential for embedded devices!
# -b:v:0 1024k Describe the bandwidth of video of the first input (index starts at 0)
# -b:v:1 256k  For the low resolution stream the bandwidth is about 256kBit/s
# -map 0:v include the videostream of the first input 
# -map 1:v include the videostream of second input as well
# -map 0:a for audio of first input (not used here)
# -copytb 1 Use the demuxer timebase
# -copyts copy timestamps from input to output
# -f hls output is HLS
# -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' select first video stream, name it "hd", assign a group + select second video stream, name it "sd", assign it to group
# -hls_time 5 each *.ts file has a length of ~5 seconds
# -hls_allow_cache 0 client must not cache the file
# -hls_list_size 10 limit the number of files to this value
# -hls_flags delete_segments+append_list+program_date_time : delete unreferenced (old) *.ts files, append new ts files to playlist, insert recorded date+time
# -hls_segment_filename '$WEBFOLDER/%v_file%d.ts' pattern for the *.ts filename
# -master_pl_name stream.m3u8 pattern for the the HLS master playlist
# -master_pl_publish_rate 10 publish a current master playlist after creating N *.ts segments
# '$WEBFOLDER/%v_stream.m3u8' the output for HLS playlists

#run ffmpeg inside the chroot
chroot "$THECHROOT" /bin/bash -c "ffmpeg -hide_banner \
                                  -rtsp_transport tcp -i $STREAMHIGH \
                                  -rtsp_transport tcp -i $STREAMLOW \
                                  -codec copy \
                                  -b:v:0 1024k -b:v:1 256k \
                                  -map 0:v -map 1:v \
                                  -copytb 1 -copyts \
                                  -f hls \
                                  -var_stream_map 'v:0,name:hd,agroup:my_stream v:1,name:sd,agroup:my_stream' \
                                  -hls_time 5 \
                                  -hls_allow_cache 0 \
                                  -hls_list_size 10 \
                                  -hls_flags delete_segments+append_list+program_date_time \
                                  -hls_segment_filename '$WEBFOLDER/%v_file%d.ts' \
                                  -master_pl_name stream.m3u8 \
                                  -master_pl_publish_rate 10 \
                                  '$WEBFOLDER/%v_stream.m3u8'"


exit 0

ffmpeg can be used to record to disk. Since there are a few *.ts files from past recordings, we can include videos of before we started the command, which is nice:

ffmpeg -hide_banner -loglevel info -live_start_index -99999 -i /www/video/hd_stream.m3u8 -c copy /storage/motion.mkv

The command will combine the HLS files and record what happened before the command was started. On SIGINT it closes the file and we have a nice recording reaching into the past before the recording was triggered.

HTH!