Files
grown/RTSP_STREAMING_SETUP.md
2026-03-11 00:06:51 +01:00

11 KiB

RTSP to HLS Streaming in SvelteKit

This guide walks you through setting up real-time RTSP stream conversion to HLS format in your SvelteKit application.

What's Included

  • Backend: FFmpeg-based RTSP to HLS converter running on Node.js
  • Frontend: SvelteKit component with HLS.js video player
  • API: RESTful endpoints to control streams

Prerequisites

System Requirements

  • Node.js: 18 or higher
  • FFmpeg: Must be installed and in your PATH
  • Package Manager: npm or bun

Install FFmpeg

macOS

brew install ffmpeg

Ubuntu/Debian

sudo apt-get update
sudo apt-get install ffmpeg

Windows (Chocolatey)

choco install ffmpeg

Windows (Scoop)

scoop install ffmpeg

Verify Installation

ffmpeg -version

Installation

1. Install Dependencies

npm install
# or with bun
bun install

This installs hls.js for browser-based HLS playback.

2. Project Structure

The setup creates the following files:

src/
├── lib/
│   ├── server/
│   │   └── streaming.ts          # FFmpeg stream manager
│   └── components/
│       └── RTSPVideoPlayer.svelte # Video player component
├── routes/
│   ├── +page.svelte              # Home page
│   └── api/
│       └── stream/
│           └── +server.ts        # Stream API endpoints
└── ...

static/
└── hls/                           # HLS playlists & segments (auto-created)

3. Run Development Server

npm run dev

Open http://localhost:5173 in your browser.

Usage

Web Interface

  1. Stream ID: Enter a unique identifier (e.g., "camera-1")
  2. RTSP URL: Enter your camera's RTSP stream URL
  3. Start Stream: Click to begin conversion and playback
  4. Stop Stream: Click to terminate the stream

Common RTSP URLs

Hikvision Cameras

rtsp://admin:password@192.168.1.100:554/stream
rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101

Dahua Cameras

rtsp://admin:password@192.168.1.100:554/live

Generic IP Cameras

rtsp://user:password@camera-ip:554/stream
rtsp://camera-ip:554/stream1

API Reference

Start Stream

Request:

curl -X POST http://localhost:5173/api/stream \
  -H "Content-Type: application/json" \
  -d '{
    "action": "start",
    "streamId": "camera-1",
    "rtspUrl": "rtsp://192.168.1.100:554/stream"
  }'

Response:

{
  "playlistUrl": "/hls/camera-1.m3u8"
}

Stop Stream

Request:

curl -X POST http://localhost:5173/api/stream \
  -H "Content-Type: application/json" \
  -d '{
    "action": "stop",
    "streamId": "camera-1"
  }'

Response:

{
  "success": true
}

Get Stream Status

Request:

curl -X POST http://localhost:5173/api/stream \
  -H "Content-Type: application/json" \
  -d '{
    "action": "status",
    "streamId": "camera-1"
  }'

Response:

{
  "streamId": "camera-1",
  "rtspUrl": "rtsp://192.168.1.100:554/stream",
  "startedAt": "2024-01-15T10:30:45.123Z",
  "isRunning": true,
  "playlistUrl": "/hls/camera-1.m3u8"
}

List All Streams

Request:

curl -X POST http://localhost:5173/api/stream \
  -H "Content-Type: application/json" \
  -d '{"action": "list"}'

Response:

{
  "streams": [
    {
      "streamId": "camera-1",
      "rtspUrl": "rtsp://192.168.1.100:554/stream",
      "startedAt": "2024-01-15T10:30:45.123Z",
      "isRunning": true,
      "playlistUrl": "/hls/camera-1.m3u8"
    }
  ]
}

Configuration

FFmpeg Parameters

Edit src/lib/server/streaming.ts to adjust encoding parameters:

// Current defaults
'-hls_time', '10',           // Segment duration (seconds)
'-hls_list_size', '3',       // Number of segments to keep
'-preset', 'fast',           // Encoding speed
'-b:a', '128k',              // Audio bitrate

Low Latency Setup

For real-time applications, modify the FFmpeg arguments:

'-hls_time', '2',            // 2-second segments
'-hls_list_size', '5',       // Keep more segments
'-preset', 'ultrafast',      // Fastest encoding
'-flags', '+low_delay',      // Low-delay mode

High Quality Setup

'-crf', '23',                // Quality (0-51, lower=better)
'-b:v', '2500k',             // Video bitrate
'-c:a', 'aac',
'-b:a', '192k',              // Higher audio quality

GPU Acceleration (NVIDIA)

'-c:v', 'h264_nvenc',        // NVIDIA encoder
'-preset', 'fast',            // fast, medium, slow

Troubleshooting

"ffmpeg: command not found"

FFmpeg is not installed or not in your system PATH.

Solution: Reinstall FFmpeg and ensure it's in your PATH, then restart your terminal.

# Verify FFmpeg is accessible
which ffmpeg
ffmpeg -version

Stream won't connect

