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.
This commit is contained in:
Scott Lamb
2018-01-23 11:05:07 -08:00
parent 0d69f4f49b
commit dc402bdc01
17 changed files with 936 additions and 599 deletions

View File

@@ -44,29 +44,32 @@ The `application/json` response will have a dict as follows:
* `uuid`: in text format
* `shortName`: a short name (typically one or two words)
* `description`: a longer description (typically a phrase or paragraph)
* `retainBytes`: the configured total number of bytes of completed
recordings to retain.
* `minStartTime90k`: the start time of the earliest recording for this
camera, in 90kHz units since 1970-01-01 00:00:00 UTC.
* `maxEndTime90k`: the end time of the latest recording for this camera,
in 90kHz units since 1970-01-01 00:00:00 UTC.
* `totalDuration90k`: the total duration recorded, in 90 kHz units.
This is no greater than `maxEndTime90k - maxStartTime90k`; it will be
lesser if there are gaps in the recorded data.
* `totalSampleFileBytes`: the total number of bytes of sample data (the
`mdat` portion of a `.mp4` file).
* `days`: object representing calendar days (in the server's time zone)
with non-zero total duration of recordings for that day. The keys are
of the form `YYYY-mm-dd`; the values are objects with the following
attributes:
* `totalDuration90k` is the total duration recorded during that day.
If a recording spans a day boundary, some portion of it is accounted to
each day.
* `startTime90k` is the start of that calendar day in the server's time
zone.
* `endTime90k` is the end of that calendar day in the server's time zone.
It is usually 24 hours after the start time. It might be 23 hours or 25
hours during spring forward or fall back, respectively.
* `streams`: a dict of stream type ("main" or "sub") to a dictionary
describing the stream:
* `retainBytes`: the configured total number of bytes of completed
recordings to retain.
* `minStartTime90k`: the start time of the earliest recording for
this camera, in 90kHz units since 1970-01-01 00:00:00 UTC.
* `maxEndTime90k`: the end time of the latest recording for this
camera, in 90kHz units since 1970-01-01 00:00:00 UTC.
* `totalDuration90k`: the total duration recorded, in 90 kHz units.
This is no greater than `maxEndTime90k - maxStartTime90k`; it will
be lesser if there are gaps in the recorded data.
* `totalSampleFileBytes`: the total number of bytes of sample data
(the `mdat` portion of a `.mp4` file).
* `days`: object representing calendar days (in the server's time
zone) with non-zero total duration of recordings for that day. The
keys are of the form `YYYY-mm-dd`; the values are objects with the
following attributes:
* `totalDuration90k` is the total duration recorded during that
day. If a recording spans a day boundary, some portion of it
is accounted to each day.
* `startTime90k` is the start of that calendar day in the
server's time zone.
* `endTime90k` is the end of that calendar day in the server's
time zone. It is usually 24 hours after the start time. It
might be 23 hours or 25 hours during spring forward or fall
back, respectively.
Example response:
@@ -78,23 +81,27 @@ Example response:
"uuid": "fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe",
"shortName": "driveway",
"description": "Hikvision DS-2CD2032 overlooking the driveway from east",
"retainBytes": 536870912000,
"minStartTime90k": 130888729442361,
"maxEndTime90k": 130985466591817,
"totalDuration90k": 96736169725,
"totalSampleFileBytes": 446774393937,
"days": {
"2016-05-01": {
"endTime90k": 131595516000000,
"startTime90k": 131587740000000,
"totalDuration90k": 52617609
},
"2016-05-02": {
"endTime90k": 131603292000000,
"startTime90k": 131595516000000,
"totalDuration90k": 20946022
"streams": {
"main": {
"retainBytes": 536870912000,
"minStartTime90k": 130888729442361,
"maxEndTime90k": 130985466591817,
"totalDuration90k": 96736169725,
"totalSampleFileBytes": 446774393937,
"days": {
"2016-05-01": {
"endTime90k": 131595516000000,
"startTime90k": 131587740000000,
"totalDuration90k": 52617609
},
"2016-05-02": {
"endTime90k": 131603292000000,
"startTime90k": 131595516000000,
"totalDuration90k": 20946022
}
}
}
},
}
},
...
],
@@ -109,29 +116,33 @@ Example response:
```json
{
"days": {
"2016-05-01": {
"endTime90k": 131595516000000,
"startTime90k": 131587740000000,
"totalDuration90k": 52617609
},
"2016-05-02": {
"endTime90k": 131603292000000,
"startTime90k": 131595516000000,
"totalDuration90k": 20946022
"description": "",
"streams": {
"main": {
"days": {
"2016-05-01": {
"endTime90k": 131595516000000,
"startTime90k": 131587740000000,
"totalDuration90k": 52617609
},
"2016-05-02": {
"endTime90k": 131603292000000,
"startTime90k": 131595516000000,
"totalDuration90k": 20946022
}
},
"maxEndTime90k": 131598273666690,
"minStartTime90k": 131590386129355,
"retainBytes": 104857600,
"totalDuration90k": 73563631,
"totalSampleFileBytes": 98901406
}
},
"description": "",
"maxEndTime90k": 131598273666690,
"minStartTime90k": 131590386129355,
"retainBytes": 104857600,
"shortName": "driveway",
"totalDuration90k": 73563631,
"totalSampleFileBytes": 98901406
"shortName": "driveway"
}
```
### `/api/cameras/<uuid>/recordings`
### `/api/cameras/<uuid>/<stream>/recordings`
A GET returns information about recordings, in descending order.
@@ -175,7 +186,7 @@ Each recording object has the following properties:
Example request URI (with added whitespace between parameters):
```
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/recordings
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/recordings
?startTime90k=130888729442361
&endTime90k=130985466591817
```
@@ -204,7 +215,7 @@ Example response:
}
```
### `/api/cameras/<uuid>/view.mp4`
### `/api/cameras/<uuid>/<stream>/view.mp4`
A GET returns a `.mp4` file, with an etag and support for range requests. The
MIME type will be `video/mp4`, with a `codecs` parameter as specified in [RFC
@@ -230,27 +241,27 @@ Expected query parameters:
Example request URI to retrieve all of recording id 1 from the given camera:
```
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/view.mp4?s=1
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.mp4?s=1
```
Example request URI to retrieve all of recording ids 15 from the given camera,
with timestamp subtitles:
```
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/view.mp4?s=1-5&ts=true
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.mp4?s=1-5&ts=true
```
Example request URI to retrieve recording id 1, skipping its first 26
90,000ths of a second:
```
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/view.mp4?s=1.26
/api/cameras/fd20f7a2-9d69-4cb3-94ed-d51a20c3edfe/main/view.mp4?s=1.26
```
TODO: error behavior on missing segment. It should be a 404, likely with an
`application/json` body describing what portion if any (still) exists.
### `/api/cameras/<uuid>/view.m4s`
### `/api/cameras/<uuid>/<stream>/view.m4s`
A GET returns a `.mp4` suitable for use as a [HTML5 Media Source Extensions
media segment][media-segment]. The MIME type will be `video/mp4`, with a