Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions doc/bin/gstreamer.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ nix shell github:moq-dev/moq#moq-gst --command gst-launch-1.0 -e \
# Publish a test pattern
gst-launch-1.0 -e videotestsrc is-live=true ! x264enc tune=zerolatency ! h264parse \
! video/x-h264,stream-format=byte-stream,alignment=au ! mux.sink_0 \
moqsink name=mux url=https://cdn.moq.dev/anon broadcast=<your-name>.hang
moqsink name=mux url=https://cdn.moq.dev/anon broadcast=<your-name>.hang sink_0::encoder=true
```

Install via `apt install gstreamer1.0-moq` or `dnf install gstreamer1-moq`
Expand All @@ -48,7 +48,8 @@ directly. A cue with no duration is dropped rather than left on screen.

Each `sink_%u` request pad is one track. Pad properties: `track` names it
(default: after the codec), `container=loc` publishes it as
[LOC](/concept/standard#loc) instead of the legacy hang container, and
[LOC](/concept/standard#loc) instead of the legacy hang container,
`encoder=true` marks it as fed by a local encoder, and
`track-status`/`track-error` report its lifecycle. Element properties:
`url`, `broadcast`, `tls-disable-verify`, `quic-idle-timeout`,
`quic-keep-alive`, and read-only `status`, `connected`, `moq-version`, and
Expand All @@ -73,6 +74,14 @@ while connected and 0 otherwise, reconnects are `started - 1` once `started`
is at least 1, and a rate is the delta over any window you sample. Unlike
`status`, a connection that drops before you poll still moves both counters.

Set `encoder=true` on audio and video pads a local encoder feeds
(`x264enc`, `opusenc`, ...). The pad then measures how late each frame reaches
the sink behind its running time and raises the catalog `jitter` by the spread,
so players buffer for an encoder that delivers irregularly. Leave it off, the
default, for file, demuxed, and network media: their arrival reflects the disk
or the network, not the original encoder, and a GStreamer segment cannot tell
the two apart. Text and opaque pads refuse it.

## moqsrc

Pads are named by kind and appear as the catalog announces renditions:
Expand Down
1 change: 0 additions & 1 deletion quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [IETF announce count](/quest/m1/ietf-announce-count.md) - an opt-in moq-transport extension carries the replay count, so IETF announce consumers go live without a timer

- [Jitter clock](/quest/m1/jitter-flush-clock.md) - renditions advertise `delay` (lag behind the earliest track) and `jitter` (spread), measured at encoder flush, never lowered; js/watch sizes playout over what it subscribes
- [GStreamer encoder jitter](/quest/m1/gst-encoder-jitter-provenance.md) - only opted-in local encoder pads feed the flush clock
- [Data jitter](/quest/m1/data-jitter.md) - JSON and binary tracks with a capture time advertise a detected `delay` and `jitter`
- [Play tune-in backpressure](/quest/m1/play-tunein-backpressure.md) - moq play: a tune-in burst larger than the video queue parks the decoder, so the clock never reaches live at a wide `--delay`
- [JavaScript FETCH](/quest/m1/js-fetch.md) - generic on-demand group serving and IETF FETCH for browser publishers
Expand Down
23 changes: 0 additions & 23 deletions quest/m1/gst-encoder-jitter-provenance.md

This file was deleted.

7 changes: 5 additions & 2 deletions quest/m1/jitter-flush-clock.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ media span of each emitted batch and advertise no `delay`.
libmoq (so OBS), moq-ffi and its wrappers, and the `js/publish` encoders call
it. The provisional PTS-gap floor is gone, and `moq_mux::Error::JitterDecreased`
plus zero-as-absent text jitter enforce never-lower in Rust and JS.
`moq-gst` is split into [GStreamer encoder jitter](/quest/m1/gst-encoder-jitter-provenance.md).
`moq-gst` pads opt in with `encoder=true`; imports stay clock-free.
What remains below is `delay` and the player. libmoq and moq-ffi expose
`flush` but no discontinuity, so a binding publisher that pauses and resumes
on a re-anchored PTS within the window would count the pause; add one when
such a caller appears.
such a caller appears. A `moq-gst` encoder pad has the same gap across a
`PLAYING -> PAUSED -> PLAYING` cycle (running time stops, the wall clock
does not) and a flushing seek, since `import::Track` forwards no
discontinuity.
- **Measurement.** Lateness is `now - timestamp`, observed by the existing
`flush` calls, so no call site changes. Each rendition keeps its own
baseline, the minimum lateness over a sliding window (about 10 s), so a media
Expand Down
10 changes: 7 additions & 3 deletions rs/moq-gst/src/sink/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//! pad lifecycle, then an object lock. No path takes the element control while holding a pad lifecycle.

use std::sync::{LazyLock, Mutex};
use std::time::Duration;
use std::time::{Duration, Instant};

use anyhow::{Context, Result};
use bytes::Bytes;
Expand Down Expand Up @@ -685,7 +685,9 @@ impl MoqSink {
if lifecycle.media.is_failed() {
return Ok(gst::FlowSuccess::Ok);
}
let outcome = lifecycle.media.push_buffer(data, pts, duration, current_running_time);
let outcome = lifecycle
.media
.push_buffer(data, pts, duration, current_running_time, Instant::now());
let changes = match &outcome {
Ok(PushOutcome::Failed(reason)) => Some(lifecycle.fail(reason.clone())),
_ => None,
Expand Down Expand Up @@ -772,7 +774,9 @@ impl MoqSink {
return false;
}
let requested = lifecycle.requested().map(str::to_owned);
let mut options = ProducerOptions::new(&caps).with_container(lifecycle.container().into());
let mut options = ProducerOptions::new(&caps)
.with_container(lifecycle.container().into())
.with_encoder(lifecycle.encoder());
if let Some(track) = requested.as_deref() {
options = options.with_track(track);
}
Expand Down
Loading
Loading