Commit Graph

713 Commits

Author SHA1 Message Date
Scott Lamb fb057309f5 slightly simplify local_start logic 2016-12-30 19:35:50 -08:00
Scott Lamb 14461fcad9 bugfix: only double length of first recording 2016-12-30 06:39:09 -08:00
Scott Lamb bca92fbf8d compilation fix for 32-bit arm 2016-12-30 06:35:10 -08:00
Scott Lamb 938d8a752f camera clock frequency correction
As described in design/time.md:

* get the realtime-monotonic once at the start of a run and use the
  monotonic clock afterward to avoid problems with local time steps

* on every recording, try to correct the latest local_time_delta at up
  to 500 ppm

Let's see how this works...
2016-12-29 21:05:57 -08:00
Scott Lamb a71f6e66d8 test the new local time logic
The test ensures it solves the problem of the initial buffering throwing off
the start time of the first segment.

Along the way, I tested and fixed the new TrailingZero flag; it wasn't being
set.
2016-12-29 17:14:36 -08:00
Scott Lamb c7443436a5 skip the first rotation
This is as described in design/time.md.
2016-12-29 13:07:25 -08:00
Scott Lamb cc297adc75 clean up Writer interface slightly 2016-12-29 12:33:34 -08:00
Scott Lamb d001e4893c new logic for calculating a recording's start time
This is as described in design/time.md. Other aspects of that design
(including using the monotonic clock and adjusting the durations to compensate
for camera clock frequency error) are not implemented yet. No new tests yet.
Just trying to get some flight miles on these ideas as soon as I can.
2016-12-28 20:56:08 -08:00
Scott Lamb 063708c9ab try again to fix time.md diagram
This time, I've given up on svg and am using png. The inline svg seems to be
totally stripped out by github's markdown->html conversion, and img links
don't work because .svg files are served with an incorrect Content-Type.
2016-12-26 21:41:19 -08:00
Scott Lamb 8ee44efcf2 try to fix some time.md formatting 2016-12-26 21:39:00 -08:00
Scott Lamb f8f7c755ff attempt to fix svg linking 2016-12-26 21:00:42 -08:00
Scott Lamb 5a6cd4e590 new design doc describing approach to time
This is more sophisticated than the current implementation. It's an attempt
to address the problems created by the 9 seconds/day of drift I'm seeing for
long-running streams.
2016-12-26 20:55:43 -08:00
Scott Lamb eee887b9a6 schema version 1
The advantages of the new schema are:

* overlapping recordings can be unambiguously described and viewed.
  This is a significant problem right now; the clock on my cameras appears to
  run faster than the (NTP-synchronized) clock on my NVR. Thus, if an
  RTSP session drops and is quickly reconnected, there's likely to be
  overlap.

* less I/O is required to view mp4s when there are multiple cameras.
  This is a pretty dramatic difference in the number of database read
  syscalls with pragma page_size = 1024 (605 -> 39 in one test),
  although I'm not sure how much of that maps to actual I/O wait time.
  That's probably as dramatic as it is due to overflow page chaining.
  But even with larger page sizes, there's an improvement. It helps to
  stop interleaving the video_index fields from different cameras.

There are changes to the JSON API to take advantage of this, described
in design/api.md.

There's an upgrade procedure, described in guide/schema.md.
2016-12-20 22:08:18 -08:00
Scott Lamb fee4141dc6 replace resource.rs with new http-entity crate
This crate is a slightly-more-polished and MIT-licensed version of
resource.rs. So far it has one advantage: running the tests doesn't
require RUST_TEST_THREADS=1.
2016-12-20 18:29:45 -08:00
Scott Lamb 86dd36d7a5 version the sqlite3 database schema
See guide/schema.md for instructions on upgrading past this commit.
2016-12-20 15:44:04 -08:00
Scott Lamb eb4221851e add some comments to Slices 2016-12-16 23:11:08 -08:00
Scott Lamb 8e499aa070 compile with stable Rust
The benchmarks now require "cargo bench --features=nightly". The
extra #[cfg(nightly)] switches in the code needed for it are a bit
annoying; I may move the benches to a separate directory to avoid this.
But for now, this works.
2016-12-09 22:04:35 -08:00
Scott Lamb d48a3e16a8 switch to ffmpeg with compile fix for Linux/arm
For the moment, this is my own fork of the ffmpeg crate on github with this
one fix:

