diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-17 11:23:36 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-17 11:23:36 +0200 |
| commit | 677775266217b08044fe734be78f0e1bc874d708 (patch) | |
| tree | 3e2ff74e5d2df4a5143fed49bac4c76a67c98954 /packages/meshbay-node/src/meshbay_node/transport | |
| parent | 1fd284dbe33d05fd5037b172fe652a8a98f7b68d (diff) | |
| download | meshbay-677775266217b08044fe734be78f0e1bc874d708.tar.gz | |
fix(node): a seek left the audio a GOP behind the picture
-ss before -i cannot trim copied video, which must begin on a keyframe, but
accurate_seek did trim the re-encoded audio to the exact request. Every seek
on a copied stream therefore opened with a GOP-wide hole in the audio and ran
a GOP out of sync afterwards — 9.979s on a real film with a 10s keyframe
interval. Accurate seeking is now off wherever video is copied, and stays on
where it is re-encoded, which is the only path that could already begin where
it was asked to.
Every timestamp was correct throughout, which is why nothing caught it; the
tests assert on decoded audio and on frames compared against the source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py | 42 |
1 files changed, 35 insertions, 7 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py index b39941d..67ca380 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py @@ -5979,13 +5979,6 @@ class WebRTCPeerSession: start = max(0.0, duration - 5) start = max(0.0, start) - # -ss BEFORE -i, which seeks by the container index rather than by - # decoding up to the point: milliseconds on a 500 MB film instead of - # tens of seconds. It lands on the keyframe at or before `start`, so - # the picture can begin a few seconds earlier than asked — which is - # what every streaming player does, and why the client is told the - # value used rather than left to assume its own. - seek_args = ["-ss", f"{start:.3f}"] if start > 0 else [] # Video is copied whenever the browser can decode it directly — # re-encoding it is the expensive thing this pipeline exists to avoid, # and H264/VP9/AV1 already decode fine in-browser. HEVC is the one @@ -6036,6 +6029,41 @@ class WebRTCPeerSession: "detail": "This video needs transcoding, which the " "operator has turned off"}) return + # Seeking, and the trap that made a seek on a copied stream unwatchable. + # + # -ss BEFORE -i seeks by the container index rather than by decoding up + # to the point: milliseconds on a 500 MB film instead of tens of + # seconds. It lands on the keyframe at or before `start`, so the + # picture can begin a few seconds earlier than asked — which is what + # every streaming player does. + # + # **`-accurate_seek` is on by default, and it trims what it can.** It + # cannot trim copied video, which has to begin on a keyframe; it does + # trim the re-encoded audio, to exactly `start`. So the output began + # with video from the keyframe and audio from `start` — correct + # timestamps, both streams honestly placed, and **a hole in the audio + # one whole GOP wide**. Measured on a real film with a 10 s keyframe + # interval: seeking to 609 s against a keyframe at 599.104 s left + # 9.979 s of silence, after which sound and picture were a GOP apart + # for the rest of the film. + # + # Nothing downstream could see it. Every timestamp check passes — the + # first PTS of each stream, their durations, their spans, the browser's + # own A/V delta through MediaSource — because the timestamps were never + # wrong. Only the *content* at a given instant was, which is why this + # was found by decoding the output and comparing it against the source: + # the first frame is byte-identical to the source frame at the + # keyframe, and with the fix the audio's energy envelope matches the + # source at that same instant (r = 0.97) instead of one GOP later. + # + # This is also why re-encoded video never showed the fault, and why a + # library's HEVC files looked like the only ones that worked: video + # that is re-encoded *can* start exactly at `start`, so accurate + # seeking is right there and stays on. + seek_args: list[str] = [] + if start > 0: + seek_args = ["-ss", f"{start:.3f}"] if transcode_video else [ + "-noaccurate_seek", "-ss", f"{start:.3f}"] map_args = ["-map", "0:v:0"] if transcode_video: log.info("stream: re-encoding %s (%s) to H264", entry.name, |