DEV Community

Cover image for RTSP to HLS and upload to AWS MediaPackage
Jonas Birmé for Eyevinn Video Dev-Team Blog

Posted on

RTSP to HLS and upload to AWS MediaPackage

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.

TP-Link Tapo C200 camera

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 and the RTSP address is then rtsp://<username>:<password>@ 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>@ \
  -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    
Enter fullscreen mode Exit fullscreen mode

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: "/",
Enter fullscreen mode Exit fullscreen mode

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

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) {
    }, 1000);
Enter fullscreen mode Exit fullscreen mode

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();
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"
Enter fullscreen mode Exit fullscreen mode

What we now have running can be illustrated with the diagram below.

Diagram of RTSP to HLS and upload to AWS

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>@ \ 
  -e MEDIAPACKAGE_URL=<ingesturl> \
  -p 8000:8000 eyevinntechnology/rtsphls
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)