Check the following:

  1. RTSP URL is correct: Test with VLC player first
  2. Network connectivity: Ping the camera IP
  3. Firewall rules: Ensure port 554 (default RTSP) is open
  4. Camera credentials: Verify username/password in URL
  5. FFmpeg logs: Check browser console and terminal output

"Playlist not found" Error

This usually means FFmpeg hasn't created the HLS segments yet.

Solution: Increase the wait time in the component:

// In RTSPVideoPlayer.svelte, startStream function
await new Promise((resolve) => setTimeout(resolve, 3000)); // Increase from 1000 to 3000

Video won't play in Safari

HLS.js may have issues with some configurations.

Solution: Check that the HLS.js library is loaded:

// Verify HLS.js is available
if (typeof window !== 'undefined' && !(window as any).HLS) {
  console.error('HLS.js not loaded');
}

High CPU Usage

The FFmpeg process is using too many resources.

Solution: Use a faster preset or reduce resolution:

// Use ultrafast preset
'-preset', 'ultrafast',

// Or reduce resolution
'-vf', 'scale=1280:720',

High Latency / Buffering

Segments are taking too long to generate or playback is laggy.

Solutions:

  1. Reduce segment duration to 2-5 seconds
  2. Enable low-latency mode
  3. Check network bandwidth
  4. Reduce video resolution
  5. Close other CPU-intensive applications

Browser Support

Browser HLS Support Notes
Chrome ✓ HLS.js Full support via HLS.js library
Firefox ✓ HLS.js Full support via HLS.js library
Safari ✓ Native Native HLS support
Edge ✓ HLS.js Chromium-based, full support
Mobile Chrome ✓ HLS.js Full support
Mobile Safari ✓ Native Native HLS support
Opera ✓ HLS.js Full support

Security Best Practices

1. Environment Variables for Credentials

Never hardcode camera credentials in your code.

// Load from environment
const rtspUrl = `rtsp://${process.env.CAMERA_USER}:${process.env.CAMERA_PASS}@${process.env.CAMERA_IP}:554/stream`;

Create a .env.local file:

CAMERA_USER=admin
CAMERA_PASS=password
CAMERA_IP=192.168.1.100

2. Restrict API Access

Implement authentication on the /api/stream endpoint:

// src/routes/api/stream/+server.ts
export async function POST({ request, locals }) {
  // Check authentication
  if (!locals.user) {
    return json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Continue with stream logic
}

3. Network Security

  • Use HTTPS in production
  • Restrict camera access to internal network only
  • Use VPN for remote access
  • Implement IP whitelisting

4. Process Management

FFmpeg runs with server privileges. Ensure:

  • Minimal file system access
  • Process limits to prevent DoS
  • Regular monitoring and logging

Performance Optimization

1. Adaptive Bitrate

Implement multiple quality levels:

// Start multiple streams at different resolutions
const streams = [
  { id: 'high', resolution: '1920:1080', bitrate: '5000k' },
  { id: 'medium', resolution: '1280:720', bitrate: '2500k' },
  { id: 'low', resolution: '640:360', bitrate: '1000k' }
];

2. Connection Pooling

For multiple concurrent streams, optimize memory usage:

// Limit concurrent streams
const MAX_STREAMS = 5;
if (activeStreams.size >= MAX_STREAMS) {
  return { error: 'Too many concurrent streams' };
}

3. Caching

Cache HLS segments on a CDN for better performance.

4. Hardware Acceleration

Use GPU encoding when available:

  • NVIDIA: h264_nvenc
  • Intel: h264_qsv
  • AMD: h264_amf

Production Deployment

Docker

Create Dockerfile:

FROM node:18-slim

RUN apt-get update && apt-get install -y ffmpeg && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["node", "build/index.js"]

Build and run:

docker build -t rtsp-hls-app .
docker run -p 3000:3000 \
  -e CAMERA_USER=admin \
  -e CAMERA_PASS=password \
  -e CAMERA_IP=192.168.1.100 \
  rtsp-hls-app

Docker Compose

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      CAMERA_USER: admin
      CAMERA_PASS: password
      CAMERA_IP: 192.168.1.100
    volumes:
      - ./static/hls:/app/static/hls
    restart: unless-stopped

Nginx Reverse Proxy

server {
  listen 443 ssl http2;
  server_name stream.example.com;

  ssl_certificate /etc/ssl/certs/cert.pem;
  ssl_certificate_key /etc/ssl/private/key.pem;

  location / {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }

  location /hls/ {
    alias /app/static/hls/;
    expires 1h;
    add_header Cache-Control "public, max-age=3600";
  }
}

Advanced Topics

Custom Video Filters

Add effects or transformations:

'-vf', 'scale=1280:720,fps=30,format=yuv420p'

Audio Processing

'-af', 'aresample=44100'  // Resample to 44.1kHz

Statistics and Monitoring

Log stream statistics:

ffmpegProcess.stdout.on('data', (data) => {
  console.log(`Stream stats: ${data}`);
});

Multiple Bitrate HLS (Adaptive)

Generate multiple quality versions:

// Create master playlist pointing to multiple variants
const masterPlaylist = `#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=5000000
high.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2500000
medium.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000
low.m3u8`;

Support & Resources

License

MIT