Moonfire NVR, a security camera network video recorder
Go to file
Scott Lamb f3ddbfe22a track cumulative duration and runs
This is useful for a combo scrub bar-based UI (#32) + live view UI (#59)
in a non-obvious way. When constructing a HTML Media Source Extensions
API SourceBuffer, the caller can specify a "mode" of either "segments"
or "sequence":

In "sequence" mode, playback assumes segments are added sequentially.
This is good enough for a live view-only UI (#59) but not for a scrub
bar UI in which you may want to seek backward to a segment you've never
seen before. You will then need to insert a segment out-of-sequence.
Imagine what happens when the user goes forward again until the end of
the segment inserted immediately before it. The user should see the
chronologically next segment or a pause for loading if it's unavailable.
The best approximation of this is to track the mapping of timestamps to
segments and insert a VTTCue with an enter/exit handler that seeks to
the right position. But seeking isn't instantaneous; the user will
likely briefly see first the segment they seeked to before. That's
janky. Additionally, the "canplaythrough" event will behave strangely.

In "segments" mode, playback respects the timestamps we set:

* The obvious choice is to use wall clock timestamps. This is fine if
  they're known to be fixed and correct. They're not. The
  currently-recording segment may be "unanchored", meaning its start
  timestamp is not yet fixed. Older timestamps may overlap if the system
  clock was stepped between runs. The latter isn't /too/ bad from a user
  perspective, though it's confusing as a developer. We probably will
  only end up showing the more recent recording for a given
  timestamp anyway. But the former is quite annoying. It means we have
  to throw away part of the SourceBuffer that we may want to seek back
  (causing UI pauses when that happens) or keep our own spare copy of it
  (memory bloat). I'd like to avoid the whole mess.

* Another approach is to use timestamps that are guaranteed to be in
  the correct order but that may have gaps. In particular, a timestamp
  of (recording_id * max_recording_duration) + time_within_recording.
  But again seeking isn't instantaneous. In my experiments, there's a
  visible pause between segments that drives me nuts.

* Finally, the approach that led me to this schema change. Use
  timestamps that place each segment after the one before, possibly with
  an intentional gap between runs (to force a wait where we have an
  actual gap). This should make the browser's natural playback behavior
  work properly: it never goes to an incorrect place, and it only waits
  when/if we want it to. We have to maintain a mapping between its
  timestamps and segment ids but that's doable.

This commit is only the schema change; the new data aren't exposed in
the API yet, much less used by a UI.

Note that stream.next_recording_id became stream.cum_recordings. I made
a slight definition change in the process: recording ids for new streams
start at 0 rather than 1. Various tests changed accordingly.

The upgrade process makes a best effort to backfill these new fields,
but of course it doesn't know the total duration or number of runs of
previously deleted rows. That's good enough.
2020-06-09 16:17:32 -07:00
.cargo keep frame poiners in release mode 2019-07-20 15:33:12 -07:00
base upgrade deps 2020-04-19 22:19:57 -07:00
ci try upgrading travis-ci setup to xenial 2018-12-29 12:21:57 -06:00
db track cumulative duration and runs 2020-06-09 16:17:32 -07:00
design track cumulative duration and runs 2020-06-09 16:17:32 -07:00
guide track cumulative duration and runs 2020-06-09 16:17:32 -07:00
scripts upgrade copyright notices 2020-03-01 22:53:41 -08:00
src track cumulative duration and runs 2020-06-09 16:17:32 -07:00
ui-src Merge branch 'master' into new-schema 2020-06-03 15:47:10 -07:00
webpack overhaul HTTP serving and caching 2020-05-29 21:20:15 -07:00
.dockerignore Initial docker support (#55) 2018-03-25 21:03:02 -07:00
.eslintrc.json fix a couple compile errors in 422cd2a 2018-11-27 12:23:44 -08:00
.gitignore add concept of user/session permissions 2019-06-19 15:34:20 -07:00
.jsbeautifyrc Major refactoring of UI code, small UI changes. (#48) 2018-03-20 07:03:12 -07:00
.prettierrc.json Major refactoring of UI code, small UI changes. (#48) 2018-03-20 07:03:12 -07:00
.travis.yml fix js lint errors 2020-05-08 18:44:02 -07:00
AUTHORS upgrade copyright notices 2020-03-01 22:53:41 -08:00
Cargo.lock Merge branch 'master' into new-schema 2020-06-03 15:47:10 -07:00
Cargo.toml Merge branch 'master' into new-schema 2020-06-03 15:47:10 -07:00
Dockerfile many improvements to install docs/procedures 2019-07-10 00:56:43 -07:00
LICENSE.txt Initial commit, with basic functionality. 2016-01-01 22:06:47 -08:00
README.md add links to the wiki 2019-07-10 11:43:58 -07:00
moonfire.sublime-project Major refactoring of UI code, small UI changes. (#48) 2018-03-20 07:03:12 -07:00
package.json overhaul HTTP serving and caching 2020-05-29 21:20:15 -07:00
screenshot-small.png add a basic Javascript UI 2017-10-21 21:54:27 -07:00
screenshot.png add a basic Javascript UI 2017-10-21 21:54:27 -07:00
yarn.lock overhaul HTTP serving and caching 2020-05-29 21:20:15 -07:00

README.md

Introduction

Moonfire NVR is an open-source security camera network video recorder, started by Scott Lamb <slamb@slamb.org>. It saves H.264-over-RTSP streams from IP cameras to disk into a hybrid format: video frames in a directory on spinning disk, other data in a SQLite3 database on flash. It can construct .mp4 files for arbitrary time ranges on-the-fly. It does not decode, analyze, or re-encode video frames, so it requires little CPU. It handles six 1080p/30fps streams on a Raspberry Pi 2, using less than 10% of the machine's total CPU.

So far, the web interface is basic: a filterable list of video segments, with support for trimming them to arbitrary time ranges. No scrub bar yet. There's also no support for motion detection, no https/SSL/TLS support (you'll need a proxy server, as described here), and only a console-based (rather than web-based) configuration UI.

screenshot

This is version 0.1, the initial release. Until version 1.0, there will be no compatibility guarantees: configuration and storage formats may change from version to version. There is an upgrade procedure but it is not for the faint of heart.

I hope to add features such as salient motion detection. It's way too early to make promises, but it seems possible to build a full-featured hobbyist-oriented multi-camera NVR that requires nothing but a cheap machine with a big hard drive. I welcome help; see Getting help and getting involved below. There are many exciting techniques we could use to make this possible:

  • avoiding CPU-intensive H.264 encoding in favor of simply continuing to use the camera's already-encoded video streams. Cheap IP cameras these days provide pre-encoded H.264 streams in both "main" (full-sized) and "sub" (lower resolution, compression quality, and/or frame rate) varieties. The "sub" stream is more suitable for fast computer vision work as well as remote/mobile streaming. Disk space these days is quite cheap (with 3 TB drives costing about $100), so we can afford to keep many camera-months of both streams on disk.
  • decoding and analyzing only select "key" video frames (see wikipedia).
  • off-loading expensive work to a GPU. Even the Raspberry Pi has a surprisingly powerful GPU.
  • using HTTP Live Streaming rather than requiring custom browser plug-ins.
  • taking advantage of cameras' built-in motion detection. This is the most obvious way to reduce motion detection CPU. It's a last resort because these cheap cameras' proprietary algorithms are awful compared to those described on changedetection.net. Cameras have high false-positive and false-negative rates, are hard to experiment with (as opposed to rerunning against saved video files), and don't provide any information beyond if motion exceeded the threshold or not.

Documentation

Getting help and getting involved

Please email the moonfire-nvr-users mailing list with questions, or just to say you love/hate the software and why. You can also file bugs and feature requests on the github issue tracker.

I'd welcome help with testing, development (in Rust, JavaScript, and HTML), user interface/graphic design, and documentation. Please email the mailing list if interested. Pull requests are welcome, but I encourage you to discuss large changes on the mailing list or in a github issue first to save effort.