RTSP to HLS and upload to AWS MediaPackage
Jonas Birmé
Posted on December 23, 2021
In this blog I will describe how I take an RTSP feed, transcodes to HLS and push HLS to AWS MediaPackage origin for Internet distribution.
In my example I have a TP-Link Tapo C200 camera which is a relatively cheap home security wifi camera. The audio and video stream from the camera is accessible using the RTSP transport protocol.
You configure the camera with the Tapo app and to enable access to the RTSP stream you need to remove the microSD card and set a camera account username and password. It is available under the Advanced settings.
Obtain the IP address to the camera under the camera settings. In this example we will assume it is 10.0.0.4
and the RTSP address is then rtsp://<username>:<password>@10.0.0.4/stream1
for the HQ stream.
Transcode and generate HLS
Once you have the stream you need to transcode it to HLS and in this example we are using ffmpeg and will create 3 variants (1080, 720 and 360).
ffmpeg -fflags nobuffer -rtsp_transport tcp \
-i rtsp://<username>:<password>@10.0.0.4/stream1 \
-max_muxing_queue_size 1024 \
-filter_complex "[0:v]split=3[v1][v2][v3];[v1]copy[v1out];[v2]scale=w=1280:h=720[v2out];[v3]scale=w=640:h=360[v3out]" \
-map [v1out] -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" \
-b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M \
-preset ultrafast -g 48 -sc_threshold 0 -keyint_min 48 \
-map [v2out] -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" \
-b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M \
-preset ultrafast -g 48 -sc_threshold 0 -keyint_min 48 \
-map [v3out] -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" \
-b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M \
-preset ultrafast -g 48 -sc_threshold 0 -keyint_min 48 \
-map a:0 -c:a:0 aac -b:a:0 256k -ar 48000 -ac 2 \
-map a:0 -c:a:1 aac -b:a:1 128k -ar 48000 -ac 2 \
-map a:0 -c:a:2 aac -b:a:2 128k -ar 48000 -ac 2 \
-f hls -hls_time 10 -hls_flags "independent_segments+delete_segments" \
-hls_segment_type mpegts \
-hls_segment_filename /media/hls/master_%v_%02d.ts \
-hls_list_size 6 -master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" /media/hls/master_%v.m3u8
We will have ffmpeg to write the output HLS to disk and to be able to serve the HLS to video players we will setup an HTTP server.
Serve HLS using NodeJS and Fastify
To serve the HLS we will create a NodeJS service based on fastify.
const server = fastify();
server.register(require("fastify-static"), {
root: "/media/hls", // this is where ffmpeg outputs the HLS
prefix: "/",
});
server.listen(8000);
The HLS will then be available at http://localhost:8000/master.m3u8
Upload HLS to AWS MediaPackage
To make the HLS available for streaming over Internet we will upload the HLS to AWS MediaPackage origin service. Follow the instructions on how to configure it for delivering live content.
We will use the @eyevinn/hls-pull-push NPM library to upload it to an AWS MediaPackage ingest endpoint. This library provides a service that pulls HLS from an endpoint and then push the HLS to an origin, and an output plugin for AWS MediaPackage is available.
Create an instance and register the MediaPackage output plugin.
const { HLSPullPush, MediaPackageOutput } = require("@eyevinn/hls-pull-push");
const pullPushService = new HLSPullPush();
const outputPlugin = new MediaPackageOutput();
pullPushService.registerPlugin("mediapackage", outputPlugin);
We need to wait for the HLS to be available before we can start to pull and push the HLS.
waitForHlsIsAvailable() {
return new Promise((resolve, reject) => {
let t = setInterval(() => {
const file = "/media/hls/master.m3u8";
fs.access(file, fs.constants.F_OK, (err) => {
if (!err) {
clearInterval(t);
resolve();
}
});
}, 1000);
});
}
Once we have the HLS available we can start the pull-push service and start a fetcher. A fetcher is the process that pulls and push the HLS.
await waitForHlsIsAvailable();
pullPushService.listen(8001);
const outputDest = outputPlugin.createOutputDestination({
ingestUrls: [{
url: <mediapackage-url>,
username: <mediapackage-username>,
password: <mediapackage-password>,
}], pullPushService.getLogger());
const source = new URL("http://localhost:8000/master.m3u8");
const sessionId = pullPushService.startFetcher({
name: "rtsp",
url: source.href,
destPlugin: outputDest,
destPluginName: "mediapackage"
});
outputDest.attachSessionId(sessionId);
What we now have running can be illustrated with the diagram below.
Docker Container
If you don't want to build this from scratch you can use our rtsphls
Docker container instead.
docker run --rm -e RTSP=rtsp://<username>:<password>@10.0.0.4/stream1 \
-e MEDIAPACKAGE_URL=<ingesturl> \
-e MEDIAPACKAGE_USERNAME=<username> \
-e MEDIAPACKAGE_PASSWORD=<password> \
-p 8000:8000 eyevinntechnology/rtsphls
The source code is available on our GitHub.
A live example with a feed from the view from our office is available here.
About Eyevinn Technology
Eyevinn Technology is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor.
At Eyevinn, every software developer consultant has a dedicated budget reserved for open source development and contribution to the open source community. This give us room for innovation, team building and personal competence development. And also gives us as a company a way to contribute back to the open source community.
Want to know more about Eyevinn and how it is to work here. Contact us at work@eyevinn.se!
Posted on December 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.