Commercial NVRs and cloud-based streaming services often impose high latency, heavy resource overhead, or recurring subscriptions. If you have an IP camera (such as a Hikvision IPC-B120 with an RTSP stream) or a USB webcam (such as a Logitech BRIO), you can stream live video directly to any modern browser with low latency and zero plugins.
Architecture Overview
This lightweight pipeline converts video on-demand using three core components:
- FFmpeg: Pulls the RTSP feed or V4L2 webcam stream, encodes it as an MPEG-TS container with MPEG-1 video (
mpeg1video) and MP2 audio, and pipes stdout to websocat. - websocat: Listens as an on-demand WebSocket server. When a browser client connects, websocat launches FFmpeg and streams the video chunks over WebSocket. When the browser tab closes, websocat terminates FFmpeg automatically—consuming 0% CPU when idle!
- JSMpeg: A tiny JavaScript decoder that renders MPEG-1 video frames directly onto an HTML5
<canvas>element in the browser.
1. RTSP Camera Service (/etc/systemd/system/rtsp-ws.service)
Create a systemd service for your Hikvision or ONVIF RTSP camera:
[Unit]
Description=On-demand WebSocket->FFmpeg bridge for JSMpeg RTSP
After=network.target
[Service]
User=pi
Group=pi
ExecStart=/usr/local/bin/websocat \
--binary \
--exit-on-eof \
-s 127.0.0.1:10000 \
sh-c:'/usr/bin/ffmpeg -hide_banner -loglevel error -rtsp_transport tcp -i "rtsp://user:pass@192.168.1.100:554/Streaming/Channels/101" -f mpegts -codec:v mpeg1video -bf 0 -r 25 pipe:1'
Restart=on-failure
[Install]
WantedBy=multi-user.target
2. USB Webcam Service (/etc/systemd/system/webcam-ws.service)
Create a systemd service for a local V4L2 USB camera (e.g. Logitech BRIO) with ALSA microphone audio:
[Unit]
Description=On-demand WebSocket->FFmpeg bridge for Logitech Brio
After=network.target
[Service]
User=pi
ExecStart=/usr/local/bin/websocat \
--binary \
--exit-on-eof \
-s 127.0.0.1:10001 \
sh-c:'WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/user/1000 /usr/bin/ffmpeg -hide_banner -loglevel error \
-f v4l2 -framerate 30 -video_size 1920x1080 -i /dev/video0 \
-f alsa -ac 2 -ar 44100 -i default \
-f mpegts -codec:v mpeg1video -bf 0 -r 30 -codec:a mp2 pipe:1'
Restart=on-failure
[Install]
WantedBy=multi-user.target
Enable and start the services:
sudo systemctl daemon-reload
sudo systemctl enable --now rtsp-ws webcam-ws
3. Frontend HTML5 Player (player.html)
Serve this minimalist HTML5 page with JSMpeg to view the stream from any desktop or mobile browser:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Live Camera Stream (JSMpeg)</title>
<style>
html, body {
margin: 0; padding: 0;
width: 100vw; height: 100vh;
background: #000;
display: flex;
align-items: center;
justify-content: center;
}
.player-container {
width: 90vw;
max-width: 1280px;
max-height: 90vh;
}
.player-container canvas {
width: 100%;
height: auto;
display: block;
}
</style>
</head>
<body>
<div class="player-container">
<canvas id="videoCanvas"></canvas>
</div>
<script src="/jsmpeg.min.js"></script>
<script>
const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
const url = protocol + location.host + '/my-ws-prefix';
new JSMpeg.Player(url, {
canvas: document.getElementById('videoCanvas'),
autoplay: true,
audio: false
});
</script>
</body>
</html>