a67d25cb85
2016-12-08 22:20:20 -08:00
Scott Lamb 1865427f75 fully implement json handling as in spec
This is a significant milestone; now the Rust branch matches the C++ branch's
features.

In the process, I switched from using serde_derive (which requires nightly
Rust) to serde_codegen (which does not). It was easier than I thought it'd
be. I'm getting close to no longer requiring nightly Rust.
2016-12-08 21:28:50 -08:00
Scott Lamb 678500bc88 stop using a couple unstable features
It would be nice to build on stable Rust. In particular, I'm hitting
compiler bugs in Rust nightly, such at this one:
https://github.com/rust-lang/rust/issues/38177
I imagine beta/stable compilers would be less problematic.

These two features were easy to get rid of:

* alloc was used to get a Box<[u8]> to uninitialized memory.
  Looks like that's possible with Vec.

* box_syntax wasn't actually used at all. (Maybe a leftover from something.)

The remaining features are:

* plugin, for clippy.
  https://github.com/rust-lang/rust/issues/29597
  I could easily gate it with a "nightly" cargo feature.

* proc_macro, for serde_derive.
  https://github.com/rust-lang/rust/issues/35900
  serde does support stable rust, although it's annoying.
  https://serde.rs/codegen-stable.html
  I might just wait a bit; this feature looks like it's getting close to
  stabilization.
2016-12-07 21:05:49 -08:00
Scott Lamb 474b5403a9 Merge pull request #6 from dolfs/rust
Enhancements to the prep.sh script
2016-12-06 20:56:28 -08:00
Scott Lamb 632358b039 test and fix If-Match handling
Seeking in Chrome 55 wasn't working. It apparently sends If-Match requests
with the correct etag, which Moonfire NVR was incorrectly responding to with
"Precondition failed" responses. Fix and test that.

I hadn't noticed the problem in earlier versions of Chrome. I think they were
using If-Range instead, which is already tested and working.
2016-12-06 19:17:46 -08:00
Scott Lamb 8df0eae567 add a basic test of Streamer, fix it
This test is copied from the C++ implementation. It ensures the timestamps are
calculated accurately from the pts rather than using ffmpeg's estimated
duration. The Rust implementation was doing the easy-but-inaccurate thing, so
fix that to make the test pass.

Additionally, I did this with a code structure that should ensure the Rust
code never drops a Writer without indicating to the syncer that its uuid is
abandoned. Such a bug essentially leaks the partially-written file, although a
restart would cause it to be properly unlinked and marked as such. There are
no tests (yet) that exercise this scenario, though.
2016-12-06 18:41:44 -08:00
Scott Lamb 3332f817c0 commit Cargo.lock
This is the best practice according to the Cargo FAQ:
http://doc.crates.io/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries
2016-12-02 21:50:37 -08:00
Scott Lamb d72feb79bb style: convert try!(...) to ...? in web.rs 2016-12-02 21:46:31 -08:00
Scott Lamb eb2dadd4f0 test and fix .mp4 generation code
* new, more thorough tests based on a "BoxCursor" which navigates the
  resulting .mp4. This tests everything the C++ code was testing on
  Mp4SamplePieces. And it goes beyond: it tests the actual resulting .mp4
  file, not some internal logic.

* fix recording::Segment::foreach to properly handle a truncated ending.
  Before this was causing a panic.

* get rid of the separate recording::Segment::init method. This was some of
  the first Rust I ever wrote, and I must have thought I couldn't loan it my
  locked database. I can, and that's more clean. Now Segments are never
  half-initialized. Less to test, less to go wrong.

