summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/MESHBAY_DESIGN.md14
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md14
2 files changed, 21 insertions, 7 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 92f56ef..817afaa 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -1132,13 +1132,16 @@ it**.
> the branches come back.**
**The floor is not the current version, and MINOR additions are why.** It is
-`MNP_MIN_SUPPORTED` in `handshake.py`, it equals the last MAJOR, and 3.1 and 3.2
-have both been added above it without moving it. So a peer can be reachable and
+`MNP_MIN_SUPPORTED` in `handshake.py`, it equals the last MAJOR, and 3.1, 3.2 and
+3.3 have all been added above it without moving it. So a peer can be reachable and
still not do something the current version can, and the client has to cope with
that — **by reading the peer's own answer, never by comparing version numbers**.
3.2's audio tracks are the worked example: the node lists them in `stream_init`,
the client draws its selector from that list, and a node that sends no list gets no
-selector. That is not the branch the box above refuses. The branch it refuses is a
+selector. 3.3's subtitles repeat it exactly, and add the case where the list is
+narrower than the file: only the tracks the node can convert to WebVTT are named,
+so what the menu offers is what will actually appear, and a track's ordinal is
+therefore not its position in the list. That is not the branch the box above refuses. The branch it refuses is a
flag the client sets from a version it parsed; this is the node stating what it
has, in the same message the feature already needed, and it takes exactly one path
per peer because the peer said which.
@@ -2940,7 +2943,8 @@ The hub, the node daemon, both transports, the unified handshake, admission and
pairing, device linking with member-visible evidence, per-node identity, named roots
with RO/RW and eject/plug, the indexer with partial hashing, uploads, the sealed
index and sealed upload path, encrypted chat with epochs, video streaming with
-seeking, transfer leases with queueing, pause and resume, the group-application
+seeking, audio-language and subtitle selection, transfer leases with queueing,
+pause and resume, the group-application
framework with Chat, Files, Videos,
Music and Photos, cross-group search with source merging, the operator CLI and
loopback control API, the desktop client through its identity and download stages,
@@ -2956,6 +2960,8 @@ account recovery, and the Windows port through packaging.
| **Stage D11** | Windows clean-machine install verification and code signing |
| **Stage D12** | Release key, signed repositories, updates through the OS |
| — | DLNA/UPnP casting (§11.4) |
+| — | **Subtitles on a Chromecast.** The client shows them; the relay re-serves the same fMP4 bytes the browser gets, and a `<track>` in the renderer's DOM never leaves the machine. The route is an out-of-band `/subs.vtt` on the relay plus `tracks` on `player.load()`, and its one unknown — whether the receiver's CORS requirement can be met by the relay — cannot be settled by reading code |
+| — | **Bitmap subtitles** (PGS, VOBSUB — about a fifth of the embedded streams). No WebVTT without OCR; they are not listed rather than listed and blank. Burn-in covers them and costs `-c:v copy`, which is what the eight-slot sizing assumes never happens |
| — | Delegation (§3.4) |
| — | Tier 3 roster attestation (§3.3) |
| — | Playlists (§9.10) |
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index d4b9552..64f32e3 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -1645,7 +1645,8 @@ array while MediaSource consumes it a segment at a time.
| | -map 0:a:<audio_track>
| | frag_keyframe+empty_moov
|<- stream_init {v, file_id, codec, duration, start, |
- | audio_tracks[], audio_track} ------------------|
+ | audio_tracks[], audio_track, |
+ | subtitle_tracks[]} ----------------------------|
| |
| check MediaSource.isTypeSupported(codec) |
| |
@@ -1676,6 +1677,11 @@ array while MediaSource consumes it a segment at a time.
| `audio_track` in `stream_init` | the ordinal actually used, for the same reason `start` is reported: a list drawn before the file was replaced on disk can name a track that is no longer there, and the client must show what is playing rather than what it asked for. `null` when the file has no audio |
| Changing track | a new `stream_req` at the current position, exactly like a seek — one ffmpeg produces one audio track, so there is nothing to switch inside a running stream |
| Capability discovery | **the list, not the version.** A client draws its selector from `audio_tracks` and sends `audio_track` only when it has one, so a node too old to enumerate is never asked for a track it would ignore and answer in the wrong language |
+| `subtitle_tracks` in `stream_init` | the subtitle tracks that can be shown: `i` (the **subtitle ordinal**, what `-map 0:s:<n>` takes, counted over *every* subtitle stream including the ones absent from this list), `lang`, `title`, `codec`. Empty for a file with no convertible subtitles |
+| Which subtitle tracks are listed | text codecs only (subrip, ass, mov_text, …). Bitmap streams (PGS, VOBSUB — about a fifth of a real library) have no WebVTT without OCR, and one extracted anyway yields a header with no cues: a track that appears in the menu and shows nothing. A file whose subtitles are all bitmap reports none, exactly like a file with none |
+| Why the ordinal is not the list position | the two differ whenever a bitmap stream precedes a text one. Renumbering the survivors would map `0:s:0` to the stream that cannot be decoded — which is an empty WebVTT, not an error |
+| Fetching a track | `subtitle_req {file_id, track}` → `subtitle_resp {file_id, track, hash, size, mime}`; the blob is pulled by `hash` over `file_req`, the same indirection as a poster or an audio transcode. Extracted whole-file, converted to WebVTT, cached under the file's own id — so a film is extracted once, not once per viewing |
+| Subtitles and seeking | nothing. The cues carry the source's absolute timestamps, so a seek and an audio-language change both leave the client's `<track>` untouched |
An ffmpeg failure before any output produces `error: Could not stream this file`;
stderr stays server-side, where it belongs — it names paths on the operator's disk and
@@ -1856,6 +1862,7 @@ The corresponding *settings* are signed operations (§10.4).
| `tmdb_search_req {media_type, query}` | `tmdb_search_resp {results: [{id, title, year, poster}]}` | candidates for a human to pick from; never collapsed to one guess |
| `music_meta_req {file_id}` | `music_meta_resp {file_id, ...}` | MusicBrainz; cover art cached like a poster |
| `audio_transcode_req {file_id}` | `audio_transcode_resp {file_id, hash, size, mime}` | WMA/Musepack decode in no mainstream browser; the node transcodes once to AAC/M4A and caches it. Fetch the result by `hash` over `file_req` |
+| `subtitle_req {file_id, track}` | `subtitle_resp {file_id, track, hash, size, mime}` | MSE decodes no in-band text track, so a subtitle travels beside the stream. `track` is the ordinal from `stream_init.subtitle_tracks` and is echoed back, because one film's two tracks are exactly the pair that can be in flight together. Fetch the result by `hash` over `file_req` |
Every one of these is keyed by the entry's **`file_id`**, never by a path: a path names
the folder a file is in, so two files in one folder — any multi-episode season — would
@@ -1935,6 +1942,7 @@ it back (§3.5).
| `tmdb_search_req` / `_resp` | C→N / N→C | auth | candidate matches for an operator |
| `music_meta_req` / `_resp` | C→N / N→C | auth | MusicBrainz metadata for one file |
| `audio_transcode_req` / `_resp` | C→N / N→C | auth | browser-playable copy of a WMA/MPC file |
+| `subtitle_req` / `_resp` | C→N / N→C | auth | one embedded subtitle track as WebVTT, by cache hash |
| `ping` / `pong` | C→N / N→C | auth | liveness on an open channel |
| `member_revoke` / `_ack` | C→N / N→C | signed | stop serving the key to someone |
| `member_unpin` / `_ack` | C→N / N→C | signed | forget a pinned identity |
@@ -2004,8 +2012,8 @@ message:
## 13. Versioning and compatibility
-MNP versions independently of the package version. Current: **`3.2`**; oldest peer
-accepted: **`3.0`** — 3.1 and 3.2 are both additive, so the floor does not move with
+MNP versions independently of the package version. Current: **`3.3`**; oldest peer
+accepted: **`3.0`** — 3.1, 3.2 and 3.3 are all additive, so the floor does not move with
them.
The two numbers are separate on purpose. `MNP_VERSION` says what this build speaks;