Skip to content

feat(moq-gst): relay-less listen mode for moqsink and moqsrc + live catalog subscription#14

Open
santi-ferreiro wants to merge 7 commits into
benchmark-developfrom
feat/moqsink-listen-mode
Open

feat(moq-gst): relay-less listen mode for moqsink and moqsrc + live catalog subscription#14
santi-ferreiro wants to merge 7 commits into
benchmark-developfrom
feat/moqsink-listen-mode

Conversation

@santi-ferreiro

@santi-ferreiro santi-ferreiro commented Jun 24, 2026

Copy link
Copy Markdown

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.)

  1. moqsink listen modelisten + tls-generate properties; the sink runs a server and serves its broadcast to subscribers that dial it (publisher-as-server topology, used by the group-ordering verification).
  2. moqsrc listen mode — the mirror: the source runs a server and consumes the broadcast from a publisher that dials it (subscriber-as-server topology). This keeps the publisher as the QUIC client, which is required for IP-migration testing since QUIC connection migration is client-only.
  3. moqsrc live catalog subscription (bug fix, all topologies) — moqsrc now keeps consuming catalog updates for the life of the session and subscribes to tracks as they appear, instead of acting on a single startup snapshot.

Listen mode

Both elements gain a Transport { Client { url } | Server { bind, tls_generate } } resolution; listen is mutually exclusive with url and 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 Drop removes 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_session loops on catalog_updates.next(), subscribing to each new track (deduplicated by name) until the catalog track finishes or shutdown. A dead session now surfaces as a gst::element_error! instead of idling forever. The now-invalid NoMorePads message is removed since pads can appear at any point in the stream.

Testing

  • Subscriber-listen topology (paired benchmark-repo PR qualabs/moq-benchmark#51): full sequential suite 7/7, previously stalling on 5/7; IP-switch test shows genuine QUIC migration (PATH_CHALLENGE/PATH_RESPONSE on the same connection, packet-level trace).
  • Dial-mode topologies (relay, publisher-listen) re-verified: stable and packet-loss runs unchanged, zero frame loss on paths that already worked.

Cross-package sync

  • doc/bin/gstreamer.md — listen-mode sections for both elements.

(Written by Claude)

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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread rs/moq-gst/src/sink/imp.rs
Comment thread rs/moq-gst/src/sink/imp.rs
Comment thread rs/moq-gst/src/sink/imp.rs
Comment thread doc/bin/gstreamer.md Outdated
santi-ferreiro and others added 5 commits June 24, 2026 02:01
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>
@santi-ferreiro santi-ferreiro changed the title feat(moq-gst): add listen mode to moqsink for relay-less publishing feat(moq-gst): relay-less listen mode for moqsink and moqsrc + live catalog subscription Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant