|
STAR
Cal STAR — firmware, DAQ server, and engine design
|
Classes | |
| class | Clip |
| class | Timing |
Functions | |
| _boxes (f, int start, int end) | |
| tuple[int, int]|None | _full_box_times (f, int at) |
| _child (f, int a, int b, bytes kind) | |
| float|None | mp4_frame_seconds (Path path) |
| float|None | mp4_duration (Path path) |
| Timing|None | read_timing (Path path) |
| tuple[float, float|None]|None | _header_cached (Path path, os.stat_result st) |
| _timing_cached (Path path) | |
| float|None | folder_time (str name, str tz) |
| bool | available () |
| list[Clip] | list_clips () |
| list[Clip] | clips_overlapping (float t0, float t1) |
| Path|None | clip_file (str recording, str stream) |
Variables | |
| RECORDING_RE | |
| STREAM_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$") | |
| int | TIMING_VERSION = 1 |
| dict | _dur_cache = {} |
| dict | _timing_cache = {} |
| _lock = threading.Lock() | |
Video recordings from the gateway, placed on a run's timeline.
The video agent (daq-server/video/agent.mjs) keeps each Save and Record as a folder
`<YYYY-MM-DD_HHMMSS>_<label>/` holding one video-only `<stream>.mp4` per stream. That
folder is mounted read-only here (`config.VIDEO_DIR`); this module lists the clips in it,
says which overlap a run, and resolves a clip to its file. It never writes.
Timing. Every clip the agent has kept since timing files existed has one beside it,
`<stream>.json` (agent.mjs, timingFor): pieces of `{fileS, wallMs}`, each saying the file
at `fileS` seconds was filmed at `wallMs` and runs on at real speed to the next piece. The
pieces carry the keyframe cut (the first frame is not the press) and any outage (the file
plays straight through a gap; the wall clock does not). Such a clip is exact to the
gateway's clock, and the file also carries `clockOffsetMs`, the gateway's clock minus the
DAQ's as the DAQ backend measured it at the press (`clockUncertaintyMs` either way). The
sensors are stamped on the DAQ's clock, so the offset is taken off every piece. A file
without one (an older agent or backend) is taken to share the DAQ's clock.
A clip with no timing file is placed by its folder name, which the agent writes in the
gateway machine's local time to the second (`config.VIDEO_TZ`):
- `_record`: the name is when Record was pressed, so that is the clip's start;
- anything else (`_save`): the name is when Save was pressed, and the clip is the buffer
*before* it, so the clip ends there and starts one duration earlier.
That is only approximate: the second is truncated, and the agent cuts the file at a
keyframe, which can move the first frame by a keyframe interval. Every such clip is
marked `approx`. A clip's duration is read from its own mp4 header (the agent writes
with +faststart, so the header is near the front), never by decoding.
Cost: one directory listing plus a `stat` per clip on each call; the mp4 header is read
once per file and cached on (mtime, size). A folder still being recorded holds no mp4
yet, and a file ffmpeg has not finished has no readable duration, so neither is listed.
|
protected |
(type, payload offset, payload end) for each box in f[start:end].
Definition at line 101 of file video.py.
References min.
Referenced by _child(), mp4_duration(), and mp4_frame_seconds().
|
protected |
Definition at line 145 of file video.py.
References _boxes().
Referenced by mp4_duration(), and mp4_frame_seconds().
|
protected |
(timescale, duration) from an mvhd/mdhd payload at `at` (both share the layout).
Definition at line 125 of file video.py.
Referenced by mp4_duration(), and mp4_frame_seconds().
|
protected |
(duration, seconds per frame) from the file's header, read once per version.
Definition at line 260 of file video.py.
References mp4_duration(), and mp4_frame_seconds().
Referenced by list_clips().
|
protected |
| bool backend.video.available | ( | ) |
Definition at line 304 of file video.py.
Referenced by clip_file(), and list_clips().
| Path | None backend.video.clip_file | ( | str | recording, |
| str | stream ) |
The file for a clip this module would list, else None. Nothing from the request reaches the filesystem until both names have passed the listing's own patterns.
Definition at line 365 of file video.py.
References available(), and list_clips().
| list[Clip] backend.video.clips_overlapping | ( | float | t0, |
| float | t1 ) |
Clips any part of which falls inside [t0, t1].
Definition at line 360 of file video.py.
References list_clips().
| float | None backend.video.folder_time | ( | str | name, |
| str | tz ) |
Epoch seconds of a recording folder's name, read in the gateway's time zone.
Definition at line 292 of file video.py.
Referenced by list_clips().
| list[Clip] backend.video.list_clips | ( | ) |
Every finished clip in the recordings folder.
Definition at line 308 of file video.py.
References _header_cached(), _timing_cached(), available(), and folder_time().
Referenced by clip_file(), and clips_overlapping().
| float | None backend.video.mp4_duration | ( | Path | path | ) |
Duration in seconds from the movie header (moov/mvhd), or None if the file has no complete header — not an mp4, or still being written.
Definition at line 193 of file video.py.
References _boxes(), _child(), and _full_box_times().
Referenced by _header_cached().
| float | None backend.video.mp4_frame_seconds | ( | Path | path | ) |
Seconds per frame of the first video track: its duration over its sample count (mdhd, stsz). The kept files are video only, constant rate, so this is the step one arrow press moves. None if the header does not say.
Definition at line 152 of file video.py.
References _boxes(), _child(), and _full_box_times().
Referenced by _header_cached().
| Timing | None backend.video.read_timing | ( | Path | path | ) |
A clip's timing file, on the DAQ's clock, or None if there is none or it is not one this viewer understands — then the clip falls back to its name.
Definition at line 226 of file video.py.
Referenced by _timing_cached().
| backend.video.RECORDING_RE |
| backend.video.STREAM_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$") |