feat(moq-gst): relay-less listen mode for moqsink and moqsrc + live catalog subscription#14
feat(moq-gst): relay-less listen mode for moqsink and moqsrc + live catalog subscription#14santi-ferreiro wants to merge 7 commits into
Conversation
Adds `listen` and `tls-generate` properties to moqsink so it can run its own QUIC/WebTransport server and serve a broadcast directly to subscribers that dial it, with no relay in between. `listen` is mutually exclusive with `url` (dial mode stays the default); the server branch in run_session mirrors the relay accept-and-serve path. Documents both properties in doc/bin/gstreamer.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a "listen mode" for moqsink in the GStreamer plugin, allowing it to run its own QUIC/WebTransport server and serve broadcasts directly to subscribers instead of dialing a relay. It adds configuration properties listen and tls-generate to support this mode. The review feedback highlights several important improvements: validating that tls-generate is provided when in listen mode to prevent runtime initialization failures, implementing a shutdown signal to prevent active accepted sessions from leaking when the element is stopped, and correcting a discrepancy in the documentation regarding the mutual exclusivity of the listen and url properties.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Add the `ordered` field to Track initializers in tests so the suite compiles after the group-order feature added that field to Track. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- require tls-generate in listen mode (server needs a cert; fail early with a clear error instead of a cryptic runtime init failure) - signal accepted sessions to close when the element stops, so detached session tasks don't leak connections past run_session - doc: state listen and url are mutually exclusive (the code already errors if both are set) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror of moqsink listen mode. moqsrc can run its own QUIC/WebTransport server (listen + tls-generate) and consume a broadcast from a publisher that dials it, with no relay. listen is mutually exclusive with url; the server branch in run_session accepts one session and consumes into the origin. Keeps the publisher as the dialing (QUIC client) peer, which is required for QUIC connection migration when the publisher's address changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The publisher briefly publishes an empty catalog while replacing a track: the old importer drop removes its rendition before the successor re-adds it. moqsrc treated an empty catalog as a completed session and exited silently, permanently stalling the stream. Skip catalog updates with no tracks and wait for one that lists renditions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pear A single catalog snapshot races the publisher pipeline: audio registers at caps time, video only at the first keyframe, and a replaced track briefly publishes an empty catalog before reappearing under a new name. Reading one snapshot could yield no tracks (silent stall) or an audio-only or doomed track list. Keep consuming catalog updates and subscribe to each new track, deduplicated by name. Session errors now surface through the catalog stream, posting an element error instead of idling on a dead session. Drop the NoMorePads message since pads can now appear at any time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ession Stopping the element before a publisher dialed left the session task parked in server.accept() forever, keeping the UDP socket bound. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Enables relay-less MoQ in moq-gst by letting either element run its own QUIC/WebTransport server, and fixes a catalog race in moqsrc that this exposed. (Originally moqsink-only; expanded to include the moqsrc mirror and the catalog fix.)
listen+tls-generateproperties; the sink runs a server and serves its broadcast to subscribers that dial it (publisher-as-server topology, used by the group-ordering verification).Listen mode
Both elements gain a
Transport { Client { url } | Server { bind, tls_generate } }resolution;listenis mutually exclusive withurland dial mode stays the default. The sink's server branch mirrors the relay accept-and-serve path (request.with_publish); the source's mirrors it on the consume side (request.with_consume(origin).ok().await), with the accept guarded by the element shutdown signal. Everything downstream of session establishment is unchanged in both elements.Catalog fix
The catalog is a live track that mutates during publisher startup: audio registers at caps time, video only at the first keyframe, and a track replacement briefly publishes an empty catalog (the old importer's
Dropremoves its rendition before the successor re-adds it under a new name).moqsrc previously read one catalog snapshot. Depending on timing it could see zero tracks (and exit as if the stream had ended: a silent, unrecoverable stall) or an audio-only list (never subscribing to video). The race is latent in every topology; subscriber-listen exposes it because the subscriber is connected and reading before the publisher's pipeline warms up.
Fix:
run_sessionloops oncatalog_updates.next(), subscribing to each new track (deduplicated by name) until the catalog track finishes or shutdown. A dead session now surfaces as agst::element_error!instead of idling forever. The now-invalidNoMorePadsmessage is removed since pads can appear at any point in the stream.Testing
Cross-package sync
doc/bin/gstreamer.md— listen-mode sections for both elements.(Written by Claude)