#[non_exhaustive]pub struct Subscription {
pub priority: u8,
pub max_age: Duration,
pub start: Option<Position>,
pub end: Option<Position>,
}Expand description
Subscriber-side preferences for receiving a track.
Each subscriber holds its own Subscription; the publisher observes an
aggregate across all live subscribers via crate::track::Producer::subscription.
A subscriber can change its preferences after the fact with
crate::track::Subscriber::update.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.priority: u8Delivery priority. Higher values preempt lower ones when bandwidth is constrained.
max_age: DurationHow old a group may get before this subscriber gives up on it.
Duration::ZERO (the default) skips immediately: group 8 arriving means group 7
is abandoned. A larger budget tolerates that much reordering before giving up.
This never adds delay, since the bound is only reached once newer data is
already that far ahead.
This is the Subscriber Max Age on the wire, and it is stored here verbatim so
what was asked for stays readable. Encoded as milliseconds in a QUIC varint, so
a duration of 2^62 milliseconds or more cannot be put on the wire. Clamped to
the publisher’s Info::max_age, since waiting for
a group longer than it is kept around cannot produce it.
§Where it is enforced
At both ends, and neither alone is enough. The publisher skips a group that has aged out instead of putting it on the wire, which bounds a backlog before it costs bandwidth. But what reaches the publisher is the aggregate across every subscriber, resolved in favor of the most tolerant one, so that gate is only ever as tight as the most patient viewer. The subscriber applies the same budget again as it reads, where its own is the only one in play.
This bounds a subscription.
track::Consumer::fetch_group is exempt:
it names one old group explicitly, so there is no live edge to be late against.
§How age is measured
In presentation time only. A group is measured by its reach, where its immediate successor begins, against the newest frame of the latest group: it cannot present past its successor, so once everything it could still hold falls outside the budget it is provably useless. The candidate needs no timestamp of its own, so an empty or stalled group is bounded by its stamped successor the same way. Wall-clock reclamation of idle content is the cache’s own policy, not this budget’s.
Protocols whose wire can’t carry a timestamp (pre-Lite05 moq-lite, moq-transport without the Timestamp property) have their frames stamped on receipt, which makes the measure burst-blind on the receiving side: thirty seconds of backlog delivered in three reads as three. The publisher’s copy is stamped as it produces, so the gate there still holds; it is just the coarser of the two.
start: Option<Position>The lowest Position the publisher may deliver, or None for no floor.
A floor, not a request: only Self::max_age asks for data, and the floor bounds
how far back it may reach. None and a floor of group 0 mean the same thing, since
nothing sits below group 0. Delivery starts at the oldest group at or above the
floor that the budget still considers fresh, so a floor above the live edge simply
waits there (a resumed subscription naming where it left off).
Aggregated across every live subscriber (the loosest floor wins, and any subscriber
without one clears it), so it says what the publisher sends, not what any one
subscriber sees. crate::track::Subscriber::set_groups is the local read cursor;
setting one does not imply the other. See Local cursor vs wire
preference.
end: Option<Position>First Position the publisher should not deliver, or None for no end.
Exclusive, like the end of a std::ops::Range, which is what lets one field
carry both “through the end of group 5” (Position::after_group(5))
and “up to frame 2 of group 5” (Position::after(5, 2)). An
inclusive end cannot express the first without a sentinel frame, and the ordering
falls out for free: group 6’s head sorts above any frame of group 5, so a
whole-group subscriber correctly absorbs a frame-capped one in the aggregate.
The wire agrees: Group End and Frame End are both encoded as absolute + 1.
A request, aggregated across every live subscriber (any unbounded subscriber makes
the aggregate unbounded). crate::track::Subscriber::set_groups is the local read
cursor; Position::group_end translates this field into its bound. Setting one
does not imply the other.
Implementations§
Source§impl Subscription
impl Subscription
Sourcepub fn with_priority(self, priority: u8) -> Self
pub fn with_priority(self, priority: u8) -> Self
Set the delivery priority, returning self for chaining.
Sourcepub fn with_max_age(self, max_age: Duration) -> Self
pub fn with_max_age(self, max_age: Duration) -> Self
Set how old a group may get before it is skipped, returning self for chaining.
Sourcepub fn with_start(self, start: impl Into<Option<Position>>) -> Self
pub fn with_start(self, start: impl Into<Option<Position>>) -> Self
Floor delivery at start, or leave it unfloored when None. Returns self for
chaining.
A floor bounds how far back Self::max_age may reach; it does not request data
on its own. Position::group is the whole-group form.
Sourcepub fn with_end(self, end: impl Into<Option<Position>>) -> Self
pub fn with_end(self, end: impl Into<Option<Position>>) -> Self
Stop delivery at end, or leave the subscription unbounded when None. Returns
self for chaining.
Exclusive, matching Self::end, so pass the position after the last one you
want. Position::after and Position::after_group name that conversion so no
call site has to write the + 1 itself.
Sourcepub fn with_groups(self, groups: impl RangeBounds<u64>) -> Self
pub fn with_groups(self, groups: impl RangeBounds<u64>) -> Self
Request the whole groups in groups, replacing both Self::start and
Self::end. Returns self for chaining.
Any range of group sequences works: 2..=5, 2..6, ..6, 2.., or .. to
clear both bounds. An inclusive end past the last group is unbounded, as
Position::after_group spells it.