Skip to content
tpawley2001Public

About

Self-hosted Linux NVR — live HLS grid, AI motion detection, dual-stream recording, PTZ, LAN discovery

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Open-Iris

A self-hosted, Linux-first network video recorder (NVR) built as an open alternative to commercial NVR software. Designed to run on a home server or NAS and be accessible from any browser on your local network or over Tailscale.


Features

Live View

  • Tight auto-optimizing camera grid — columns adjust automatically as cameras are added or removed
  • Inline live sub-stream video in every tile (low-latency HLS)
  • Click any tile to open the full HD stream in a modal
  • Single-port architecture: all traffic (UI, API, HLS streams) flows through port 8099 — no firewall exceptions needed beyond one port

Recording

  • Sub-stream recording — always-on, continuous low-bitrate recording for every enabled camera, stored separately from HD clips
  • HD recording — two modes per camera:
    • motion — HD recording starts only when CodeProject.AI detects a configured label (person, car, etc.)
    • continuous — HD recording runs 24/7
  • Pre-roll — ring buffer keeps the last N seconds before motion so clips include context
  • Segmented clips — ffmpeg writes rolling .mp4 segments; configurable segment length per camera
  • Retention pruning — clips older than retention_days are automatically deleted per camera
  • Trigger labels — every recording event is tagged with what triggered it (person, car, continuous, etc.)
  • Audio transcoded to AAC (cameras that output unsupported codecs like pcm_mulaw are handled automatically)

AI Detection

  • Integrates with CodeProject.AI Server for on-device object detection
  • Polls camera snapshots on a configurable interval
  • Supports any label CodeProject.AI can recognize (person, car, dog, etc.)
  • Configurable confidence threshold and cooldown per camera
  • Webhook notifications on detection

Clips Browser

  • Filter by camera, date, and status
  • Every clip card shows the trigger label, timestamps, status badge, and download links
  • Inline video player
  • Delete individual recording events

Camera Management

  • Full CRUD via the web UI — no YAML editing required after initial setup
  • Supports dual streams (main + sub), snapshot URL, detection, PTZ, and recording config per camera

LAN Discovery

  • Scan any IPv4 CIDR for likely camera devices
  • Checks RTSP ports, ONVIF paths, and HTTP banners to identify cameras and guess RTSP/snapshot URLs
  • One-click "Add Camera" pre-fills the form from scan results

PTZ Control

  • Directional pad + zoom buttons in the live grid (hover to reveal)
  • Supports Hikvision and Dahua CGI protocol
  • PTZ base URL auto-inferred from the RTSP URL if not set

Architecture

IP Cameras (H.265 / H.264 RTSP)
        │
        ▼
  ffmpeg transcode   ──────────────────────────────────────────┐
  (H.264 @ 720p sub / 1080p HD)                                │
        │                                                       │
        ▼                                                 ffmpeg record
  MediaMTX RTSP receiver (:19554)                        (segmented MP4)
        │
        ▼
  MediaMTX HLS server (:18991)
        │
        ▼
  FastAPI /hls/{path} proxy (:8099)   ◄── all browser traffic
        │
        ▼
  Browser (HLS.js)
  • FastAPI (port 8099) serves the UI, REST API, and proxies HLS — one port for everything
  • MediaMTX handles RTSP ingest and HLS output (internal, not exposed to clients)
  • CodeProject.AI runs object detection on camera snapshots (port 32168, optional)
  • ffmpeg does codec transcoding and clip recording

Quick Start

Prerequisites

  • Linux host with Docker Engine and the Compose plugin
  • IP cameras that expose RTSP streams

1. Clone and configure

git clone https://github.com/t69broken/Open-Iris.git
cd Open-Iris

Copy the example files:

cp .env.example .env
cp app/cameras.example.yaml data/cameras.yaml

Edit .env to set a username and password (or leave both blank to disable auth):

BLUE_NVR_USERNAME=admin
BLUE_NVR_PASSWORD=yourpassword

Edit data/cameras.yaml with your camera details (see Camera Configuration below).

2. Start the stack

docker compose up -d --build

3. Open the dashboard

http://<host-ip>:8099
  • Live tab — live camera grid
  • Clips tab — recorded events browser
  • Cameras tab — add, edit, delete cameras
  • Discover tab — scan your LAN for cameras

Camera Configuration

