Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
739082c
quest(m1): claim subscribe-ranges/dynamic
kixelated Oct 19, 2026
40d72f0
feat(moq-net): serve each subscriber its own ranges, with time anchors
kixelated Oct 19, 2026
d68f823
chore(moq-net): box the cursor's range filter, regenerate Dart bindings
kixelated Oct 19, 2026
81f8a87
docs: link the MIGRATE entries to #5929
kixelated Oct 19, 2026
8c8c02f
Merge remote-tracking branch 'origin/main' into merge-5929
kixelated Oct 19, 2026
7bea69b
Merge remote-tracking branch 'origin/main' into pr-5929
kixelated Oct 19, 2026
feb8a72
feat(moq-c): give the time anchor its own timestamp_us field
kixelated Oct 19, 2026
5d89b17
Merge branch 'main' of github.com:moq-dev/moq into pr-5929
kixelated Oct 19, 2026
13a2b33
fix(moq-c): refuse a raw track update at the call
kixelated Oct 19, 2026
9dd0ab4
fix(moq-net): resolve time anchors only from frame-0 starts, refuse d…
kixelated Oct 19, 2026
c015690
docs: name the time anchor in the Swift and Dart aliases and the C++ …
kixelated Oct 19, 2026
01cec52
quest(m1): the time anchor moves to exact timestamps with the bindings
kixelated Oct 19, 2026
b6e48df
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
52d2c4f
fix(moq-net): a lowered floor re-offers the cached groups it newly co…
kixelated Oct 19, 2026
e4f31c2
docs(moq-net): state the real bound on a time end; quest the one-grou…
kixelated Oct 19, 2026
d79f671
style(moq-net): name the passed-floor check in has_unread_group
kixelated Oct 19, 2026
4a46ebe
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
9b680ec
docs(moq-net): quest local frame bounds; fix the stale drift cap comment
kixelated Oct 19, 2026
b2cb366
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
bb03fca
fix(moq-net): a lowered floor re-offers only groups readers can see
kixelated Oct 19, 2026
dee0478
docs(drafts): log the lowered Group Start behavior in the lite-07 cha…
kixelated Oct 19, 2026
cee4bc0
fix(moq-net): a failover clamp never widens a range
kixelated Oct 19, 2026
7697958
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
a24bff1
fix(moq-net): an unresolved start never widens to the failover floor
kixelated Oct 19, 2026
d788a30
style(moq-net): format the pending-copy regression test
kixelated Oct 19, 2026
7060b26
fix(moq-net): a replacement copy never reads below where the reader g…
kixelated Oct 19, 2026
bf1105b
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
a4eea28
fix(moq-mux): read the floor through the optional subscription main i…
kixelated Oct 19, 2026
e212240
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 19, 2026
8036328
fix(moq-net): a cursor holds only its range, and widening re-scans th…
kixelated Oct 20, 2026
5f53bdd
fix(moq-net): a copy updated while pending names the ranges it will r…
kixelated Oct 20, 2026
6c77716
docs(moq-net): a finished track ends a capped subscriber
kixelated Oct 20, 2026
6db1f34
test(moq-net): apply the pending copy's update as the reader does
kixelated Oct 20, 2026
27f7080
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 20, 2026
23c4b3c
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 20, 2026
30cb4f1
fix(moq-net): a time end stays open across a group yet to start
kixelated Oct 20, 2026
03611bc
fix(moq-net): a time end settles only once every sequence below it is…
kixelated Oct 20, 2026
d679eaf
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 20, 2026
330ae11
test(moq-c): set refresh in the time anchor test's track info
kixelated Oct 20, 2026
ac9daca
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 20, 2026
9f0ec25
fix(moq-net): measure drift only within the groups a subscriber's ran…
kixelated Oct 20, 2026
07eb25e
Merge branch 'main' of github.com:moq-dev/moq into iterate-5929
kixelated Oct 20, 2026
472a40f
perf(moq-net): keep a single servable run as cheap as a cap
kixelated Oct 20, 2026
a7fe519
Merge origin/main into quest/m1/subscribe-ranges/time-anchors
kixelated Oct 20, 2026
00390f2
feat(moq-net): restore time anchors on main's per-subscriber ranges
kixelated Oct 20, 2026
3d031d7
feat(moq-net): regression tests, settle bench, and docs for time anchors
kixelated Oct 20, 2026
08ba001
style(moq-net): format time anchor tests and bench
kixelated Oct 20, 2026
cf2fb1c
docs: link time anchor migration notes to #6074
kixelated Oct 20, 2026
931ed72
Merge remote-tracking branch 'origin/main' into quest/m1/subscribe-ra…
kixelated Oct 20, 2026
e2976c7
Merge remote-tracking branch 'origin/main' into quest/m1/subscribe-ra…
kixelated Oct 20, 2026
bce205e
fix(moq-net): judge a waiting window's datagrams against time starts
kixelated Oct 20, 2026
ca6e68b
fix(moq-net): format the session bench's history in its id
kixelated Oct 20, 2026
7e09fee
fix(moq-net): judge every datagram against its range's times
kixelated Oct 20, 2026
fed180d
test(moq-net): build the single gone run without tripping clippy
kixelated Oct 20, 2026
1554bb3
fix(moq-net): keep absolute groups on a waiting window's datagrams
kixelated Oct 20, 2026
f33cbe8
fix(moq-net): charge the starts index to each cached group
kixelated Oct 20, 2026
c6a87f4
fix(moq-net): settle a time end on a group that starts mid-settle
kixelated Oct 20, 2026
9f3c64f
docs(moq-net): state the per-datagram rule for time anchors
kixelated Oct 20, 2026
bd5f77b
Merge remote-tracking branch 'origin/main' into quest/m1/subscribe-ra…
kixelated Oct 20, 2026
5b96b3f
fix(moq-net): end a time end's walks at the last sequence
kixelated Oct 20, 2026
68a40c7
fix(moq-net): read a missing sequence past a time end on speculation
kixelated Oct 20, 2026
4ef73d3
perf(moq-net): leave a time end uncapped past every held group
kixelated Oct 20, 2026
3ea2989
fix(moq-net): place a datagram's group against a time end by its start
kixelated Oct 20, 2026
771c155
fix(moq-net): judge datagrams by their anchors, and keep the starts a…
kixelated Oct 20, 2026
682b224
revert(moq-net): datagrams stay ephemeral, out of the starts index
kixelated Oct 20, 2026
05a2702
Merge remote-tracking branch 'origin/main' into quest/m1/subscribe-ra…
kixelated Oct 20, 2026
f108fa9
test(moq-net): gate the datagram time start test on async
kixelated Oct 20, 2026
1a9c8e7
Merge remote-tracking branch 'origin/main' into worktree-agent-ad19a9…
kixelated Oct 20, 2026
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
5 changes: 5 additions & 0 deletions cpp/moq/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`Anchor` gains a `Time` case holding `timestamp_us`, the group containing that presentation time, so exhaustive visits need a case for it.
`TrackConsumer::update` now returns a result, failing with `TimestampMismatch` on a time anchor an untimed track can't serve.

