Commit Graph

265 Commits

Author SHA1 Message Date
Scott Lamb
cb1bb5d810 upgrade cursive to latest major version 2019-05-31 16:35:07 -07:00
Scott Lamb
428f5a3ba4 update a few deps
cursive & rusqlite are more significant; I'll do those separately
2019-05-31 15:08:49 -07:00
Scott Lamb
579150c9d5 redact URLs within stream.rs; fixes #13 2019-02-13 22:34:19 -08:00
Scott Lamb
091217b1a4 configure ffmpeg to only stream video
This works around #36 for now. I'll need to do something different when
I actually implement audio support.
2019-02-11 22:58:09 -08:00
Scott Lamb
c271cfa2b5 make Writer enforce maximum recording duration
My installation recently somehow ended up with a recording with a
duration of 503793844 90,000ths of a second, way over the maximum of 5
minutes. (Looks like the machine was pretty unresponsive at the time
and/or having network problems.)

When this happens, the system really spirals. Every flush afterward (12
per minute with my installation) fails with a CHECK constraint failure
on the recording table. It never gives up on that recording. /var/log
fills pretty quickly as this failure is extremely verbose (a stack
trace, and a line for each byte of video_index). Eventually the sample
file dirs fill up too as it continues writing video samples while GC is
stuck. The video samples are useless anyway; given that they're not
referenced in the database, they'll be deleted on next startup.

