nikhil-patil
#rtsp #hls #node-js #streaming #ffmpeg

Streaming Live RTSP Camera Feeds in a Web Browser with Node.js and HLS

How I set up a media server to convert RTSP camera streams into HLS for playback in a React backoffice

5 min read min read

The Problem

IP cameras output RTSP streams. Browsers can't play RTSP natively. We needed to display live warehouse camera feeds inside a React backoffice.

The Solution: RTSP → FFmpeg → HLS → Browser

FFmpeg converts the RTSP stream into HLS (HTTP Live Streaming) segments that any browser can play.

const { spawn } = require('child_process');
const path = require('path');

function startStream(cameraId, rtspUrl) {
  const outputDir = path.join(__dirname, 'streams', cameraId);
  
  const ffmpeg = spawn('ffmpeg', [
    '-i', rtspUrl,
    '-c:v', 'libx264',
    '-preset', 'ultrafast',
    '-tune', 'zerolatency',
    '-f', 'hls',
    '-hls_time', '2',
    '-hls_list_size', '3',
    '-hls_flags', 'delete_segments',
    path.join(outputDir, 'stream.m3u8')
  ]);

  ffmpeg.stderr.on('data', (data) => {
    console.log(`Camera ${cameraId}:`, data.toString());
  });

  return ffmpeg;
}

Playing HLS in React

import Hls from 'hls.js';
import { useEffect, useRef } from 'react';

export function CameraFeed({ cameraId }) {
  const videoRef = useRef(null);

  useEffect(() => {
    const hls = new Hls();
    hls.loadSource(`/streams/${cameraId}/stream.m3u8`);
    hls.attachMedia(videoRef.current);
    return () => hls.destroy();
  }, [cameraId]);

  return <video ref={videoRef} autoPlay muted />;
}

Key Settings for Low Latency

  • -preset ultrafast — reduces encoding time
  • -tune zerolatency — removes buffering optimizations that add delay
  • -hls_time 2 — 2 second segments keep latency low
  • -hls_list_size 3 — only keep last 3 segments in playlist