Before, it'd look like the following, as mentioned in
https://github.com/scottlamb/moonfire-nvr/issues/331:
```
2024-12-09T20:53:24.853572 WARN s-CP3PRO-main streamer{stream="CP3PRO-main"}: moonfire_nvr::streamer: sleeping for 1 s after error err=UNKNOWN
caused by: deadline has elapsed
```
...which isn't as clear as it could be, particularly if you don't notice
the `caused by` bit. After, it should look more like the following:
```
2024-12-09T20:53:24.853572 WARN s-CP3PRO-main streamer{stream="CP3PRO-main"}: moonfire_nvr::streamer: sleeping for 1 s after error err=DEADLINE_EXCEEDED unable to play stream and get first frame within PT30S
caused by: deadline has elapsed
```
* in backend, save the full reason, not just the most direct error,
which is often the useless `UNKNOWN`
* in UI, instead of wrapping in `<Typography>` which just resorts in
a weird ransom-note mixed size look, reserve space for or actually
use an icon.
`cursive` now requires `Send + Sync` bounds, so I had to switch from
`Rc<RefCell<...>>` to `Arc<Mutex<...>>`. I also coalesced some
lock calls together, hopefully without introducing any deadlocks.
I don't see any calls into the UI that would need the model while these
are held, and it seemed fine in a quick test.
This is a step toward H.265 recording support (#33) by using Retina
for the `VisualSampleEncoding` instead of the prior H.264-specific
logic. The only further change to Moonfire itself needed for H.265 is to
add `| "h265"` to the check for the right video stream.
This also adds support for recording MJPEG, although browser playback
almost certainly won't work.
It's cleaner anyway to use `tokio::broadcast::channel` than the list
of callbacks.
Also make it send pings only on long pauses between frames, as when
the camera is disconnected.
Users are often puzzled why there are short recordings. Previously
the only way to see this was to examine Moonfire's logs. This should
be a much better experience to find it right in the UI where you're
wondering, and without the potential the logs are gone.
Fixes#302
Fixes#286.
I dug into Firefox code a bit to understand this but got a lost. But
I guess there are different policies for what's accessible via
`video.src = ""` than for `<video src="">`. This works for now, matches
what other players such as `mpegts.js` do, and is closer to what Safari
MME (required on iPhone) needs anyway. (With MME, apparently you have to
use `video.srcObject`, or the `MediaSource`'s `sourceopen` event will
never fire.)
* The `DisplaySelector` wasn't getting the correct flex layout.
Before this was done by a manual style on a `Paper` element. That
broke when adding the inner `<CardContent>` to because that's the
container that needs the `display: "flex"`. But really, it's clearer
to do this with `<FormGroup>` anyway, so do that.
* Switch from `<Card><CardContent>` to `<Paper sx={{ padding: ... }}>`.
The card content has extra padding (16 px in general, 24 at the bottom
of the last element to fit with an action). I'm not quite sure the
best way to remove it, and the simpler `<Paper>` seems fine for this
use anyway.
* strongly encourage the single-binary approach and say why.
* fix a broken link in troubleshooting guide (and regenerate toc).
* add a couple more comments to the docker compose snippet