aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-17 16:18:30 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-17 16:18:30 +0200
commit0fed6786fe304195df66776787c5b5b82d321bcf (patch)
treefdce22ba79b830efe9a2802958aa9b8e7131ca23 /docs/MESHBAY_DESIGN.md
parent289a00afa33c6b7d2f77e46cdae56a593b21cf9a (diff)
downloadmeshbay-0fed6786fe304195df66776787c5b5b82d321bcf.tar.gz
fix(node): measure where a seek lands instead of predicting it
The previous fix read ffprobe's key frames and took the last one at or before the request. It was wrong twice, and a viewer felt the difference: subtitles went from 5 s early to 2–3 s late. Matroska's Cues index only some keyframes, so an index seek backs off to an indexed one that the frame list does not single out. And the landing point moves with **which streams are mapped**, because the container is positioned where every mapped stream has data — on the reported title, a seek to 4913.7 s landed at 4909.863 with video alone and at 4907.236 with the second audio track mapped beside it. The frame scan gave the first number; the stream delivered the second; the gap was 2.65 s, and the measured audio displacement in the served stream was 2.65 s. So the node asks ffmpeg instead: the same seek, the same mapping, one copied frame under `-copyts`, and the answer read back off the result. 0.06–0.07 s, cheaper than the scan it replaces. The `-ss` argument stays at the request, so the bytes served are exactly the ones served before — only the number naming them changes. The probe runs after the audio track is resolved, because it cannot be right before that is known. An answer after the request, or further before it than any real keyframe gap, is discarded in favour of the old label: a number wrong by seconds beats a fabricated one. Found by decoding the served stream and locating its first frame in the source, which put it at 4907.213 s against an announced 4909.863 s. The test does the same thing rather than comparing the announced number against a second reading of the same probe. 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.md21
1 files changed, 13 insertions, 8 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 7ce1168..8f91a87 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -2052,14 +2052,19 @@ MSE string) fed to a source buffer, with the node holding one slot per viewer.
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.
+ dragged**, and on the copy path that position is **measured, never predicted**.
+ The client builds `SourceBuffer.timestampOffset` out of this number and a
+ subtitle cue carries the source's own absolute time, so every second of
+ disagreement puts a line on screen a second away from the voice saying it.
+ Reading the key frames and taking the last one at or before the request answers
+ a different question twice over: Matroska's Cues index only some keyframes, so
+ an index seek backs off to an indexed one that can be much earlier, and the
+ landing point moves with **which streams are mapped**, because the container is
+ positioned where every mapped stream has data — one real seek answered 4909.863 s
+ from the frame list and delivered 4907.236 s. So the node runs the same seek with
+ the same mapping, copies one frame under `-copyts`, and reads the answer back:
+ 0.06–0.07 s, cheaper than the scan it replaced. 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.