How to stream RTSP on the web using web sockets and canvas

Some time ago we bought a shiny “new” IP camera:

Reviews were mixed but we found a pretty good deal and we decided to go for it.

“Not a cloud camera” and it’s also discontinued by Foscam, lol

The primary purpose of this camera would be to stream the view from our B&B in Gozo, Malta. So I connected the webcam to our wifi network, installed the Foscam app and found out two things:

  1. The webcam has a dynamic DNS client built-in 👍
  2. It streams using RTSP 👎

The dyndns client is great because, once the right port is forwarded, you can connect to the webcam from wherever you are using the Foscam app and see from it, hear from it and move it around.

The app is definitely not bad

The RTSP stream sucks. To see the stream on a browser you would have to install a plugin that supports the protocol or some Flash-based solution. Both options go against the open, standardized web I’ve come to love throughout these years.

Being the lazy dog I am, my first course of action was to Google “convert RTSP to WebM” 😎. Turns out it’s a lot more complicated than that. RTSP is a streaming protocol based on RTP which, in turns, is a protocol to encapsulate chunks of data, no matter their format. WebM is just a media format backed by the VP8/9 video codec. Long story short, it’s not important what comes out of the pipe as much as actually having the pipe, i.e. something that speaks and translates RTSP into something “better”.

It became clear that a client-only solution wasn’t viable. I needed an intermediary to convert the RTSP stream into a more “web-friendly” stream. I immediately thought of using web sockets and node and tried Googling “node RTSP web sockets stream”.

Bingo!

I found node-rtsp-stream, a neat little npm package that allows to convert a RTSP stream into a MPEG-TS stream, over web sockets, that’s compatible with jsmpeg. Jsmpeg, in turn, is a Javascript library that allows to visualize such stream into a <canvas> element. Jsmpeg comes with its own web socket server but it only streams MPEG-TS, not RTSP.

So now we have the following:

webcam <-> node-rtsp-stream <-> jsmpeg <-> canvas

Are we done? Of course not!

Turns out that if the camera goes down node-rtsp-stream won’t try to reconnect. The solution to this problem is neither trivial nor too complicated. It just involves two more components: supervisord and a simple Python script to act as a watchdog.

I configured supervisor to keep the streaming server and the watchdog up. The watchdog simply checks the size of the server’s log every 5 seconds. If the log stops changing it’s because ther camera is down so it keeps restarting the server every 60 seconds until a connection is re-established. Easier than using ICMP or abusing socket.connect, to the exposed port, to see if the camera is up.

This whole thing is running well on a t2.micro AWS EC2 instance. I tried running it on a RaspberryPi 2 and an old Asus Eee 1005HA but neither was suitable for the task, the server being very CPU-intensive.

You can see the final result here: blueharbour.com.mt/webcam.

While the source code is available on GitHub: https://github.com/chpmrc/foscam_streamer.