This ensures the offending recording is never added to the database, so
we don't get the same persistent problem. Instead, writing to the
recording will fail. The stream will drop and be retried. If the
underlying condition that caused a too-long recording (many
non-key-frames, or the camera returning a crazy duration, or the
monotonic clock jumping forward extremely, or something) has gone away,
the system should recover.
2019-01-29 08:26:36 -08:00
Scott Lamb
3ba3bf2b18 backend support for live stream (#59)
This is so far completely untested, for use by a new UI prototype.

It creates a new URL endpoint which sends one video/mp4 media segment
per key frame, with the dependent frames included. This means there will
be about one key frame interval of latency (typically about a second).
This seems hard to avoid, as mentioned in issue #59.
2019-01-21 15:58:52 -08:00
Scott Lamb
95a8c2e78d support .mp4 files > 13.25 hours
Use version 1 of the mvhd, tkhd, and mdhd boxes to support 64-bit
durations. 2^32 units / 90,000 units/sec / 60 sec/min / 60 min/hr ~=
13.25 hrs.

Compatibility: looks like Chrome, Firefox, VLC, and ffmepg all support
version 1 with no problem.
2019-01-07 00:59:32 -08:00
Scott Lamb
de643f9f8d include segments in debug output 2018-12-29 13:15:01 -06:00
Scott Lamb
eb8a51aecb add a url for getting debug info about a .mp4 file
and add a unit test of path decoding along the way
2018-12-29 13:09:16 -06:00
Scott Lamb
b5387af3d4 lose "extern crate" everywhere (Rust 2018 edition) 2018-12-28 21:59:39 -06:00
Scott Lamb
f5703b9968 introduce typed errors and use in mp4 code
Fixes #46. If there are no video_sample_entries, it returns
InvalidArgument, which gets mapped to a HTTP 400. Various other failures
turn into non-500s as well.

There are many places that can & should be using typed errors, but it's
a start.
2018-12-28 17:30:33 -06:00
Scott Lamb
699ec87968 upgrade to 2018 Rust edition
This is mostly just "cargo fix --edition" + Cargo.toml changes.
There's one fix for upgrading to NLL in db/writer.rs:
Writer::previously_opened wouldn't build with NLL because of a
double-borrow the previous borrow checker somehow didn't catch.
Restructure to avoid it.

I'll put elective NLL changes in a following commit.
2018-12-28 14:59:06 -06:00
Scott Lamb
3644548018 fix some outdated comments in slices.rs 2018-12-28 10:04:47 -06:00
Scott Lamb
89fa35a2f7 be slightly more graceful on bad /view.mp4 (#46)
Before, this would panic from the reactor thread. After, it returns a
internal server error. Still not ideal, but better.

To return "bad request" as it should, mp4::FileBuilder::build() should
return a new error type that distinguishes "invalid argument" from
"internal" and the like. I'm thinking of using a ErrorKind enum
throughout the program that's similar to grpc::StatusCode.
2018-12-28 09:01:47 -06:00
Scott Lamb
4580038013 fix --require-auth flag
Apparently with docopt, --require-auth=false doesn't work, so booleans
with a default value of true can't be turned off. Toggle the default to
false to deal with this, for now. I'd prefer the default be true, but
I also would prefer to not use a negative --no-require-auth or
--allow-unauthenticated flag. I think I'll switch from docopt to clap
in the near future; it seems to be what the cool kids use.
2018-12-28 08:39:50 -06:00
Scott Lamb
24674f5b50 document proxy setup in guide/secure.md (for #26)
The guide is not as quick to follow and amateur-friendly as I'd like. A
few things that might improve matters:

   * complete #27 (built-in https+letsencrypt), so that when not sharing
     the port, users don't need to use nginx or certbot.
   * more ubiquitous IPv6 (out of my control but should happen over
     time) to reduce need to share the port
   * embed a dynamic DNS client
   * support UPnP Internet Gateway Device Control Protocol (if common
     routers have this enabled? probably not for security reasons.)

It's progress, though. Enough that I think I'll merge the auth branch
into master shortly.
2018-12-27 16:00:15 -06:00
Scott Lamb
3c1163dfe2 use SameSite=Strict (for #26)
I initially chose SameSite=Lax because I thought if a user followed a
link to the landing page, the landing page's ajax requests wouldn't send
the cookie. But I just did an experiment, and that's not true. Only the
initial page load (of a .html file) lacks the cookie. All of its
resources and ajax requests send the cookie. I'm not sure about
document.cookie accesses, but my cookie is HttpOnly anyway, so it's
irrelevant. So no reason to be lax.
2018-12-01 22:04:54 -08:00
Scott Lamb
3f76096a81 add Cache-Control: private hdr to non-static stuff
for #26
2018-12-01 16:55:10 -08:00
Scott Lamb
4f87c16c31 Merge branch 'master' into auth 2018-12-01 15:27:54 -08:00
Scott Lamb
35e6891221 update all Rust deps 2018-12-01 15:20:19 -08:00
Scott Lamb
087fdafc61 endpoint to debug --trust-forward-hdrs for #26 2018-12-01 00:44:19 -08:00
Scott Lamb
7a81d36562 support proxy forwarded headers
I went with legacy headers (X-Real-IP, X-Forwarded-Proto) because they
appear to be more widely supported than the RFC 7239 Forwarded header.
2018-11-28 14:49:56 -08:00
Scott Lamb
422cd2a75e preliminary web support for auth (#26)
Some caveats:

  * it doesn't record the peer IP yet, which makes it harder to verify
    sessions are valid. This is a little annoying to do in hyper now
    (see hyperium/hyper#1410). The direct peer might not be what we want
    right now anyway because there's no TLS support yet (see #27).  In
    the meantime, the sane way to expose Moonfire NVR to the Internet is
    via a proxy server, and recording the proxy's IP is not useful.
    Maybe better to interpret a RFC 7239 Forwarded header (and/or
    the older X-Forwarded-{For,Proto} headers).

  * it doesn't ever use Secure (https-only) cookies, for a similar reason.
    It's not safe to use even with a tls proxy until this is fixed.

  * there's no "moonfire-nvr config" support for inspecting/invalidating
    sessions yet.

  * in debug builds, logging in is crazy slow. See libpasta/libpasta#9.

Some notes:

  * I removed the Javascript "no-use-before-defined" lint, as some of
    the functions form a cycle.

  * Fixed #20 along the way. I needed to add support for properly
    returning non-OK HTTP statuses to signal unauthorized and such.

  * I removed the Access-Control-Allow-Origin header support, which was
    at odds with the "SameSite=lax" in the cookie header. The "yarn
    start" method for running a local proxy server accomplishes the same
    thing as the Access-Control-Allow-Origin support in a more secure
    manner.
2018-11-27 11:08:33 -08:00
Scott Lamb
61af963a64 Merge branch 'master' into auth 2018-11-20 11:10:47 -08:00
Scott Lamb
071be03c6f update most deps, notably including reqwest
Fixes #60

The reqwest dependency is significant because the old version required
an old version of openssl, complicating compilation on newer platforms.
reqwest also pulled in old/duplicate versions of hyper, tokio, etc.
Nice to drop a lot of that cruft.

I left rusqlite and uuid alone because they had breaking changes I
didn't want to mess with at the moment.

Bumped the minimum Rust version to 1.30.0, as required by the
new encoding_rs crate (and perhaps other things).
2018-11-20 09:32:55 -08:00
Scott Lamb
8a5056b253 "moonfire-nvr config" support for users (for #26) 2018-11-02 07:15:48 -07:00
Scott Lamb
aa81eae65a more robust timezone detection (fixes #12) 2018-08-31 17:19:24 -07:00
Scott Lamb
955a0a8c15 upgrade to hyper 0.12.x
Just one (intentional) functional change---now the streamers start
shutting down while the webserver shuts down gracefully.
2018-08-29 22:26:19 -07:00
Scott Lamb
a10e77d98e update cursive from 0.7 to 0.9 2018-08-24 22:14:03 -07:00
Scott Lamb
8dc5d64333 make with_recording_playback less monomorphized
This is a minor code size reduction - instead of being monomorphized
into four variants (according to "cargo llvm-lines"), it's now
monomorphized into two. The stripped release binary on macOS is about
8kB smaller (0.15%). Not a huge improvement but better than nothing.

Benchmarks seem unchanged (though they have a lot of variance).
2018-08-24 15:34:42 -07:00
Scott Lamb
b0071515e0 update deps
I want to use hyper::server::Request::bytes_mut(), so an update is
needed. Update everything at once. Most notably, the http-serve update
starts using the http crate types for some things. (More to come.)
2018-04-06 15:54:52 -07:00
Scott Lamb
f720f6acd4 fix timezone fetching on macOS High Sierra 2018-04-06 13:49:18 -07:00
Scott Lamb
97d831e054 move strutil to base crate
I plan to use strutil::hex in db/auth.rs.
2018-03-30 08:54:20 -07:00
Scott Lamb
299c0b1802 Merge branch 'master' (early part) into new-schema
Catch the new-schema branch up with everything up to (but not including) the
big UI refactoring. I'll merge that separately.
2018-03-24 22:29:40 -07:00
Scott Lamb
91636d3193 refine flush_if_sec behavior
The new behavior eliminates a couple unpleasant edge cases in which it
would never flush:

* if all recording stops, whatever was unflushed would stay that way
* if every recording attempt produces a 0-duration recording (such as if the
  camera sends only one frame and thus no PTS delta can be calculated),
  the list of recordings to flush would continue to grow
2018-03-23 15:16:43 -07:00
Scott Lamb
addeb9d2f6 add a TimerGuard around db locks & ops
I moved the clocks member from LockedDatabase to Database to make this happen,
so the new DatabaseGuard (replacing a direct MutexGuard<LockedDatabase>) can
access it before acquiring the lock.

I also made the type of clock a type parameter of Database (and so several
other things throughout the system). This allowed me to drop the Arc<>, but
more importantly it means that the Clocks trait doesn't need to stay
object-safe. I plan to take advantage of that shortly.
2018-03-23 13:31:23 -07:00
Scott Lamb
320374c6e9 favicon: security camera in orange/red circle
This is derived from the following icon:
https://thenounproject.com/term/security-camera/72530
by iconsmind.com. I paid for the royalty-free license.

Fixes #50.
2018-03-12 22:47:44 -07:00
Scott Lamb
4c8daa6d24 save timestamps along with opens 2018-03-10 16:15:36 -08:00
Scott Lamb
5854b31b76 serve .map files as Javascript
This fixes #47 for now at least.
2018-03-10 16:04:37 -08:00
Scott Lamb
d6fa470713 tests and fixes for Writer and Syncer
* separate these out into a new file, writer.rs, as dir.rs was getting
  unwieldy.
* extract traits for the parts of SampleFileDir and std::fs::File they needed;
  set up mock implementations.
* move clock.rs to a new base crate to be accessible from the db crate.
* add tests that exercise all the retry paths.
* bugfix: account for the new recording's bytes when calculating how much to
  delete.
* bugfix: when retrying an unlink failure in collect_garbage, we shouldn't
  warn about all the recordings no longer existing. Do this by retrying each
  step rather than the whole procedure again.
* avoid double-panic scenarios, which I hit while tweaking the mocks. These
  are quite annoying to debug as Rust doesn't print information about either
  panic. I ended up using lldb to get a backtrace. Better to be cautious about
  what we're doing when already panicking.
* give more context on raw::insert_recording errors, which I hit as well while
  tweaking the new tests.
2018-03-07 04:42:46 -08:00
Scott Lamb
672a327ee2
support serving Access-Control-Allow-Origin header (#19)
support serving Access-Control-Allow-Origin header

Closes #17.
2018-03-03 06:43:36 -08:00
Scott Lamb
b78ffc3808 view in-progress recordings!
The time from recorded to viewable was previously 60-120 sec for the first
recording of a RTSP session, 0-60 sec otherwise. Now it's one frame.
2018-03-02 15:40:32 -08:00
Scott Lamb
45f7b30619 allow listing and viewing uncommitted recordings
There may be considerable lag between being fully written and being committed
when using the flush_if_sec feature. Additionally, this is a step toward
listing and viewing recordings before they're fully written. That's a
considerable delay: 60 to 120 seconds for the first recording of a run,
0 to 60 seconds for subsequent recordings.

These recordings aren't yet included in the information returned by
/api/?days=true. They probably should be, but small steps.
2018-03-02 11:38:11 -08:00
Scott Lamb
b17761e871 move list_recordings_by_* logic into raw.rs
I want to start having the db.rs version augment this with the uncommitted
recordings, and it's nice to have the separation of the raw db vs augmented
versions. Also, this fits with the general theme of shrinking db.rs a bit.

I had to put the raw video_sample_entry_id into the rows rather than
the video_sample_entry Arc. In hindsight, this is better anyway: the common
callers don't need to do the btree lookup and arc clone on every row. I think
I'd originally done it that way only because I was quite new to rust and
didn't understand that db could be used from within the row callback given
that both borrows are immutable.
2018-03-01 20:59:05 -08:00
Scott Lamb
b2a8b3c216 update "moonfire-nvr check" for new schema 2018-03-01 17:07:42 -08:00
Scott Lamb
f01f523c2c refine 1->3 upgrade process
In hindsight, the "post_tx" step in the upgrade process introduced in
e7f5733 doesn't make sense. If the procedure fails at this stage, nothing says
it still needs to be completed. If the sample file dirs have to be updated
after the database, then there should be another database version to mark that
it's fully completed, and indeed that's the purpose version 3 serves. So get
rid of the Upgrader trait and just go back to a simple run function per
version.

In the case of the sample file dir metadata, it actually can happen before the
database transaction; the stuff written to the database later just needs to be
consistent with what it finds if there's an existing metadata file from a
half-completed update.

For safety, ensure there are no unexpected directory contents before
upgrading 1->2, and ensure the metadata matches before upgrading 2->3.
2018-03-01 09:47:56 -08:00
Scott Lamb
bcf42fe02c move db upgrade logic into db crate
This allows shrinking db's API surface.
2018-02-28 21:21:47 -08:00
Scott Lamb
fbe1231af0 move open_id from recording_playback to recording
I want to be able to use it in etags without having to do a full scan of the
recording_playback in advance, which would greatly increase time to first
byte. I probably will even use it in urls to ensure the segments they point to
are stable. I haven't actually done this yet - it will wait until I implement
serving unflushed recordings - but I want to get the schema set up properly.
2018-02-28 20:52:43 -08:00
Scott Lamb
fb4d88d3e2 make db::dir::Writer equally stubborn
Every recording it starts must be sent to the syncer with at least one sample
written. It will try forever (unless the channel is down, then panic). This
avoids the situation in which it prevents something in the uncommitted
VecDeque from ever being synced and thus any further recordings from being
flushed.
2018-02-28 12:32:52 -08:00
Scott Lamb
b1d71c4e8d improve Syncer's robustness
The new approach is to, rather than panicking, retry forever. The assumption
is that if a given operation is failing, a following operation is unlikely to
succeed, so it's simpler to just keep trying the earlier one than come up with
ways to undo it and proceed with later operations.

I still need to apply this approach to the Writer class. It currently unwraps
(crashes) or just gives up on a recording without ever sending it to the
Syncer. Given that recordings are all synced in order, that means further ones
can never be synced.
2018-02-28 11:07:55 -08:00
Scott Lamb
843e1b49c8 take FnMut closures by reference
I mistakenly thought these had to be monomorphized. (The FnOnce still
does, until rust-lang/rfcs#1909 is implemented.) Turns out this way works
fine. It should result in less compile time / code size, though I didn't check
this.
2018-02-23 09:19:42 -08:00
Scott Lamb
bf45ae6011 extend recording_playback with an open_id
As noted in schema.sql, this can be used for disambiguation. It also may be
useful in diagnosing data integrity problems.

Also, sneak in a couple minor improvements: better diagnostics in a couple
places, fix to 1->2 upgrade procedure.
2018-02-22 21:46:41 -08:00
Scott Lamb
b037c9bdd7 knob to reduce db commits (SSD write cycles)
This improves the practicality of having many streams (including the doubling
of streams by having main + sub streams for each camera). With these tuned
properly, extra streams don't cause any extra write cycles in normal or error
cases. Consider the worst case in which each RTSP session immediately sends a
single frame and then fails. Moonfire retries every second, so this would
formerly cause one commit per second per stream. (flush_if_sec=0 preserves
this behavior.) Now the commits can be arbitrarily infrequent by setting
higher values of flush_if_sec.

WARNING: this isn't production-ready! I hacked up dir.rs to make tests pass
and "moonfire-nvr run" work in the best-case scenario, but it doesn't handle
errors gracefully. I've been debating what to do when writing a recording
fails. I considered "abandoning" the recording then either reusing or skipping
its id. (in the latter case, marking the file as garbage if it can't be
unlinked immediately). I think now there's no point in abandoning a recording.
If I can't write to that file, there's no reason to believe another will work
better. It's better to retry that recording forever, and perhaps put the whole
directory into an error state that stops recording until those writes go
through. I'm planning to redesign dir.rs to make this happen.
2018-02-22 16:35:34 -08:00
Scott Lamb
31adbc1e9f initial split of database to a separate crate
It should reduce compile time / memory usage to put quite a bit of the code
into a separate crate. I also intend to limit visibility of some things to
only within the db crate, but that's for a future change. This is the smallest
move that will compile.
2018-02-20 23:15:39 -08:00
Scott Lamb
d84e754b2a replace homegrown Error with failure crate
This reduces boilerplate, making it a bit easier for me to split the db stuff
out into its own crate.
2018-02-20 22:46:14 -08:00
Scott Lamb
253f3de399 reorganize the sample file directory
The filenames now represent composite ids (stream id + recording id) rather
than a separate uuid system with its own reservation for a few benefits:

  * This provides more information when there are inconsistencies.

  * This avoids the need for managing the reservations during recording. I
    expect this to simplify delaying flushing of newly written sample files.
    Now the directory has to be scanned at startup for files that never got
    written to the database, but that's acceptably fast even with millions of
    files.

  * Less information to keep in memory and in the recording_playback table.

I'd considered using one directory per stream, which might help if the
filesystem has trouble coping with huge directories. But that would mean each
dir has to be fsync()ed separately (more latency and/or more multithreading).
So I'll stick with this until I see concrete evidence of a problem that would
solve.

Test coverage of the error conditions is poor. I plan to do some restructuring
of the db/dir code, hopefully making steps toward testability along the way.
2018-02-20 10:11:10 -08:00
Scott Lamb
e7f5733f29 new database/sample file dir interlock scheme
The idea is to avoid the problems described in src/schema.proto; those
possibilities have bothered me for a while. A bonus is that (in a future
commit) it can replace the sample file uuid scheme in favor of using
<camera_uuid>-<stream_type>/<recording_id> for several advantages:

  * on data integrity problems (specifically, extra sample files), more
    information to use to understand what happened.
  * no more reserving sample files prior to using them. This avoids some extra
    database transactions on startup (now there's an extra two total rather
    than an extra one per stream). It also simplifies an upcoming change I
    want to make in which some streams are not flushed immediately, reducing
    the write load significantly (maybe one per minute total rather than one
    per stream per minute).
  * get rid of eight bytes per playback cache entry in RAM (and nine bytes
    per recording_playback row on flash).

The implementation is still pretty rough in places:

  * Lack of tests.
  * Poor ode organization. In particular, SampleFileDirectory::write_meta
    shouldn't be exposed beyond db. I'm thinking about moving db.rs and
    SampleFileDirectory to a new crate, moonfire_nvr_db. This would improve
    compile times as well.
  * No tooling for renaming a sample file directory.
  * Config subcommand still panics in conditions that can be reasonably
    expected to happen.
2018-02-14 23:35:52 -08:00
Scott Lamb
89b6bccaa3 support multiple sample file directories
This is still pretty basic support. There's no config UI support for
renaming/moving the sample file directories after they are created, and no
error checking that the files are still in the expected place. I can imagine
sysadmins getting into trouble trying to change things. I hope to address at
least some of that in a follow-up change to introduce a versioning/locking
scheme that ensures databases and sample file dirs match in some way.

A bonus change that kinda got pulled along for the ride: a dialog pops up in
the config UI while a stream is being tested. The experience was pretty bad
before; there was no indication the button worked at all until it was done,
sometimes many seconds later.
2018-02-11 23:04:02 -08:00
Scott Lamb
6f309e432f store rfc6381_codec in the database
This avoids having codec-specific logic to synthesize it in db.rs. It's not
too much of a problem now with only H.264 support, but it'd be a pain when
supporting H.265 and other codecs.
2018-02-05 11:57:59 -08:00
Scott Lamb
cc6579b211 upgrader for v1->v2 2018-02-03 22:19:02 -08:00
Scott Lamb
57c44b5e35 schema 2: add a "record" bool to streams 2018-02-03 22:19:02 -08:00
Scott Lamb
dc402bdc01 schema version 2: support sub streams
This allows each camera to have a main and a sub stream. Previously there was
a field in the schema for the sub stream's url, but it didn't do anything. Now
you can configure individual retention for main and sub streams. They show up
grouped in the UI.

No support for upgrading from schema version 1 yet.
2018-02-03 22:15:54 -08:00
Scott Lamb
0d69f4f49b use add_camera in tests, not direct db inserts
This is a wash in terms of lines of code now, but it makes it a bit easier to
maintain as I make changes to the schema (such as separating out streams from
cameras), and it helps ensure the tests reflect reality.
2018-02-03 21:56:04 -08:00
Scott Lamb
c43fb80639 warn if a streamer op takes too long
My odroid setup has been occasionally (about once a week) losing about 15
seconds of recordings on all cameras. I'm not sure why. So I'm labelling all
the likely suspect spots and logging if any of them takes longer than a
second. I think this will give me more information; hopefully narrow it down
to network or local disk I/O.
2018-01-31 14:20:30 -08:00
Scott Lamb
6902be1981 upgrade deps 2018-01-30 22:05:39 -08:00
Scott Lamb
2c62d977b0 gzip json responses, handle HEAD properly 2018-01-23 11:24:40 -08:00
Scott Lamb
8caa2e5d0e crate rename: http-(entity|file) -> http-serve 2018-01-23 11:08:21 -08:00
Scott Lamb
5c8970fe8a update dependencies 2017-11-16 23:01:09 -08:00
Scott Lamb
8de7e391f8 populate timeZoneName as expected by UI
This works by a nasty hack, but it seems to work well enough for now.
Fingers crossed.
2017-10-21 23:57:13 -07:00
Scott Lamb
315f3594c2 add a basic Javascript UI
The Javascript is pretty amateurish I'm sure but at least it's something to
iterate from. It's already much more pleasant for browsing through videos in
several ways:

* more responsive to load only a day at a time rather than 90+ days
* much easier to see the same time segment on several cameras
* more pleasant to have the videos load as a popup rather than a link
  that blows away your position in an enormous list
* exposes the fancier .mp4 generation options: splitting at lengths
  other than the default, trimming to an arbitrary start and end time,
  including a subtitle track with timestamps.

There's a slight regression in functionality: I didn't match the former
top-level page which showed how much camera used of its disk allocation and
the total duration of video. This is exposed in the JSON API, so it shouldn't
be too hard to add back.
2017-10-21 21:54:27 -07:00
Scott Lamb
6eda26a9cc support run splitting in json api 2017-10-17 09:00:05 -07:00
Scott Lamb
9041eeb907 fix panic when requesting zero segment duration
The recording::Segment was constructing a segment with no frames in it, which
was causing a panic when appending a zero-length stts to the Slices. Fix this
in a couple ways:

* Slices::append should return Err rather than panic. No reason to crash the
  whole program when we have trouble serving a single .mp4 request.
* recording::Segment shouldn't produce zero-frame segments
2017-10-17 08:55:21 -07:00
Scott Lamb
1d08698d0c debug, fix panic with zero-duration recording
I had an assert that fired in this case, dating back to when I hadn't plumbed
Result returns through much of .mp4 construction. Now I have, so there's no
excuse in having an assert here. Change to an error return, and tweak it to
not fire in the zero-duration case.

Also fix a problem in the test harness; I hadn't finished converting it for
multi-recording tests, and it was returning the wrong recording.

Because of that, I seem to have stumbled across a related problem in which
asking for zero duration of a non-zero duration recording will return a
recording::Segment with no frames, which will cause panics because its
corresponding .mp4 slices are zero-length. I just adjusted the panic message
here; I'll follow up with changes to address that.
2017-10-17 06:14:47 -07:00
Scott Lamb
2bdb2eca5d fix a couple time problems
* CameraDayKey::bounds (used to generate the start and end times of days in
  the returned JSON) returned UTC, not matching what recordings were mapped
  into that day. So fetching a day with its given bounds would return
  something different. Test and fix it.

* Several time-related tests weren't calling testutil::init(), so they weren't
  fixing the time zone to the expected America/Los_Angeles. If the machine
  time is set to something else, they would break.
2017-10-11 20:08:26 -07:00
Scott Lamb
bbe04f909c fix ClockAdjuster logic
Small negative deltas caused every_minus_1 to be negative, which caused
underflow errors in debug builds. Fix this, test more comprehensively.
2017-10-09 22:13:38 -07:00
Scott Lamb
1e4d7d5ad9 make json api more idiomatic
* camelCase
* lose the "days":null in the overall cameras dict
2017-10-09 21:58:44 -07:00
Scott Lamb
5bb3dde74e work around #10 with advanced_editlist=false
I think this is an ffmpeg bug, which I plan to report. In the meantime, this
makes the tests pass. Long-term, even if ffmpeg fixes this, I probably don't
want to continue doing acceptance tests against whatever version of ffmpeg
happens to be installed - my real targets of interest are the latest versions
of Chrome, Firefox, Safari, QuickTime, and VLC.
2017-10-09 21:44:48 -07:00
Scott Lamb
711f7b3409 fix with-editlist hash
I missed this because I was running with ffmpeg 3 and had grown to expect this
test to fail. Quick fix on that coming shortly.
2017-10-09 21:00:45 -07:00
Scott Lamb
af282c309e fix corrupt stss on segments after trimmed segment
This was causing Firefox to fail to play multipart .mp4s which trimmed away a
prefix. In the developer console, it said NS_ERROR_DOM_MEDIA_METADATA_ERR
without giving any RESULT_DETAIL, making it a pain to diagnose. Given that the
stss is supposed to be needed for seeking, I'm surprised it didn't have any
immediately obvious impact on Chrome or VLC.  Maybe they just took longer to
seek than otherwise necessary.

The bug was that when keeping track of the "next frame num" while constructing
the .mp4, I appended the number in the underlying recording, not the number
post-trimming. That meant following segments used the wrong numbers. In some
cases, it caused it to exceed the total number of samples in the generated
.mp4, which seems to be what Firefox was complaining about. Running the result
through "ffmpeg -i bad.mp4 -c copy -f mp4 good.mp4" just trimmed away the most
obviously invalid ones, leaving others that didn't point to the frames they
meant to. That was enough to make Firefox start playing the file. /shruggie

The existing tests were all with a single segment, so I added a new one to
catch this. I also added a Debug implementation to recording::Segment and
mp4::Segment.
2017-10-09 06:32:43 -07:00
Scott Lamb
919e9a6deb remove extraneous debug logging 2017-10-04 22:55:29 -07:00
Scott Lamb
cb18ba44d8 fix /view.mp4 with rel_start
This was totally broken in commit 1cf27c18. It would serve bytes from the
beginning of the sample file in question, not from the start of the given
range.
2017-10-04 22:51:16 -07:00
Scott Lamb
57985079cc bugfix: in /recordings, end_id should be inclusive 2017-10-04 06:36:30 -07:00
Scott Lamb
5ea2c2fed1 fix media rate in edit list
it should be exactly 1, but was slightly more because the fraction was
incorrectly 1 rather than 0. I'm not sure if any actual players care about
this, but it was something I noticed when looking into strange edit list
behavior.
2017-10-04 00:03:33 -07:00
Scott Lamb
bd4104b446 add start_id and end_id to .../recordings json
This was added to the API documentation in eee887b9 but never actually
implemented then. It's necessary to actually fetch the .mp4 in question.
2017-10-04 00:00:56 -07:00
Scott Lamb
7673a00bd9 serve 'video/mp4; codecs="avc1.xxxxxx"' mime type
This can be used when constructing a HTML5 SourceBuffer.
2017-10-03 23:25:58 -07:00
Scott Lamb
9eff91f7da fix test clip's mvhd timebase
I manually fixed the offending timebases and durations with a hex editor.
This addresses most of the failing tests described in #10.
2017-10-02 20:43:37 -07:00
Scott Lamb
04e9f3f160 support segmented mp4s
This is intended to support HTML5 Media Source Extensions, which I expect to
be the most practical way to make a good web UI with a proper scrub bar and
such.

This feature has had very limited testing on Chrome and Firefox, and that was
not entirely successful. More work is needed before it's usable, but this
seems like a helpful progress checkpoint.
2017-10-01 15:29:22 -07:00
Scott Lamb
cb689b2ec8 Linux/arm compilation fix
libc::c_char is u8 rather than i8 there, unlike Linux/x86_64 or OS X.
Use correct type to compile on all platforms.
2017-09-23 21:12:17 -07:00
Scott Lamb
11420df065 update deps (particularly hyper) + fix warnings 2017-09-21 21:51:58 -07:00
Scott Lamb
857a66f29c use my own ffmpeg crate
This significantly improves safety of the ffmpeg interface. The complex
ABIs aren't accessed directly from Rust. Instead, I have a small C
wrapper which uses the ffmpeg C API and the C headers at compile-time to
determine the proper ABI in the same way any C program using ffmpeg
would, so that the ABI doesn't have to be duplicated in Rust code.
I've tested with ffmpeg 2.x and ffmpeg 3.x; it seems to work properly
with both where before ffmpeg 3.x caused segfaults.

It still depends on ABI compatibility between the compiled and running
versions. C programs need this, too, and normal shared library
versioning practices provide this guarantee. But log both versions on
startup for diagnosing problems with this.

Fixes #7
2017-09-20 21:06:06 -07:00
Scott Lamb
8ff1d0dcb8 workaround config crash since cursive 0.5.1
https://github.com/gyscos/Cursive/issues/144
2017-07-02 22:03:16 -07:00
Scott Lamb
ac43e7fe17 fix bench --features=nightly, broken by upgrade
The benchmarks don't get compiled with the standard "cargo test";
they require "cargo +nightly bench --features=nightly", so I didn't notice
they were broken in the previous commit. Now fixed.
2017-06-11 19:40:36 -07:00
Scott Lamb
bebd6ee79a update dependencies
* The mylog update fixes a couple bad bugs.
* Otherwise, just keep up with the Rust ecosystem.
2017-06-11 12:57:55 -07:00
Scott Lamb
30cda85a2e shrink mp4::Segment by another 24 bytes on 32-bit
This is 1,440 bytes for a 60-segment .mp4, so another modest cache
improvement.
2017-03-27 20:55:58 -07:00
Scott Lamb
bfc0e2abe8 use my own logging package
This supports formats that I find more useful; one that mimicks the Google
glog package, and one that is similar but adapted for the systemd journal.
2017-03-26 00:01:48 -07:00
Scott Lamb
c3cffb510b make an assert more informative
I got this error but didn't understand how it happened.
2017-03-05 00:58:06 -08:00
Scott Lamb
4806c62ca1 reuse reqwest client in serve_camera_html bench
This makes a huge difference in the reported time - 863 usec rather than 6
milliseconds on my laptop. Part of the difference is in reqwest client setup
(it apparently initializes a SSL_CTX that is never used in this test), part
fresh connections vs keepalive, part I don't know what. None of it seems
relevant to the logic I want to test.
2017-03-03 22:26:29 -08:00
Scott Lamb
1cf27c189f upgrade to async hyper
serve_generated_bytes is >3X faster. One caveat is that the reactor thread may
stall when reading from the memory-mapped slice. Moonfire NVR is basically a
single-user program, so that may not be so bad, but we'll see.
2017-03-02 19:29:28 -08:00
Scott Lamb
618709734a trim the recording playback cache a bit
It had an Arc which in hindsight isn't necessary; the actual video index
generation is fast anyway. This saves a couple pointers per cache entry and
the overhead of chasing them. LruCache itself also has some extra pointers on
it but that's something to address another day.
2017-02-28 23:28:25 -08:00
Scott Lamb
045ee95820 shrink RecordingPlayback by one pointer 2017-02-27 23:30:53 -08:00