### JSON consumers return `JsonValue` (#5677)

`JsonSnapshotConsumer::next()` and `JsonStreamConsumer::next()` return `JsonValue` (`value`, `timestamp_us`) instead of a string; `timestamp_us` is empty when untimed.
Expand Down
5 changes: 5 additions & 0 deletions dart/moq/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`Anchor` gains a time variant, so exhaustive switches need a case for it.
`TrackConsumer.update` now throws on a time anchor an untimed track can't serve.

### JSON consumers return `Value` (#5677)

`SnapshotConsumer.next()` and `StreamConsumer.next()` return `Value` (`value`, `timestampUs`) instead of a string; `timestampUs` is null when untimed.
Expand Down
2 changes: 1 addition & 1 deletion dart/moq/lib/src/aliases.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ typedef Subscription = MoqSubscription;
/// A span of a track to deliver: from `start` up to `end`, or onward when `end` is null.
typedef Range = MoqRange;

/// Where one end of a [Range] falls: absolute or relative to the latest group.
/// Where one end of a [Range] falls: absolute, relative to the latest group, or the group containing a time.
typedef Anchor = MoqAnchor;

/// Which pending group the publisher sends first.
Expand Down
48 changes: 46 additions & 2 deletions dart/moq_ffi/lib/src/moq.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4268,6 +4268,12 @@ class FfiConverterMoqAnchor {
lifted.value,
lifted.bytesRead - subview.offsetInBytes + 4,
);
case 3:
final lifted = TimeMoqAnchor.read(subview);
return LiftRetVal<MoqAnchor>(
lifted.value,
lifted.bytesRead - subview.offsetInBytes + 4,
);
default:
throw UniffiInternalError(
UniffiInternalError.unexpectedEnumCase,
Expand Down Expand Up @@ -4377,6 +4383,44 @@ class RelativeMoqAnchor extends MoqAnchor {
}
}

