diff options
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 40 |
1 files changed, 26 insertions, 14 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 23dc4e0a..53522060 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -4219,19 +4219,32 @@ within two. The main process holds what the scan has found and the page polls it waiting on one call for the whole scan; a poll uses the same checked `handle()` door as every other call, where a pushed event would be a second one. -**The Android relay is the desktop's, with three things the phone found.** +**Both relays hold to three things the phone found first.** (1) **The header is everything before the first moof**, however many chunks it -arrives in: the node's first chunk can be the 28-byte ftyp alone, and served as -the header it left the receiver without a moov. (2) **What a receiver has not -read waits in a spool file, not in memory.** A fragment is a segment — 5 to -10 MB at a film's bitrate — and the page runs ahead of the television by its -whole read-ahead; dropped past the desktop's 8 MB bound, each lost fragment -froze the picture for its length. (3) **A seek's first segments are held while -it lands** (`video-player.js`): they are the new stream, its header first, and -they arrived while `reinitAt` waited on the SourceBuffer and were dropped as the -old film's — the local player never noticed, a relay restarted there did. The -local element keeps playing while a cast runs, because its playhead paces the -stream, and is muted. +arrives in: the node's first chunk can be the 28-byte ftyp alone, the header +exactly, or the header and the start of the first fragment, depending on when +the node read ffmpeg's output. Taken whole as the header, two of the three broke +the stream, so the same film cast one try in three. A receiver that connects +before the first moof waits for it. (2) **A receiver reading at playback speed +loses nothing.** A fragment is a keyframe interval, a few seconds of picture +and up to 10 MB, and the page runs ahead of the television by its whole +read-ahead; dropped past the desktop's old 8 MB bound, each lost fragment froze +the picture for its length. Android spools a receiver's lead to a file, the +desktop keeps it in the response's buffer up to 512 MB; both bound the backlog +a joining receiver is sent in bytes as well as in fragments. (3) **A seek's +first segments are held while it lands** (`video-player.js`): they are the new +stream, its header first, and they arrived while `reinitAt` waited on the +SourceBuffer and were dropped as the old film's; the local player never +noticed, a relay restarted there did. The local element keeps playing while a +cast runs, because its playhead paces the stream, and is muted. + +**A stop clears its state before it waits for anything.** The page stops the +relay at every `stream_init` without waiting and starts it again at the first +segment. The desktop relay cleared its token, port and header only once its +old port had closed, which waited for the receiver to drop the old stream, +which it does when loaded with the new one: the clearing landed on the new +relay, and the receiver was handed `:null/stream.mp4?t=null`. The old +connections are now cut rather than waited for. **While a receiver plays the film, the player is its remote.** Phone and television start apart and drift, so a scrubber on the local playhead lied about @@ -4801,8 +4814,7 @@ process runs it — `systemctl --user` on Linux, Task Scheduler on Windows. |---|---| | **A disk that stops answering still stalls what is not the scan** | The indexer's own reads are bounded (§6.3). Serving a chunk, the availability check and the directory deletions go through the roots' I/O thread, which has no such limit, and the tag and photo readers leave a thread in the shared pool at each timeout | | **A file that gave no answer is asked again at every start** | The list of unread files is memory. With three or more such files in a row the root is paused again three minutes into each start, until the operator resumes it or moves them | -| **The desktop cast relay drops whole segments** | `cast-relay.js` drops a fragment once 8 MB wait for a receiver, and a fragment is a segment, 5 to 10 MB at a film's bitrate: the picture freezes for its length. Its backlog is bounded in fragments only. The Android relay spools a receiver's lead to disk and bounds its backlog in bytes (§11.4); the desktop has neither | -| **The desktop cast relay takes the first chunk for the whole header** | The node's first chunk can be the ftyp alone, the moov in the next; the receiver then gives up. Fixed in the Android relay (§11.4), not in `cast-relay.js` | +| **A receiver that never answers a load leaves the player waiting** | `cast-chromecast.js` waits on `player.load()` with no time limit, so a receiver that neither plays nor refuses keeps the remote on its spinner until the viewer stops the cast. A timeout that reports the failure, as Android's `reported` does, is the fix | | **A cast restart may pull far ahead** | Seen once on an emulator with a synthetic film: after the restart's reinit the node reported `duration=None`, and the page pulled most of the film at network speed. Not reproduced on a real film; the suspicion is a read-ahead budget computed without a duration | | **"Copy stream URL" after picking a receiver casts to that receiver** | The player keeps the last device chosen, so the copy-only path reconnects it instead of only starting the relay | | **A folder renamed on the node's disk empties until the next reconciliation** | Seen live (2026-10-10, ntfs3): renaming a 2,441-file folder inside a root made the watcher drop every entry under the old path and announce none under the new one, so the group read as empty, and the loop was busy long enough for the hub's keepalive to time out. A restart (full scan) brought them back. A directory moved within a root should rescan its new subtree at once, not wait for the periodic scan | |