aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-17 15:50:55 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-17 15:50:55 +0200
commit3dcce17f9c616c8528a93bc625eb462d7fffb8f9 (patch)
tree3fcd64d9e0b5bef9e917052f8269907325dbc350 /docs/MESHBAY_DESIGN.md
parentd472725167c1d335996059d24b7b74f3730768fb (diff)
downloadmeshbay-3dcce17f9c616c8528a93bc625eb462d7fffb8f9.tar.gz
fix(node): a seek reports where the picture begins, not where it was asked
Copied video can only start on a keyframe, so `-ss t` with `-c:v copy` delivers the keyframe at or before `t`. The node reported `t` anyway and the client builds `SourceBuffer.timestampOffset` out of that number, so everything downstream believed the picture stood a few seconds further along than it did. That was a wrong label while only the scrubber read it — it is recorded as such, low priority, in the design document. Subtitles made it a wrong answer: their cues carry the source's own absolute timestamps, so the mismatch put every line on screen before it was spoken. Reported from real use on an H264 title, where seeking to 600 s, 2650 s and 5000 s lands on keyframes 0.82 s, 1.56 s and 4.64 s earlier. The copy path now resolves the request to that keyframe, seeks to it, and reports it. The bytes delivered do not change — ffmpeg lands on the same frame either way — only the number that names them. The look-up reads the thirty seconds before the request and cost 0.12–0.51 s on that title, which is the price already costed in §15.3 and never paid. Re-encoded video is untouched: it can begin exactly where it is asked to, and does. Two details worth their lines. The keyframe is passed back to ffmpeg at six decimals, because rounding a keyframe's own timestamp down puts it before the frame it names and selects the previous one — the same fault again, smaller. And the ffprobe window ends past the request, since an interval whose end is the request never emits a keyframe sitting exactly on it: the resume position is the one place a viewer asks for the same instant twice, and it would have been answered a whole GOP early. The test decodes the first frame served and matches it against the source frame at the position announced, rather than comparing `start` to an expected number — both sides of that comparison would be reading the same ffprobe and would agree by construction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md11
1 files changed, 10 insertions, 1 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 817afaa..7ce1168 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -2051,6 +2051,15 @@ MSE string) fed to a source buffer, with the node holding one slot per viewer.
- **Seeking restarts the source with an index seek before the input**, clamped away
from the end and echoed back; the client supplies the timestamp offset, because
copying timestamps does not preserve position.
+- **`stream_init.start` names where the picture begins, never where the viewer
+ dragged.** Copied video can only begin on a keyframe, so on that path the node
+ resolves the request to the keyframe at or before it, seeks to that, and reports
+ that. The client builds `SourceBuffer.timestampOffset` out of this number, and a
+ subtitle cue carries the source's own absolute time: one GOP of disagreement
+ between the two puts every line on screen before it is spoken. The look-up is a
+ bounded read of the thirty seconds before the request, measured at 0.12–0.51 s
+ on a real title, and only the copy path needs it — re-encoded video begins
+ exactly where it is asked to.
- **A seek trims what it can, and what it can differs per stream — which is a
desync, not an inconvenience.** Accurate seeking cannot trim copied video, which
must begin on a keyframe, but it does trim re-encoded audio to the exact request.
@@ -2986,7 +2995,7 @@ account recovery, and the Windows port through packaging.
| **Free-text third-party search** | `tmdb_search_req` takes a member's query and spends the operator's per-credential quota with no rate limit and no per-member bound, where link previews carry both. §6.5's standing rule — a bound and a named adversary in the same commit — was not applied here |
| **Disk I/O on the node's event loop** | A chunk read-and-encrypt and every upload chunk write run in the message handler. On a spun-down or network-mounted root that stalls every group, every stream and the hub socket, which is `AV9`'s lesson with the disk in place of the mail server |
| **Node announcements are not bounded** | One account may announce unlimited distinct node keys, each a row plus an IP-log row under a one-year retention. Proof of possession is checked (**M8**); the count is not |
-| **A seek reports the position asked for, not the one delivered** | `stream_init.start` echoes the request, while the content begins at the keyframe before it — 7–11 s earlier on a real film with a 10 s keyframe interval. Picture and sound now begin there *together*, so what disagrees is the scrubber and not the film. The comment beside `seek_args` has said since it was written that the client is "told the value used rather than left to assume its own", and that is the one thing it does not do. The fix is a keyframe look-up before the spawn, needed only on the copy path and measured at 0.11–0.47 s per seek; that cost against a wrong label is the trade nobody has made yet |
+
| **Migrations run on SQLite only** | The chain reaches head and agrees with the models there (§12), which is not where it ships. A default or a constraint PostgreSQL refuses would still reach a deploy |
---