* fix recording::Segment::new to treat a trailing zero duration on a segment
  with a non-zero start in the same way as it does with a zero start. I'm
  still not sure what I'm doing makes sense, but at least it's not
  surprisingly inconsistent.

* add separate, smaller tests of recording::Segment

* address a couple TODOs in the .mp4 code and add missing comments

* change a couple panics on database corruption into cleaner error returns

* increment the etag version given the .mp4 output has changed
2016-12-02 20:40:55 -08:00
Dolf Starreveld c739bd799e Corrections as requested in pull request 2016-12-01 01:33:49 -08:00
Dolf Starreveld 2b37c30dd3 - Fixed a left behind customization in the prep.sh script 2016-11-30 22:10:34 -08:00
Dolf Starreveld af7aa26709 - Fix prep.sh script to exit on test failure
- Add prep.sh capabilty to read custom configuration from file prep.config (ignored in .gitignore) to keep script pristine
2016-11-30 22:05:52 -08:00
Dolf Starreveld 7d15a54a47 Merge branch 'rust' of https://github.com/scottlamb/moonfire-nvr into rust 2016-11-30 21:48:24 -08:00
Scott Lamb 59051f960d Make tests not care about the machine's timezone 2016-11-30 11:17:46 -08:00
Scott Lamb b15ec58865 test behavior of dropped transactions
This addresses one of db.rs's TODOs. No surprises here.
2016-11-30 10:59:19 -08:00
Scott Lamb 32647e20f5 Fix error deleting a camera's last recordings
I found this while bringing db.rs's test coverage up to the old
moonfire-db-test.cc. I mistakenly thought that in SQLite, an ungrouped
aggregate on a relation with no rows would return a row with a null result of
the aggregate. Instead, it returns no rows. In hindsight, this makes more
sense; it matches what grouped aggregates (have to) do.
2016-11-30 10:41:25 -08:00
Scott Lamb bc604ae012 mention the brand new Rust branch in the README 2016-11-25 15:01:46 -08:00
Scott Lamb 0a7535536d Rust rewrite
I should have submitted/pushed more incrementally but just played with it on
my computer as I was learning the language. The new Rust version more or less
matches the functionality of the current C++ version, although there are many
caveats listed below.

Upgrade notes: when moving from the C++ version, I recommend dropping and
recreating the "recording_cover" index in SQLite3 to pick up the addition of
the "video_sync_samples" column:

    $ sudo systemctl stop moonfire-nvr
    $ sudo -u moonfire-nvr sqlite3 /var/lib/moonfire-nvr/db/db
    sqlite> drop index recording_cover;
    sqlite3> create index ...rest of command as in schema.sql...;
    sqlite3> ^D

Some known visible differences from the C++ version:

* .mp4 generation queries SQLite3 differently. Before it would just get all
  video indexes in a single query. Now it leads with a query that should be
  satisfiable by the covering index (assuming the index has been recreated as
  noted above), then queries individual recording's indexes as needed to fill
  a LRU cache. I believe this is roughly similar speed for the initial hit
  (which generates the moov part of the file) and significantly faster when
  seeking. I would have done it a while ago with the C++ version but didn't
  want to track down a lru cache library. It was easier to find with Rust.

* On startup, the Rust version cleans up old reserved files. This is as in the
  design; the C++ version was just missing this code.

* The .html recording list output is a little different. It's in ascending
  order, with the most current segment shorten than an hour rather than the
  oldest. This is less ergonomic, but it was easy. I could fix it or just wait
  to obsolete it with some fancier JavaScript UI.

* commandline argument parsing and logging have changed formats due to
  different underlying libraries.

* The JSON output isn't quite right (matching the spec / C++ implementation)
  yet.

Additional caveats:

* I haven't done any proof-reading of prep.sh + install instructions.

* There's a lot of code quality work to do: adding (back) comments and test
  coverage, developing a good Rust style.

