Commit Graph

141 Commits

Author SHA1 Message Date
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
Scott Lamb
1d55567384 Populate MoonfireDatabase::CameraData::days.
Also expose it via GetCameraRow.
2016-05-02 08:24:22 -07:00
Scott Lamb
28fa458982 A helper to find the bounds of a day. 2016-05-01 22:12:55 -07:00
Scott Lamb
292bcbaad5 Add helper for adjusting date-to-duration map.
The helper isn't used yet. The goal is to export this on /camera/<uuid>/ as
described in a TODO in design/api.md.

The next step is to keep MoonfireDatabase::CameraData::days up-to-date:

* Init: call on every recording (replacing the current aggregated query with
  a recording-by-recording query)
* InsertRecording, DeleteRecordings: call for added/removed recordings

then return it from GetCamera and pass it along to the client in
WebInterface::HandleJsonCameraDetail.
2016-05-01 21:26:17 -07:00
Scott Lamb
a7bfb00083 More logging tweaks. 2016-05-01 10:06:31 -07:00
Scott Lamb
cefbcedc9c Logging tweak. 2016-05-01 10:01:07 -07:00
Scott Lamb
52b6b8272c Remove a stray LOG(INFO). 2016-05-01 09:42:55 -07:00
Scott Lamb
3030e3fb32 Support trimming video segments.
* If the end of a segment is between samples, the last included sample will
  have a shortened duration.

* If the beginning of a segment not on a key frame (aka sync sample), the
  prefix will be included but trimmed using an edit list. (It seems like a
  ctts box might be able to accomplish the same thing, fwiw.)
2016-05-01 09:16:14 -07:00
Scott Lamb
713d7863de Improve warning message on open failure.
Before:

W0430 08:26:53.958887 41576 moonfire-nvr.cc:123]
driveway: Output error; sleeping before retrying: open
031e423c-2a0c-4450-b6cc-8af629606a90: Permission denied

After:

W0430 08:50:06.315666 43514 moonfire-nvr.cc:123]
driveway: Output error; sleeping before retrying: open
98592dfa-4ab4-427a-8ad0-033325f0f0b3 (within dir /home/slamb/moonfire/sample):
Permission denied
2016-04-30 08:51:58 -07:00
Scott Lamb
374975a73c On startup, ensure --sample_file_dir is writable. 2016-04-30 08:38:29 -07:00
Scott Lamb
33b24197c3 Merge pull request #3 from dolfs/prepfix
Prepfix
2016-04-30 07:31:57 -07:00
Dolf Starreveld
c562ccd3fa Fix oversight in prep.sh 2016-04-29 16:22:49 -07:00
Dolf Starreveld
f9135a263b Fixes to prep.sh 2016-04-29 16:20:42 -07:00
Scott Lamb
138db4f491 Compile fixes for Raspbian 8.
* gcc (Raspbian 4.9.2-10) 4.9.2 complains about -1 in const char[]s.
  gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010 was fine with this.
  Use '\xff' instead.

* libjsoncpp-dev 0.6.0~rc2-3.1 doesn't have Json::writeValue.
  Use an older interface instead.

* libre2-dev 20140304+dfsg-2 has a bug in which custom RE2 parsers don't
  compile because the relevant constructor is only declared, not defined as
  trivial. (This is fixed on my Ubuntu's libre2-dev 20150701+dfsg-2.)
  Avoid using this.
2016-04-25 04:54:36 -07:00
Scott Lamb
ff08118001 Support for timetamp subtitles.
I tested these in VLC and QuickTime. Both players appear to ignore the
as the track dimensions, track transformation matrix, box dimensions, and box
justification. I just left them at default values then.

Automated testing is minimal. There's a new test that the resulting .mp4
parses, but I didn't actually ensure correctness of the subtitles in any way.
2016-04-25 04:17:43 -07:00
Scott Lamb
5dd0dca51f Add a simple JSON API.
This is a work in progress. There are no tests yet.
2016-04-23 13:55:36 -07:00
Scott Lamb
8ab2edb970 Merge pull request #2 from dolfs/master
Updated README to explain and show use of "cameras.sql"
2016-04-23 10:20:46 -07:00
Dolf Starreveld
770fe1512a Updated README to explain and show use of "cameras.sql" 2016-02-09 00:02:07 -08:00
Scott Lamb
c4a5c52773 Merge pull request #1 from dolfs/master
Added prep.sh script for automated builds
2016-02-08 22:49:34 -08:00
Dolf Starreveld
e7456643cd Added prep.sh script for automated builds
* Changed README.md commensurately
* Add cameras.sql to .gitignore to not commit personal camera data
* Change CMakeLists.txt to explicitly refer to hand-built libevent dirs
2016-02-07 22:59:29 -08:00
Scott Lamb
3b0dc5368e Write using the shiny new schema
There's a lot of work left to do on this:

* important latency optimization: the recording threads block
  while fsync()ing sample files, which can take 250+ ms. This
  should be moved to a separate thread to happen asynchronously.

* write cycle optimizations: several SQLite commits per camera per minute.

* test coverage: this drops testing of the file rotation, and
  there are several error paths worth testing.

* ffmpeg oddities to investigate:

  * the out-of-order first frame's pts
  * measurable delay before returning packets
  * it sometimes returns an initial packet it calls a "key" frame that actually
    has an SEI recovery point NAL but not an IDR-coded slice NAL, even though
    in the input these always seem to come together. This makes playback
    starting from this recording not work at all on Chrome. The symptom is
    that it loads a player-looking thing with the proper dimensions but
    playback never actually starts.

  I imagine these are all related but haven't taken the time to dig through
  ffmpeg code and understand them. The right thing anyway may be to ditch
  ffmpeg for RTSP streaming (perhaps in favor of the live555 library), as
  it seems to have other omissions like making it hard/impossible to take
  advantage of Sender Reports. In the meantime, I attempted to mitigate
  problems by decreasing ffmpeg's probesize.

* handling overlapping recordings: right now if there's too much time drift or
  a time jump, you can end up with recordings that the UI won't play without
  manual database changes. It's not obvious what the right thing to do is.

* easy camera setup: currently you have to manually insert rows in the SQLite
  database and restart.

but I think it's best to get something in to iterate from.

This deletes a lot of code, including:

* the ffmpeg video sink code (instead now using a bit of extra code in Stream
  on top of the SampleFileWriter, SampleIndexEncoder, and MoonfireDatabase
  code that's been around for a while)

* FileManager (in favor of new code using the database)

* the old UI

* RealFile and friends

* the dependency on protocol buffers, which was used for the config file
  (though I'll likely have other reasons for using protocol buffers later)

* even some utilities like IsWord that were just for validating the config
2016-02-03 23:22:37 -08:00
Scott Lamb
ca368944ec AnnexB->AVC sample data converter
I discovered that the mp4 files I was writing were viewable in VLC and in
Chrome-on-desktop (ffmpeg-based) but not in Chrome-on-Android
(libstagefright-based). It turns out that I was writing Annex B sample data
rather than the correct AVCParameterSample format. ffmpeg gives both the
"extradata" and the actual frames in Annex B format when reading from rtsp.

This is still my simple, unoptimized implementation of the Annex B parser. My
Raspberry Pi 2 is still able to record my six streams using about 30% of 1
core, so it will do for the moment at least.
2016-02-02 20:18:41 -08:00
Scott Lamb
8ee1ab1c7b Extend ListCameras to return more ifno
In particular, this returns all the extra configuration data that will be
necessary to actually instantiate streams from the database rather than the
soon-to-be-removed configuration file.
2016-01-31 23:27:52 -08:00