Cameras are defined in data/cameras.yaml. The web UI writes to this file, but you can also edit it directly and reload (curl -X POST http://localhost:8099/api/reload).

Full field reference

cameras:
  - id: front-door              # URL-safe slug, must be unique
    name: Front Door            # Display name
    rtsp_url: rtsp://user:pass@192.168.1.50:554/h264Preview_01_main
    rtsp_url_sub: rtsp://user:pass@192.168.1.50:554/h264Preview_01_sub  # optional low-res stream
    snapshot_url: http://192.168.1.50/cgi-bin/snapshot.cgi              # optional, needed for AI detection
    enabled: true

    record:
      enabled: true
      mode: motion              # "motion" (AI-triggered) or "continuous" (always-on HD)
      segment_seconds: 60       # clip segment length in seconds (min 10)
      pre_roll_seconds: 5       # seconds of pre-roll to prepend (0 to disable)
      retention_days: 30        # auto-delete HD clips older than this (1–365)

    detect:
      enabled: true
      labels:                   # labels that trigger HD recording
        - person
        - car
      min_confidence: 0.50      # detection threshold (0.0–1.0)
      cooldown_seconds: 20      # keep recording this long after last match
      poll_interval_seconds: 3  # snapshot poll rate in seconds
      webhook_url:              # optional POST URL for motion events

    ptz:
      enabled: false
      type: hikvision            # "hikvision" or "dahua"
      base_url:                  # inferred from rtsp_url if omitted
      username:
      password:

Notes

  • If rtsp_url_sub is set, the sub-stream is used for the live grid tiles and continuous sub-stream recording. The main stream is used for HD recording only.
  • If snapshot_url is not set, Open-Iris uses ffmpeg to grab a frame from the RTSP stream for detection — slower but works with any camera.
  • Sub-stream recording runs continuously regardless of record.mode. The mode setting controls HD recording only.

External Storage

By default recordings are stored inside ./data/recordings/ (inside the project directory). To store clips on a separate drive:

  1. Update docker-compose.yml to add a volume mount:
volumes:
  - ./app:/app/app:ro
  - ./data:/data
  - /mnt/your-drive:/mnt/your-drive   # add this
  1. Update the environment variables:
environment:
  RECORDINGS_DIR: /mnt/your-drive/nvr/hd
  SUB_RECORDINGS_DIR: /mnt/your-drive/nvr/sub

Environment Variables

All variables are optional — defaults work for a standard install.

Variable Default Description
CAMERA_CONFIG_PATH /data/cameras.yaml Camera inventory file
MEDIAMTX_CONFIG_PATH /data/mediamtx.generated.yml Generated MediaMTX config path
MEDIAMTX_RTSP_ADDRESS :19554 MediaMTX RTSP receiver port
MEDIAMTX_HLS_ADDRESS :18991 MediaMTX HLS server port
MEDIAMTX_API_ADDRESS :19997 MediaMTX management API port
MEDIAMTX_RTSP_PUSH rtsp://127.0.0.1:19554 RTSP push target for ffmpeg transcoders
HLS_UPSTREAM http://127.0.0.1:18991 MediaMTX HLS address (used by the proxy)
CODEPROJECTAI_URL http://localhost:32168 CodeProject.AI server URL
RECORDINGS_DIR /data/recordings/hd HD clip storage directory
SUB_RECORDINGS_DIR /data/recordings/sub Sub-stream clip storage directory
EVENTS_INDEX_PATH /data/recordings-index.json HD recording event index
SUB_EVENTS_INDEX_PATH /data/sub-recordings-index.json Sub-stream event index
PREROLL_DIR /data/preroll Pre-roll ring buffer directory
DEFAULT_SCAN_CIDR 192.168.1.0/24 Default CIDR for LAN discovery
FFMPEG_BIN /usr/bin/ffmpeg ffmpeg binary path
RETENTION_CHECK_INTERVAL 3600 Seconds between retention prune runs
HEALTH_CHECK_INTERVAL 30 Seconds between camera health probes
BLUE_NVR_USERNAME (none) HTTP Basic Auth username
BLUE_NVR_PASSWORD (none) HTTP Basic Auth password

If both BLUE_NVR_USERNAME and BLUE_NVR_PASSWORD are set, the dashboard and API require HTTP Basic Auth. The /health, /static/, /hls/, and /recordings/ paths are always exempt so streams and clips load correctly in the browser.


Remote Access

The recommended remote setup is Tailscale:

  1. Install Tailscale on the Linux host and join it to your tailnet
  2. Access the dashboard using the host's Tailscale IP or MagicDNS name: http://<tailscale-host>:8099
  3. Keep all ports bound to the host — rely on Tailscale ACLs for access control

Only port 8099 needs to be reachable. All HLS streams are proxied through it.


Codec Notes

Open-Iris transcodes camera streams to H.264 before serving them via HLS (most cameras output H.265 which browsers can't decode natively). The ffmpeg transcoder:

  • Outputs 720p for live grid tiles ({id}-web)
  • Outputs 1080p for HD modal streams ({id}-web-hd)
  • Copies the main stream codec directly for recording (no re-encode — fast and lossless)
  • Transcodes audio to AAC for MP4 compatibility (cameras using pcm_mulaw or other unsupported codecs are handled automatically)

Limitations / Roadmap

  • Single-credential Basic Auth only (no multi-user or role-based permissions)
  • ONVIF not supported for PTZ — Hikvision and Dahua CGI only
  • LAN discovery is heuristic; ONVIF discovery is not implemented
  • No mobile push notifications (webhook integration works as a bridge)
  • No two-way audio
  • Timeline / scrubber playback not yet implemented

Project Layout

Open-Iris/
├── app/
│   ├── main.py                  # FastAPI backend — all server logic
│   ├── requirements.txt
│   ├── cameras.example.yaml     # camera config template
│   └── static/
│       ├── index.html
│       ├── app.js
│       ├── styles.css
│       └── hls.min.js           # HLS.js bundled locally
├── data/
│   ├── cameras.yaml             # your camera config (gitignored)
│   ├── mediamtx.generated.yml   # auto-generated at startup (gitignored)
│   ├── codeprojectai-appsettings.json
│   └── recordings/              # clip storage (gitignored)
├── docker-compose.yml
├── Dockerfile
├── .env                         # your credentials (gitignored)
└── .env.example                 # credentials template

License

MIT

About

Self-hosted Linux NVR — live HLS grid, AI motion detection, dual-stream recording, PTZ, LAN discovery

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages