aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py56
1 files changed, 46 insertions, 10 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 5f5c9ff..6b87e31 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -463,18 +463,29 @@ class WebRTCPeerSession:
# whatever one likes into the operator's file.
def _f(key: str, n: int = 24) -> str:
return str(msg.get(key))[:n].replace("\n", " ")
+ if msg.get("event"):
+ # Once per stream or per seek, not once per five seconds —
+ # and a seek nobody asked for looks exactly like a viewer
+ # dragging the scrubber from this side, so it has to be
+ # visible without turning DEBUG on.
+ log.info(
+ "stream: client %s target=%s t=%ss offset=%s ready=%s "
+ "duration=%s ranges=[%s]",
+ _f("event", 16), _f("target"), _f("t"), _f("offset"),
+ _f("ready"), _f("duration"), _f("ranges", 120))
# Debug: one line every five seconds per viewer. Run the daemon
# with --log-level debug to see inside a player that is
# misbehaving — it is the only view of the browser there is
# when the browser is a phone.
- log.debug(
- "stream: client t=%ss ahead=%ss ready=%s paused=%s "
- "stalled=%s q=%s inflight=%s appending=%s updating=%s "
- "quota=%s ms=%s err=%s ranges=[%s] (sent=%d)",
- _f("t"), _f("ahead"), _f("ready"), _f("paused"),
- _f("stalled"), _f("q"), _f("inflight"), _f("appending"),
- _f("updating"), _f("quota"), _f("ms"), _f("err", 80),
- _f("ranges", 120), self._stream_segments)
+ else:
+ log.debug(
+ "stream: client t=%ss ahead=%ss ready=%s paused=%s "
+ "stalled=%s q=%s inflight=%s appending=%s updating=%s "
+ "quota=%s ms=%s err=%s ranges=[%s] (sent=%d)",
+ _f("t"), _f("ahead"), _f("ready"), _f("paused"),
+ _f("stalled"), _f("q"), _f("inflight"), _f("appending"),
+ _f("updating"), _f("quota"), _f("ms"), _f("err", 80),
+ _f("ranges", 120), self._stream_segments)
elif mtype == MNP.STREAM_STOP:
age = (time.monotonic() - self._stream_started_at
if self._stream_started_at else -1)
@@ -2056,8 +2067,29 @@ class WebRTCPeerSession:
self._send({"type": "error", "detail": "Unsupported video codec"})
return
+ # Where to begin. Seeking is a stream restarted somewhere else: the
+ # viewer moves the scrubber, this session's previous stream is retired
+ # by _replace_stream, and ffmpeg is spawned again with -ss.
+ try:
+ start = float(msg.get("start", 0) or 0)
+ except (TypeError, ValueError):
+ start = 0.0
+ # Past the end would produce an empty stream and a player waiting for
+ # segments that are never coming.
+ if duration and start >= duration - 1:
+ 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 []
proc = await asyncio.create_subprocess_exec(
"ffmpeg", "-hide_banner", "-loglevel", "error",
+ *seek_args,
"-i", str(file_path),
"-c", "copy",
"-movflags", "frag_keyframe+empty_moov+default_base_moof",
@@ -2071,6 +2103,10 @@ class WebRTCPeerSession:
"file_id": file_id,
"codec": codec_str,
"duration": duration,
+ # ffmpeg restarts its timestamps at zero whatever we seek to, so
+ # this is what the client adds back (`SourceBuffer.timestampOffset`)
+ # to put the fragments where they belong on the timeline.
+ "start": start,
})
# A client that says nothing gets the old behaviour, which is why this
@@ -2087,8 +2123,8 @@ class WebRTCPeerSession:
self._stream_started_at = time.monotonic()
self._stream_segments = 0
reason = "eof"
- log.info("stream: stream_init sent file=%s paced=%s credits=%d",
- file_id[:12], paced, self._stream_credit)
+ log.info("stream: stream_init sent file=%s paced=%s credits=%d start=%.1fs",
+ file_id[:12], paced, self._stream_credit, start)
try:
while True:
if paced and not await self._await_stream_credit():