class TimeMoqAnchor extends MoqAnchor {
final int timestampUs;
TimeMoqAnchor(int this.timestampUs);
TimeMoqAnchor._(int this.timestampUs);
static LiftRetVal<TimeMoqAnchor> read(Uint8List buf) {
int new_offset = buf.offsetInBytes;
final timestampUs_lifted = FfiConverterUInt64.read(
Uint8List.view(buf.buffer, new_offset),
);
final timestampUs = timestampUs_lifted.value;
new_offset += timestampUs_lifted.bytesRead;
return LiftRetVal(TimeMoqAnchor._(timestampUs), new_offset);
}

@override
RustBuffer lower() {
final buf = Uint8List(allocationSize());
write(buf);
return toRustBuffer(buf);
}

@override
int allocationSize() {
return FfiConverterUInt64.allocationSize(timestampUs) + 4;
}

@override
int write(Uint8List buf) {
buf.buffer.asByteData(buf.offsetInBytes).setInt32(0, 3);
int new_offset = buf.offsetInBytes + 4;
new_offset += FfiConverterUInt64.write(
timestampUs,
Uint8List.view(buf.buffer, new_offset),
);
return new_offset;
}
}

enum MoqOrder { ascending, descending }

class FfiConverterMoqOrder {
Expand Down Expand Up @@ -8354,7 +8398,7 @@ class MoqTrackConsumer implements MoqTrackConsumerInterface {
FfiConverterMoqSubscription.lower(subscription),
status,
);
}, null);
}, moqExceptionErrorHandler);
}
}

