summaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_NODE_PROTOCOL.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/MESHBAY_NODE_PROTOCOL.md')
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md34
1 files changed, 26 insertions, 8 deletions
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index 2682820..19cced9 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -1,6 +1,6 @@
# MeshBay Node Protocol (MNP)
-**Wire version:** `3.1` — `meshbay_common/__init__.py` (`MNP_VERSION`)
+**Wire version:** `3.2` — `meshbay_common/__init__.py` (`MNP_VERSION`)
**Oldest peer accepted:** `3.0` — `handshake.py` (`MNP_MIN_SUPPORTED`)
**Normative implementation:** `meshbay-common` (`protocol.py`, `handshake.py`,
`groupbox.py`, `chatbox.py`, `adminop.py`, `join.py`, `device.py`, `crypto.py`,
@@ -1627,19 +1627,23 @@ array while MediaSource consumes it a segment at a time.
```
C N
- |-- stream_req {v, file_id, start, credits} -------->|
+ |-- stream_req {v, file_id, start, credits, |
+ | audio_track?} -------------------------------->|
| | retire this session's previous
| | stream (a second request
| | means the first is over)
| | acquire a transcode slot (8)
- | | ffprobe: codec, duration, audio
+ | | ffprobe: codec, duration, the
+ | | audio tracks
| | spawn ffmpeg
| | -ss before -i (index seek)
| | video: copy, or libx264 when
| | the browser cannot decode
- | | audio: always AAC, 2 ch
+ | | audio: always AAC, 2 ch,
+ | | -map 0:a:<audio_track>
| | frag_keyframe+empty_moov
- |<- stream_init {v, file_id, codec, duration, start}-|
+ |<- stream_init {v, file_id, codec, duration, start, |
+ | audio_tracks[], audio_track} ------------------|
| |
| check MediaSource.isTypeSupported(codec) |
| |
@@ -1664,6 +1668,11 @@ array while MediaSource consumes it a segment at a time.
| Concurrent transcodes | 8 node-wide, semaphore on the transport context |
| Seeking | a new `stream_req` with `start`; the previous stream is retired first, ffmpeg respawned with `-ss` |
| `start` in `stream_init` | the value actually used — seeking lands on the keyframe at or before the request, and the client adds it back as `SourceBuffer.timestampOffset` |
+| `audio_tracks` in `stream_init` | every audio track: `i` (the **audio ordinal**, what `-map 0:a:<n>` takes, never the container stream index), `lang`, `title`, `codec`, `ch`. Empty for a file with no audio |
+| `audio_track` in `stream_req` | which ordinal to map. Absent, out of range or malformed is the first track |
+| `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 |
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
@@ -1992,8 +2001,9 @@ message:
## 13. Versioning and compatibility
-MNP versions independently of the package version. Current: **`3.1`**; oldest peer
-accepted: **`3.0`** — 3.1 is additive, so the floor does not move with it.
+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
+them.
The two numbers are separate on purpose. `MNP_VERSION` says what this build speaks;
`MNP_MIN_SUPPORTED` says what it will talk to, and moving the second is a decision about
@@ -2009,6 +2019,14 @@ whether an older peer can still do anything useful:
chat. A break that touches something every session depends on cannot be confined, and
the honest form is to refuse at the handshake: **a stated refusal is a bug report, a
feature that quietly does not work is a support case.**
+* **Discovery from the answer, not from the version number.** 3.2's audio-track
+ selection is the shape to copy: the node lists the tracks in `stream_init`, and the
+ client sends `audio_track` only when it was given a list. A peer that ignores that
+ field would not degrade — it would serve a different language in silence, which is a
+ wrong answer and not a missing feature — and what keeps the change additive is that
+ no client can ever put an old node in that position. This is not the opt-in switch
+ I10 refuses: there is no second branch on the node, which always enumerates, always
+ honours the request and always reports the track it used.
* **A requirement is breaking even when its messages are additive** (I10). New message
types and a new optional field are additive on the wire; *requiring* them is not, and
an opt-in switch that enforces the requirement only for peers that speak the new
@@ -2183,7 +2201,7 @@ LP(x) = uint32be(len(x)) || x every field, no exceptions
| Constant | Value | Source |
|---|---|---|
-| `MNP_VERSION` | `3.1` | `meshbay_common/__init__.py` |
+| `MNP_VERSION` | `3.2` | `meshbay_common/__init__.py` |
| `MNP_MIN_SUPPORTED` | `3.0` | `handshake.py` |
| `NONCE_LEN` | 32 bytes (both handshake nonces) | `handshake.py` |
| `ADMIN_CHALLENGE_TTL` | 120 s | `adminop.py` |