Casting RTMP streams using NGINX

I recently got my hands on a Google Home Hub - its remarkable how having a visual screen attached to the Google Assistant changes how interact with the whole assistant experience. But I'm also a huge fan of Home Assistant, the open source home automation system that allows you to merge all your walled gardens of the "Smart Home" into one UI/Automation System. As soon as I got my hands on the Home Hub I knew I wanted to get my son's baby cam casting to it.

Google Home Hub

I gave a talk at Full Stack Toronto a year ago about utilising Home Assistant and my number one bit of advice was to automate the things you repeat every day; that's what I wanted to do with the baby cam. Every night we put Jameson to bed, and get out an android tablet and put on his baby cam; what if I could automate our half of the bedtime routine? The baby cam isn't really a baby cam at all; it's a Unifi G3 Micro that's hooked up to our IP CCTV system. Unifi Video has apps for all platforms and offers RTSP, RTMP and RTMPS streams; as well as being available remotely without having to open up ports in our firewall; its pretty neat but you still have to login to the app every night (the account is 2FA protected) and balance a tablet on a side table. The Google Home Hub was designed to just sit there on the side.

Ignoring the other elements of our part of his bedtime routine (playing a particular playlist on his Google Home, settings lights to dim etc) I wanted to get his baby cam's stream casting to the Home Hub without having to pick anything up, without having to login etc. Now, you can do all of this using Google's ecosystem approved hardware and apps. As far as I'm aware the only cameras that can "cast" to a cast-enabled device today are the Arlo & Nest cameras - well neither of these fulfil what I want from a CCTV system so they were out of the running and I already had the Unifi system surrounding the house. This is why I'm a huge fan of Home Assistant - I get to pick and choose which devices I use and write middleware in-between them to join everything up.

Here's the end result of cast-enabling the Unifi G3 Micro.


How?

To be able to cast a video stream that stream has to be in a particular format; you can find this information in the Cast Docs. But most importantly for me; the RTMP protocol and the media that Unifi Video was already outputting was a near perfect match for casting. The video and audio were encoded using H264 and AAC - exactly what the cast enabled device wanted; the only thing it didn't like was the RTMP protocol; it needed to be streamed using HTTP streaming technologies - two of which are MPEG-DASH and HLS. If you know how IP media works at all you'll know that these are just containers around the actual media - I didn't need to transcode the media from one format to another - only change the container delivering it - luckily NGINX gives us a module to do exactly that.Not having to transcode the media meant it was a low cost CPU action. So now I'm running an nginx docker container within my home network that converts RTMP to both DASH and HLS (why not get it to convert to both?!)

Lets check out the nginx config.

That's it! You pull in the rtmp streams using the pull keyword and make sure to make it a static stream otherwise nginx won't pull it until another RTMP user tries to access the RTMP stream through nginx - that's never going to happen here because we only care about pulling via HLS or DASH so you have to tell nginx to just pull it all of the time. The Access-Control-Allow-Origin headers are to enable the media to be cast; it's a requirement of the cast SDK. The /stat path just lets me view the status of all my streams. This absolutely shows you if you care about bandwidth (I don't, its inside my network) you shouldn't go down the static route I have.

Streams stats screen

This then lets me cast the resulting HLS or DASH urls which end up looking something like http://<ip-of-nginx>:8080/hls/foo.m3u8 and http://<ip-of-nginx>:8080/dash/bar.mpd depending on which technology you use. Notice you can have multiple RTMP streams and output each into its own HLS or DASH stream.

I'll cover how to cast these streams using Home Assistant and Google Assistant/Alexa in another post.