Expand Down Expand Up @@ -18558,7 +18602,7 @@ void _checkApiChecksums() {
if (uniffi_moq_ffi_checksum_method_moqtrackconsumer_recv_group() != 28712) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqtrackconsumer_update() != 24851) {
if (uniffi_moq_ffi_checksum_method_moqtrackconsumer_update() != 39083) {
throw UniffiInternalError.panicked("UniFFI API checksum mismatch");
}
if (uniffi_moq_ffi_checksum_method_moqgroupdemand_is_used() != 55523) {
Expand Down
9 changes: 7 additions & 2 deletions doc/concept/moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Each subscription carries the knobs that decide behavior under congestion:
| **Priority** (0..255) | Higher-priority tracks get bandwidth first. Audio above video, base layer above enhancement. From a native Rust publisher over QUIC or WebTransport (iroh excepted), subscriptions at the same priority split bandwidth evenly by bytes, streams and datagrams alike, however many groups each has queued. A browser publisher and other transports without send groups are not covered. |
| **Order** | Which group to send first when several are pending. Newest first for live (the default), oldest first for catch-up. Needs moq-lite 07 or moq-transport; older moq-lite always sends newest first. |
| **Max delay** | How long a non-latest group stays worth delivering before it is skipped. Zero means "live edge only", and raising it is also what asks for history. |
| **Ranges** | Which groups to ask for: from a group, or a number of groups back from the latest, optionally up to an end. The default asks for everything max delay allows. Max delay still caps every range. |
| **Ranges** | Which groups to ask for: from a group, a number of groups back from the latest, or the group containing a presentation time, optionally up to an end. The default asks for everything max delay allows. Max delay still caps every range. |

On moq-lite 05 and later, a publisher explicitly reports groups that will not arrive.
This lets a finished subscription settle without waiting for missing groups.
Expand All @@ -240,7 +240,12 @@ own ranges cover, resolved again whenever it replaces them, even when another
subscriber of the same track asked for more. Widening them, including lowering
the start, also serves the cached groups they newly cover, never one already
served. A relay merges its subscribers' ranges into one upstream request, which
today's wire versions carry as the single span covering them.
today's wire versions carry as the single span covering them. A time anchor is
resolved from the groups the serving hop holds; upstream it asks for everything
max delay allows, and an untimed track refuses it. At the live edge, a time end
may let one group through before its first frame shows it starts past the end:
the first group that has yet to start is handed out without waiting for that
frame, and the next one waits on it. A datagram is judged by its own timestamp.

Order only decides what goes first, never what is skipped, so catching up oldest
first needs a max delay that covers the backlog. When a relay serves subscribers of
Expand Down
5 changes: 5 additions & 0 deletions go/wrapper/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`AnchorTime` names the group containing a presentation time, and type switches over `Anchor` need a case for it.
`TrackConsumer.Update` also fails on a time anchor an untimed track can't serve.

### JSON consumers return `*json.Timed` (#5677)

`SnapshotConsumer.Next`, `StreamConsumer.Next`, and `All` return `*Timed{Value, Timestamp}` instead of the raw message; `Timestamp` is a `*time.Duration`, nil when untimed.
Expand Down
7 changes: 3 additions & 4 deletions go/wrapper/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,14 @@ func (t *TrackConsumer) Info() (TrackInfo, error) {
return trackInfoFromFFI(info), nil
}

// Update changes this subscriber's delivery preferences. It fails only on a
// negative MaxDelay.
// Update changes this subscriber's delivery preferences. It fails on a
// negative MaxDelay, or a time anchor the track can't serve.
func (t *TrackConsumer) Update(subscription Subscription) error {
sub, err := subscription.ffi()
if err != nil {
return err
}
t.inner.Update(sub)
return nil
return t.inner.Update(sub)
}

// Groups ranges over groups in sequence order.
Expand Down
4 changes: 3 additions & 1 deletion go/wrapper/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ type (
// A relative start is the first frame of its group, a relative end includes its whole
// group, and an absolute end is exclusive.
Range = ffi.MoqRange
// Anchor is where one end of a Range falls: AnchorAbsolute or AnchorRelative.
// Anchor is where one end of a Range falls: AnchorAbsolute, AnchorRelative, or AnchorTime.
Anchor = ffi.MoqAnchor
// AnchorAbsolute is an exact group and frame.
AnchorAbsolute = ffi.MoqAnchorAbsolute
// AnchorRelative is the group Groups before the latest one, clamped to group 0: 0 is the latest.
AnchorRelative = ffi.MoqAnchorRelative
// AnchorTime is the group containing TimestampUs, a presentation time in microseconds.
AnchorTime = ffi.MoqAnchorTime
// Order is which of a subscription's pending groups the publisher sends first.
Order = ffi.MoqOrder
// VideoDecoderOutput configures what a video.Consumer delivers: an optional resize, a max delay, and whether frames keep the decoder's surface (macOS only; refused elsewhere).
Expand Down
5 changes: 5 additions & 0 deletions kt/moq/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`Anchor` gains `AnchorTime`, so exhaustive `when` expressions need a branch for it.
`TrackConsumer.update` now throws `MoqException` on a time anchor an untimed track can't serve.

### JSON consumers emit timestamps (#5677)

`values()` emits `json.Value` (`value`, plus `timestamp` as a `Duration?`) instead of a string, and `valuesAs<T>()` emits `Timed<T>(value, timestamp)`.
Expand Down
4 changes: 3 additions & 1 deletion kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ typealias Route = uniffi.moq.MoqRoute
typealias Subscription = uniffi.moq.MoqSubscription
/** A span of a track to deliver: from `start` up to `end`, or onward when `end` is null. */
typealias Range = uniffi.moq.MoqRange
/** Where one end of a [Range] falls: [AnchorAbsolute] or [AnchorRelative]. */
/** Where one end of a [Range] falls: [AnchorAbsolute], [AnchorRelative], or [AnchorTime]. */
typealias Anchor = uniffi.moq.MoqAnchor
/** An exact group and frame. */
typealias AnchorAbsolute = uniffi.moq.MoqAnchor.Absolute
/** The group `groups` before the latest one, clamped to group 0: 0 is the latest. */
typealias AnchorRelative = uniffi.moq.MoqAnchor.Relative
/** The group containing `timestampUs`, a presentation time in microseconds. */
typealias AnchorTime = uniffi.moq.MoqAnchor.Time
/** Which pending group the publisher sends first: `ASCENDING` or `DESCENDING`. */
typealias Order = uniffi.moq.MoqOrder
/** Options for fetching one past group by sequence. */
Expand Down
5 changes: 5 additions & 0 deletions py/moq-rs/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`Anchor` gains a `TIME` variant, the group containing a presentation time.
`TrackConsumer.update` now raises on a time anchor an untimed track can't serve.

### JSON consumers yield `json.Timed` (#5677)

`json.SnapshotConsumer` and `json.StreamConsumer` yield `Timed(value, timestamp)` instead of the bare value; `timestamp` is a `timedelta`, or `None` when untimed.
Expand Down
5 changes: 4 additions & 1 deletion py/moq-rs/moq/subscribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ def info(self) -> TrackInfo:
return TrackInfo._from_ffi(self._inner.info())

def update(self, subscription: Subscription) -> None:
"""Change this subscriber's delivery preferences."""
"""Change this subscriber's delivery preferences.

Raises on a time anchor the track can't serve.
"""
self._inner.update(subscription._ffi())

def cancel(self) -> None:
Expand Down
6 changes: 4 additions & 2 deletions quest/m1/binding-timestamps.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ Public API: breaking in every binding.

Today every binding speaks microseconds: moq-ffi `timestamp_us: Option<u64>`
(about 160 sites in 9 files: `MoqFrame`, `MoqDatagram`, `MoqMediaFrame`, audio
frames, json/flate producer `update`/`append`, media `flush`), moq-c
`timestamp_us` plus `timestamp_present` pairs (about 140 sites in 6 files), and
frames, json/flate producer `update`/`append`, media `flush`, the
subscription start `MoqAnchor::Time`), moq-c `timestamp_us` plus
`timestamp_present` pairs (about 140 sites in 6 files, plus
`moq_anchor.timestamp_us` read as `MOQ_ANCHOR_TIME`), and
each wrapper's native type (Python `timedelta`, Go `*time.Duration`, Swift
`UInt64`, Kotlin `Duration`, Dart `int`, C++ `std::optional<uint64_t>`, OBS
microseconds). Reads floor through `as_micros()`, so any scale that does not
Expand Down
15 changes: 11 additions & 4 deletions quest/m1/subscribe-ranges/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,17 @@ relay bounds a sparse FETCH only if the model can request ranges:
has no time filter. It is part of lite-07 and must land before
[lite-07 finalizes](/quest/m1/lite07-finalize.md), which this line gates. This amends the 09-29
premise that ranges are group-only because a group's timestamp isn't always
known: timed tracks know it, and untimed ones refuse. Split out of #5929
(2026-10-19) into [time anchors](/quest/m1/subscribe-ranges/time-anchors.md),
which lands in its own PR.
known: timed tracks know it, and untimed ones refuse. The cache half landed in its own PR after #5929.
A datagram is judged on its own (maintainer, 2026-10-20): its timestamp
against the range's times, its sequence against the absolute and resolved
relative groups, never through the settle walk. Refusing time anchors on
datagram tracks was considered and can't work: nothing signals that a track
carries datagrams, and the lite draft lets a publisher send any single-frame
group as a datagram, including a copy of one it also streams. Datagrams stay
ephemeral (maintainer, 2026-10-20): a buffered datagram never indexes where
its group starts, so a time end settles across a sequence that arrived only
as a datagram, and a stream copy of that group arriving later is read even
if it starts past the end.
- Ranges are frame-precise (`Position`), not whole groups. The IETF joining
FETCH for a mid-group SUBSCRIBE's uncached prefix stays, because today's
bridge relies on it (maintainer, 09-29).
Expand All @@ -92,7 +100,6 @@ every sequence delivered or dropped.
- [moq-transport ranges](/quest/m1/subscribe-ranges/ietf.md) - range FETCH upstream per missing run, and non-blocking FETCH served downstream
- [JS ranges](/quest/m1/subscribe-ranges/js.md) - `@moq/net` model and lite-07 parity
- [Frame bounds](/quest/m1/subscribe-ranges/frame-bounds.md) - a local subscriber reads only the frames its ranges cover
- [Time anchors](/quest/m1/subscribe-ranges/time-anchors.md) - a range start or end may name a presentation time

## Related

Expand Down
4 changes: 2 additions & 2 deletions quest/m1/subscribe-ranges/dynamic.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ kept beside it.

A subscriber already reads only its own ranges, re-resolved when they are
replaced. What remains is the miss: one below the cache becomes a range request
on `Dynamic` rather than a group request. Time anchors land separately; see
[time anchors](/quest/m1/subscribe-ranges/time-anchors.md).
on `Dynamic` rather than a group request. A time anchor the cache can't
resolve passes to the handler unresolved in its range request.

- Replace `requested_group` and `fetch_group` rather than keep both. Update
every caller (relay, moq-archive, hls, the ladder) in the same PR,
Expand Down
56 changes: 0 additions & 56 deletions quest/m1/subscribe-ranges/time-anchors.md

This file was deleted.

6 changes: 6 additions & 0 deletions rs/moq-c/MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ How to update your code for each breaking change, newest first.

## Unreleased

### Anchors can name a time (#6074)

`moq_anchor` gains `timestamp_us`, read with the new `MOQ_ANCHOR_TIME` kind, which changes its ABI.
`moq_consume_track_update` fails with a negative code when the track refuses an update, such as a time anchor on an untimed track, leaving the subscription as it was.
An update made before the subscription is established applies once it is, so a refusal then ends the consumer through its callback.

### JSON values carry their timestamp (#5677)

`moq_json_value` gains `timestamp_us` and `timestamp_present`, which changes its ABI.
Expand Down
Loading
Loading