Skip to content

Repository files navigation

Open-Source Network Video Recorder (NVR), with Object Detection

Web application to monitor your IP Security Camera network, continuously record camera feeds to your computers harddrive, and monitor motion events through a web app. Features include:

✔️ No expensive hardware required, use your old computers & harddrives
✔️ Supports one or multiple cameras, with single, filtered, motion list
✔️ No cloud account required, self-contained
✔️ Enhance your Cameras motion detection with Object Detection tagging/filtering
✔️ Continuously monitors hardrives, deleting the oldest video segments when almost full

NOTE: This repo was developed with Reolink POE cameras, that provided a RTMP endpoint, and a API for motion detection. But can be develoed/extended for other IP cameras

Object Detection

In addition, if your cameras motion sensor triggers a detection, the app will take a still of the detection, and run a Object Detection process, to tag the still with the objects in the picture. You can then use these tags to filter and review your motion events. This is very useful to avoid false positives, like the sun going behind a cloud, or a rain shower.

Settings

Using the settings menu, you first select the disk you will be using to stream real-time video from the cameras, and if you want to use the auto-deletion featre to prevent the disk from filling up, then if you want to use the object detection feature. Then you can add your cameras


Repo structure

  • /server contains the typescript api server that runs forks the ffmpeg and object detection apps, stores state in a embeded database, and provides restful apis for the react frontend.
  • /ai/detector contains the python object detection app that uses YOLO models to detect objects in the images placed in a particular directory.
  • /src contains the react components that is built into the frontend browser app.

Install / Setup / Run

The benifit of this app, its, its open-source, and it can be installed on any comodity h/w running linux (a free o/s operating system), techincal savvy users should be able to get this working.

ROADMAP

See the ROADMAP.md for planned features and future development.

Build & Run Web App

Ensure you have nodejs (recommended version >= 22 LTS), python3, and ffmpeg (latest version) installed.

Clone this repo onto a Linux machine, then build the app by running these commands:

# install Node.js dependencies
npm i 

# build typescript server
npx tsc

# build frontend
npm run-script build

# install Python ML detector dependencies
# Create a virtual environment to avoid PEP 668 "externally-managed-environment" errors
# (Modern Linux distributions prevent direct pip installs to the system Python)
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e ./ai
deactivate

To manually run the server

PATH="$PWD/.venv/bin:$PATH" LOG_LEVEL=info node ./lib/index.js

Set LOG_LEVEL to debug for verbose logging, or error for minimal output.

Note: The PATH prefix ensures that when the Node server spawns python3, it uses the Python from the virtual environment (.venv) instead of the system Python.

Then open a browser and navigate to http://<hostname>:8080. You are free to use a proxy like nginx and add TLS/DNS, authentication, then expose your app to the internet so you can monitor your home when away

Object Detection with YOLO11n ONNX

The app includes a YOLO11n ONNX model at ./ai/model/yolo11n.onnx for real-time object detection. To use it:

  1. Install Python dependencies (see build instructions above)
  2. In the settings panel, enable ML detection
  3. Configure the ML model (defaults to YOLO11n)
  4. Optionally set a custom frames path for extracted images
  5. Configure object detection labels to filter (e.g., ignore "car" detections)

The detector will automatically run when motion is detected, tagging objects found in the frames.

Movement Status Indicators

Each movement in the list displays a small status icon next to the duration, indicating its processing and detection state:

Icon Color Meaning
🕐 Clock Gray Waiting to process (pending)
⏳ Spinner Blue Currently processing/extracting frames
✓ Checkmark Green Detection complete (objects found)
⊟ Scan Gray Complete, no objects detected
✕ X Red Processing failed (hover for error details)

Hover over any icon for a tooltip with additional details.

To run the server each time the machine starts

To start automatically on boot, enable the systemd user service:

# Create the service (already provided at ~/.config/systemd/user/open-source-nvr.service)
systemctl --user daemon-reload
systemctl --user enable --now open-source-nvr

# Allow the service to start at boot (not just at login) — requires sudo once
sudo loginctl enable-linger $USER

Useful commands:

systemctl --user status open-source-nvr     # check status
systemctl --user restart open-source-nvr    # restart after code changes
systemctl --user stop open-source-nvr       # stop
journalctl --user-unit=open-source-nvr -f     # view logs
journalctl --user-unit=open-source-nvr -n 100 --no-pager  # last 100 lines

After making code changes, rebuild and restart:

npx tsc && systemctl --user restart open-source-nvr

Reverse proxy with nginx

To serve the app behind nginx with SSL and basic auth:

  1. Create an nginx site config:
