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
39 changes: 10 additions & 29 deletions draft-lcurley-moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,9 @@ A Frame is a payload of bytes within a Group.
A frame is used to represent a chunk of data with an upfront size.
The contents are opaque to the moq-lite layer.

Each frame carries a presentation timestamp expressed in the parent Track's `Timescale` (units per second, part of the [TRACK_INFO](#track-info)), and a duration in the same scale.
Each frame carries a presentation timestamp expressed in the parent Track's `Timescale` (units per second, part of the [TRACK_INFO](#track-info)).
The timestamp is the source-of-truth for media time and is used by the moq-lite layer for [expiration](#expiration) decisions instead of wall-clock arrival time.
The duration is a hint for the application layer (e.g. presentation scheduling) and is not used by moq-lite itself; a duration of `0` means unknown and the frame is presented until the next frame begins.
A Track with a `Timescale` of 0 (unspecified) carries no meaningful timestamps or durations and falls back to wall-clock arrival time for expiration.
A Track with a `Timescale` of 0 (unspecified) carries no meaningful timestamps and falls back to wall-clock arrival time for expiration.

# Flow
This section outlines the flow of messages within a moq-lite session.
Expand Down Expand Up @@ -495,13 +494,12 @@ DATAGRAM Body {
Subscribe ID (i)
Group Sequence (i)
[Timestamp (i)]
[Duration (i)]
Payload (b)
}
~~~

`Timestamp` and `Duration` are present only when the Track's `Publisher Timescale` (see [TRACK_INFO](#track-info)) is non-zero.
When `Publisher Timescale` is 0, both fields are omitted from the wire and the datagram body consists of just `Subscribe ID`, `Group Sequence`, and `Payload`.
`Timestamp` is present only when the Track's `Publisher Timescale` (see [TRACK_INFO](#track-info)) is non-zero.
When `Publisher Timescale` is 0, the field is omitted from the wire and the datagram body consists of just `Subscribe ID`, `Group Sequence`, and `Payload`.

**Subscribe ID**:
The Subscribe ID of an active subscription on the same session.
Expand All @@ -515,10 +513,6 @@ Each datagram represents a complete group containing exactly one frame.
The absolute timestamp of the single frame in the group, expressed in the Track's negotiated `Timescale`.
Any varint value (including 0) is a valid absolute timestamp.

**Duration**:
The absolute duration of the frame, expressed in the Track's negotiated `Timescale`.
A value of `0` means the duration is unknown; the frame is presented until the next frame begins (or indefinitely, since a datagram-delivered group contains exactly one frame, until the application supersedes it).

**Payload**:
The frame payload, extending to the end of the datagram.
If the Track's `Publisher Compression` is non-zero, the payload is compressed using the negotiated algorithm (see [TRACK_INFO](#track-info)).
Expand Down Expand Up @@ -847,7 +841,7 @@ The unit is milliseconds (independent of `Publisher Timescale`) so cache retenti
**Publisher Timescale**:
The number of timestamp units per second for frame timestamps on this Track.
A value of 0 means unspecified; the subscriber MUST treat per-frame timestamps as opaque and fall back to wall-clock arrival time for [expiration](#expiration).
When `Publisher Timescale` is 0, the per-frame `Timestamp Delta` and `Duration Delta` fields are omitted from FRAME messages and the `Timestamp` and `Duration` fields are omitted from datagram bodies (see [FRAME](#frame) and [Datagrams](#datagrams)).
When `Publisher Timescale` is 0, the per-frame `Timestamp Delta` field is omitted from FRAME messages and the `Timestamp` field is omitted from datagram bodies (see [FRAME](#frame) and [Datagrams](#datagrams)).
Common values include `1000` (milliseconds), `1000000` (microseconds), `48000` (audio sample rate), and `90000` (RTP video clock).

**Publisher Compression**:
Expand Down Expand Up @@ -1044,14 +1038,13 @@ The FRAME message is a payload within a group.
~~~
FRAME Message {
[Timestamp Delta (i)]
[Duration Delta (i)]
Message Length (i)
Payload (b)
}
~~~

`Timestamp Delta` and `Duration Delta` are present only when the Track's `Publisher Timescale` (see [TRACK_INFO](#track-info)) is non-zero.
When `Publisher Timescale` is 0, both fields are omitted from the wire and the FRAME consists of just `Message Length` and `Payload`.
`Timestamp Delta` is present only when the Track's `Publisher Timescale` (see [TRACK_INFO](#track-info)) is non-zero.
When `Publisher Timescale` is 0, the field is omitted from the wire and the FRAME consists of just `Message Length` and `Payload`.

**Timestamp Delta**:
A signed delta from the previous frame's timestamp, in the Track's negotiated `Timescale`.
Expand All @@ -1063,18 +1056,6 @@ Encoded as a zigzag-mapped variable-length integer:
Zigzag interleaves non-negative and negative values (`0 → 0, -1 → 1, 1 → 2, -2 → 3, 2 → 4, ...`) so small magnitudes of either sign fit in a 1-byte varint and there is exactly one wire encoding for zero.
The first frame of a group is delta-encoded from `0`, so its `Timestamp Delta` is the zigzag encoding of the absolute timestamp.

**Duration Delta**:
A signed delta from the previous frame's duration, in the Track's negotiated `Timescale`, encoded using the same zigzag mapping as `Timestamp Delta`.
A wire value of `0` means the duration is unchanged from the previous frame; this is the common case for constant-rate media and fits in one byte.
The first frame of a group is delta-encoded from a prior duration of `0`.

The resolved duration value carries the following semantics for the application:

- A resolved duration of `0` means the duration is unknown; the frame is presented until the next frame in the group begins (or until the group ends, if it is the last frame).
- A non-zero resolved duration is the explicit presentation duration in the Track's `Timescale`.

The duration is an application-level hint and is not used by the moq-lite layer for delivery decisions.

**Payload**:
An application-specific payload.
If the Track's `Publisher Compression` is non-zero, the payload is compressed using the negotiated algorithm (see [TRACK_INFO](#track-info)) and the `Message Length` describes the compressed size.
Expand All @@ -1093,9 +1074,9 @@ A generic library or relay MUST NOT inspect or modify the decompressed contents
- Renamed `Start Group`/`End Group` to `Group Start`/`Group End` in SUBSCRIBE, SUBSCRIBE_UPDATE, and SUBSCRIBE_DROP for consistency with the entity-first naming used elsewhere (e.g. `Group Sequence`). Wire format unchanged.
- Allowed a duplicate `active` ANNOUNCE to atomically replace the prior advertisement (equivalent to UNANNOUNCE+ANNOUNCE). Used when only the origin or hop path changes (e.g. relay failover) without interrupting the broadcast. No new wire enum value — the existing `active` status carries the new metadata.
- Added ANNOUNCE_OK message, sent once at the head of the Announce Stream response. Carries the publisher's `Hop ID` (hoisted out of every ANNOUNCE's Hop ID list) and an `Active Count` so subscribers can batch the initial set instead of reporting each ANNOUNCE as it trickles in.
- Added `Publisher Timescale` to TRACK_INFO for per-track timestamp negotiation. When `Publisher Timescale` is 0, the per-frame timestamp/duration fields are omitted entirely from FRAME and datagram bodies.
- Added `Timestamp Delta` and `Duration Delta` to FRAME, both zigzag-encoded signed varints (present only when timescale is non-zero). `Duration Delta = 0` is the common "unchanged" case and fits in one byte; a resolved duration of `0` means "until the next frame".
- Added `Timestamp` and `Duration` to the QUIC datagram body (absolute, present only when timescale is non-zero).
- Added `Publisher Timescale` to TRACK_INFO for per-track timestamp negotiation. When `Publisher Timescale` is 0, the per-frame timestamp field is omitted entirely from FRAME and datagram bodies.
- Added `Timestamp Delta` to FRAME, a zigzag-encoded signed varint (present only when timescale is non-zero).
- Added `Timestamp` to the QUIC datagram body (absolute, present only when timescale is non-zero).
- Renamed `Publisher Max Latency` to `Publisher Cache` (now in TRACK_INFO), defined as a minimum retention guarantee (similar to HTTP `Cache-Control: max-age`). Groups may live longer than `Publisher Cache` and remain FETCH-able.
- Renamed `Subscriber Max Latency` to `Subscriber Stale` in SUBSCRIBE/SUBSCRIBE_UPDATE. It is the subscriber's delivery-time preference for dropping non-latest stale groups, separate from the publisher's retention guarantee.
- Timestamp-based expiration replaces wall-clock arrival time when a Track timescale is negotiated.
Expand Down
33 changes: 7 additions & 26 deletions draft-lcurley-moq-timestamp.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ informative:

--- abstract

This document defines an extension for MoQ Transport {{moqt}} that attaches a media presentation timestamp and duration to each object.
A track-level Timescale property establishes the units, an object-level Timestamp property carries the presentation time of each object, and an optional Duration property carries its presentation duration.
This document defines an extension for MoQ Transport {{moqt}} that attaches a media presentation timestamp to each object.
A track-level Timescale property establishes the units, and an object-level Timestamp property carries the presentation time of each object.
Exposing media time to the transport lets relays make consistent age-based decisions (e.g. dropping stale objects) without parsing the media container, and it remains consistent across hops regardless of buffering or jitter.

--- middle
Expand All @@ -46,7 +46,7 @@ A relay frequently needs a notion of *when* an object is meant to be presented:
MoQ also demultiplexes media into many independent tracks — audio, video, captions, metadata, and more — so a timestamp is needed on nearly every track.
Re-implementing per-object timestamping inside each application's container format, for every track, is repetitive and error-prone; standardizing it at the transport lets one implementation serve every track and lets relays use it directly.

This extension exposes media time to the transport with three Key-Value-Pairs ({{moqt}} Section 2.5): a track-level **Timescale**, an object-level **Timestamp**, and an optional object-level **Duration**.
This extension exposes media time to the transport with two Key-Value-Pairs ({{moqt}} Section 2.5): a track-level **Timescale** and an object-level **Timestamp**.
The transport does not interpret the *meaning* of the timeline (it is still the application's clock); it only uses the timestamp for relative age comparisons.


Expand All @@ -67,7 +67,7 @@ A relay MAY perform timestamp-based dropping for a track only if the upstream pu


# TIMESCALE Track Property
The TIMESCALE property establishes the units for every Timestamp and Duration on a track.
The TIMESCALE property establishes the units for every Timestamp on a track.
It is a track-level Key-Value-Pair, carried with the track's properties (see {{moqt}} Section 2.5 and Section 12).
Because the value is a single integer, TIMESCALE uses an even Type so the value is a bare varint with no length prefix:

Expand All @@ -81,11 +81,11 @@ TIMESCALE Track Property {
**Value**:
The number of timestamp units per second.
Common values include `1000` (milliseconds), `1000000` (microseconds), `48000` (a typical audio sample rate), and `90000` (the RTP video clock).
A value of `0`, or the absence of the property, means the track has no media timeline: Timestamp and Duration properties, if present, MUST be ignored, and a relay MUST fall back to wall-clock arrival time for any age-based decision.
A value of `0`, or the absence of the property, means the track has no media timeline: Timestamp properties, if present, MUST be ignored, and a relay MUST fall back to wall-clock arrival time for any age-based decision.

The Timescale is fixed for the lifetime of the track and MUST NOT change.

The Timescale is required to interpret the units of every Timestamp and Duration, so a receiver cannot resolve an object's timing until it has the track's properties.
The Timescale is required to interpret the units of every Timestamp, so a receiver cannot resolve an object's timing until it has the track's properties.
Those properties are delivered in SUBSCRIBE_OK or TRACK_STATUS ({{moqt}} Section 12), so a receiver that begins receiving objects before it has them MUST buffer the timing (or treat it as unknown) until the Timescale arrives.
A relay that has not yet learned the Timescale MUST fall back to wall-clock arrival time for any age-based decision.

Expand Down Expand Up @@ -117,24 +117,6 @@ This decision is identical at every hop because it depends only on values embedd
A relay MUST NOT use timestamps to reorder delivery beyond what {{moqt}} already permits; this property informs *dropping*, not transmission order.


# DURATION Object Property
The DURATION property carries the presentation duration of an object, in the track's Timescale.
It is optional and is an object-level Key-Value-Pair with an even Type:

~~~
DURATION Object Property {
Type (vi64) = 0x915C4
Value (vi64) ; presentation duration, in Timescale units
}
~~~

**Value**:
The presentation duration of the object, expressed in the track's Timescale.
A value of `0`, or the absence of the property, means the duration is unknown; the object is presented until the next object begins.

Duration is primarily an application-level presentation hint, but a relay MAY also use it to refine age-based dropping: an object's Timestamp plus its Duration marks the end of its presentation interval, which is a more precise "this object is now in the past" signal than the Timestamp alone (for example, the last object of a group has no following object to bound it). A relay MUST NOT rely on Duration being present; when it is absent, the relay falls back to comparing Timestamps as in [Age-Based Dropping](#age-based-dropping).


# Security Considerations
Timestamps expose the media timeline to relays, which is the point of the extension, but a relay still treats payloads as opaque and gains no access to media content.

Expand All @@ -147,7 +129,7 @@ Because age-based dropping only affects which objects a live subscription receiv

This document requests the following registrations.
High, distinctive values are requested to avoid the low ranges reserved by {{moqt}} and to minimize collisions with provisional registrations by other extensions; they also avoid the greasing pattern (`0x7f * N + 0x9D`).
The three property Types are even so that each value is a bare varint with no length prefix (see {{moqt}} Section 2.5).
The property Types are even so that each value is a bare varint with no length prefix (see {{moqt}} Section 2.5).

## MOQT Setup Options

Expand All @@ -165,7 +147,6 @@ This document requests registrations in the "MOQT Properties" registry ({{moqt}}
|:--------|:----------|:-------|:--------------|
| 0x915C0 | TIMESCALE | Track | This Document |
| 0x915C2 | TIMESTAMP | Object | This Document |
| 0x915C4 | DURATION | Object | This Document |


--- back
Expand Down
Loading