* The ffmpeg foreign function interface is particularly sketchy. I'd
  eventually like to switch to something based on autogenerated bindings.
  I'd also like to use pure Rust code where practical, but once I do on-NVR
  motion detection I'll need to existing C/C++ libraries for speed (H.264
  decoding + OpenCL-based analysis).
2016-11-25 14:34:00 -08:00
Scott Lamb 0f75e4f94a fix a couple README formatting mistakes 2016-11-25 13:03:58 -08:00
Scott Lamb 98720911c7 update the README to reflect the SQLite schema 2016-11-25 13:00:27 -08:00
Scott Lamb 7c5a90b8fb Merge pull request #5 from zetafunc/patch-1
Fix for Ubuntu 16.04.01
2016-11-25 11:45:22 -08:00
zetafunc 350abbb97f Fix for Ubuntu 16.04.01 2016-11-25 22:58:50 +11:00
Scott Lamb b50050358c read-only operation, database locking
These are meant to ease safe side-by-side testing with the upcoming Rust
implementation.
2016-11-12 11:57:44 -08:00
Scott Lamb 9f087eefa9 be more forgiving of out-of-date profiles 2016-11-12 11:49:31 -08:00
Scott Lamb 60f0ffc970 Upgrade to latest googletest/googlemock release
The old release on googlecode.com now 404s, so out-of-the-box builds were
broken. The releases on github have a slightly different file structure, so it's
more than just a change of URL. I upgraded from 1.7.0 to 1.8.0 in the process.
2016-11-12 11:40:18 -08:00
Scott Lamb 6b6137f8e7 fixes to mp4 generation
* typo: the subtitle should use its own mdhd, not alias the video one
* use 64-bit ints for the edit lists; the 32-bit values overflow at 13.25 hours
* use etags that reflect the edit list
2016-10-18 20:28:25 -07:00
Scott Lamb 0aadf227c1 Benchmark & speed up SampleIndexIterator
I'm seeing what is possible performance-wise in the current C++ before
trying out Go and Rust implementations.

* use the google benchmark framework and some real data.

* use release builds - I hadn't done this in a while, and there were a
  few compile errors that manifested only in release mode. Update the
  readme to suggest using a release build.

* optimize the varint decoder and SampleIndexIterator to branch less.

* enable link-time optimization for release builds.

* add some support for feedback-directed optimization. Ideally "make"
  would automatically produce the "generate" build outputs with a
  different object/library/executable suffix, run the generate
  benchmark, and then produce the "use" builds. This is not that fancy;
  you have to run an arcane command:

  alias cmake='cmake -DCMAKE_BUILD_TYPE=Release'
  cmake -DPROFILE_GENERATE=true -DPROFILE_USE=false .. && \
  make recording-bench && \
  src/recording-bench && \
  cmake -DPROFILE_GENERATE=false -DPROFILE_USE=true .. && \
  make recording-bench && \
  perf stat -e cycles,instructions,branches,branch-misses \
      src/recording-bench --benchmark_repetitions=5

  That said, the results are dramatic - at least 50% improvement. (The
  results weren't stable before as small tweaks to the code caused a
  huge shift in performance, presumably something something branch
  alignment something something.)
2016-05-19 22:53:23 -07:00
Scott Lamb d083797e42 Coalesce adjacent recordings for efficiency 2016-05-10 17:37:53 -07:00
Scott Lamb b27df92cac {start,end}_time_usec should be ..._time_90k 2016-05-10 17:10:42 -07:00
Scott Lamb 3aac88aa35 Fixes to design doc markdown. 2016-05-03 05:20:23 -07:00
Scott Lamb 7bdaf161cf Support limiting the range when listing recordings
Now it's possible to quickly determine what calendar days have data and then
query recordings for just the day(s) of interest with their returned
{start,end}_time_usec.
2016-05-03 05:17:06 -07:00
Scott Lamb d07ecc877b Remove a TODO that's been done. 2016-05-02 19:43:56 -07:00
Scott Lamb cd1c536efe Export the calendar days map. 2016-05-02 08:38:52 -07:00