server {
    server_name home.example.com;

    location / {
        auth_basic "Authorised Users Only";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        proxy_read_timeout 300s;
    }

    location /.well-known {
        auth_basic off;
    }

    listen 80;
}
  1. Enable and reload:
sudo ln -s /etc/nginx/sites-available/open-source-nvr /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
  1. Add an SSL certificate (e.g. with Certbot):
sudo certbot --nginx -d home.example.com
  1. Create the password file for basic auth:
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd <username>

Example to create Logic Volume for the local files

To create a logical volume from a volume group storage pool, use the lvcreate command. Specify the size of the logical volume with the -L option, specify a name with the -n option, and pass in the volume group to allocate the space from.

sudo lvcreate -L 40G -n video-files ubuntu-vg

format:

sudo mkfs -t ext4 /dev/mapper/ubuntu--vg-video--files

mount:

mkdir /video
sudo mount /dev/mapper/ubuntu--vg-video--files  /video

ensure its always mounted:

sudo vi /etc/fstab
/dev/mapper/ubuntu--vg-video--files /video ext4 defaults 0 0

Video Format Info

The video encapsulation format used by all Reolink cameras is MP4. So the video's format we download via Reolink Client or Reolink app is MP4. But when using USB disk to backup videos directly from Reolink NVR, you could choose either H.264 or MP4 video files. - the compression type is H264

The player

https://videojs.com/

HTTP Live Streaming (HLS) is a widely used protocol developed by Apple that will serve your stream better to a multitude of devices. HLS will take your stream, break it into chunks, and serve it via a specialized playlist

Program Structure

Target ./server app structure

Each major program components should be separated in its own file, for example

  • www.ts has the web apis
  • sse-manager.ts has the SSE
  • disk-check.ts has all the disk cleaning logic
  • processor.ts has all the control logic

there can be a utils files that have helper functions if it makes the files easer to read/maintain

Processor logic

There should be a single control loop, that will be ran ever second the calls the following functions that are described below.

Each function can have have entry-criteria checked before running. I'd like these checks to be implemented clearly so the code will be easy to maintain.

Control Loop Functions

  • controllerDetector() - Manages ML detection process lifecycle (starts/stops Python detector)

  • Run Per-camera

    • controllerFFmpeg - starts/stops ffmpeg streaming process is in the appropriate desired state for each camera

      • entry-criteria: function isn't already running from previous loops, if it is, skip
    • controllerFFmpegConfirmation - this function is to ensure ffmpeg is running successfullly, and generating the expected output. if its not, indicate for ffmpeg to be restarted, that will be implemented in controllerFFmpeg

      • is successful, update canera status to successfulllyConfirmed (state can be in memory)
      • entry-criteria: function isn't already running from previous loops, & ffmpeg is suppose to be running, and only run the first time ffmpeg is started, and then every 5seconds
    • detectCameraMovement - if configured, detects movement from the cameras api & updates the movement database appropriate given the rules, and constructs the playlist file for that movement.

      • entry-criteria:
        • only run if the configured interval has elapsed, not on every loop (state can be in memory)
        • onlu run if secMovementStartupDelay after ffmpeg has been started or re-started & the been successfully confirme to prevent false detections during stream startup
    • triggerProcessMovement - if function will look to see if it has new movements to process, based on its processMovementPointer (the key of the movementdb where its currently processing) , if it has it will start a ffmpeg process using the playlist file from the movementdb entry, and process its output and send the frame information to the oML detection process, that inturn will update the movement with the objects, and move the pointer to this movement. when ffmpeg exits, it will close the movement, and check to see if there is another movment to process.

      • entry-criteria: function isn't already running from previous loops, if it is, skip.
  • sseKeepAlive - if clients are connected, send a keepalive

    • entry-criteria:
      • only run at 30second intervals (every 30th loop), not on every loop (state can be in memory)
  • clearDownDisk - - Removes old recordings when disk usage exceeds threshold

    • entry-criteria:
      • only run at configured interval seconds, not on every loop (state can be in memory)

Details on the triggerProcessMovement

I what to ensure that we don't have any unnecessary delays, for example if the camera api detects a movement, it should also attempt to run triggerprocessMovement using the same call as in the main loop, so it does not wait for the next periodic check (controller).

Some processes have effectively pipes so that the output of one process is fed into another process. For example the output of the ffmpeg frames capture process is piped through a filter to only capture the required frame data, then piped into the stdin of the image processing process that detects motion in the frames, then, in-turn is then piped into a process that updates the movement database with the accumulated movement information.

Database Connections

Maintain state in the following databases:

  • cameradb - Camera configurations
  • settingsdb - System settings
  • movementdb - Movement events - the key is time-based, based on the movement time detected

About

Open Source NVR

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages