MoQT explained
The MoQT SUBSCRIBE message
A SUBSCRIBE in Media over QUIC Transport is not a one-shot GET —
it is a standing request for a track. Its structure answers three questions:
which track (Track Namespace + Track Name), from where
(the filter), and how (subscriber priority, group order, and whether delivery
starts paused). The publisher replies with exactly one
SUBSCRIBE_OK or error,
and Objects then flow on their own streams.
The field layout below is the SUBSCRIBE body of draft-ietf-moq-transport
drafts 15–23 (draft-17 adds one field). Earlier drafts are covered further down. For byte-exact ordering in one draft, see
the draft-23 SUBSCRIBE section;
for the type code SUBSCRIBE carries in every draft, see the
control message type reference.
SUBSCRIBE fields
| Field | Type | What it does |
|---|---|---|
| Request ID | varint · body | Identifier for this request. The response, later REQUEST_UPDATEs and teardown are keyed to it. Drafts 07–10 call it Subscribe ID. From draft-19 an endpoint may hold several subscriptions to one track at once, each with its own Request ID. |
| Track Namespace | tuple · body | Ordered tuple of byte-string fields identifying the namespace, e.g. ["sports","live"]. |
| Track Name | bytes · body | Byte string naming the track within the namespace, e.g. "video". Matched exactly. In draft-23 it must be at least one byte long: an empty Track Name closes the session with PROTOCOL_VIOLATION. |
| Parameters | count + params | Everything else: priority, group order, pause state, the Location Filter, delivery timeouts, the authorization token and the Range Filters (see the table below). |
The Track Alias is not in the SUBSCRIBE: the publisher assigns it in
SUBSCRIBE_OK. The two identifiers people most often confuse are
Track Namespace vs Track Name.
The namespace is a tuple that groups tracks; the name addresses one track inside it.
The parameters a SUBSCRIBE uses most
| Parameter | Type | Drafts | What it does |
|---|---|---|---|
| SUBSCRIBER_PRIORITY | 0x20 | 15–23 | How this subscription ranks against the session’s other subscriptions and fetches. Lower value = higher priority. |
| GROUP_ORDER | 0x22 | 15–23 | Requested delivery order across groups: Ascending (0x1, oldest first) or Descending (0x2, newest first). |
| FORWARD | 0x10 | 15–22 | 1 = send Objects (the default); 0 = establish the subscription but send no Objects until an update sets it to 1. |
| PAUSED | 0x11 | 23 | 0 = not paused (the default); 1 = establish the subscription paused. A REQUEST_UPDATE with PAUSED = 0 resumes it. |
| SUBSCRIPTION_FILTER | 0x21 | 15–18 | Where in the track delivery starts (and, optionally, ends). |
| LOCATION_FILTER | 0x21 | 19–23 | The same filter under its later name. Its encoding differs by draft range (see below). |
| SUBGROUP_FILTER … OBJECT_PROPERTY_FILTER | 0x25–0x28 | 19–23 | Range Filters: restrict which Objects within the subscription are delivered, by Subgroup ID, Object ID, publisher priority or an Object Property. |
| FILL_PARAMETERS | 0x23 | 20–23 | Asks the publisher to also deliver Objects before the live edge on a separate fill fetch stream. |
| INCLUDE_PROPERTIES | 0x35 | 20–23 | Whether SUBSCRIBE_OK carries Track Properties. |
A SUBSCRIBE can also carry AUTHORIZATION_TOKEN (0x03) and the delivery
timeouts. Drafts 22 and 23 list exactly which parameters a SUBSCRIBE may carry; any other one closes the
session.
The SUBSCRIBE body in every draft
| Drafts | Fields, in order |
|---|---|
| 07–10 | Subscribe ID, Track Alias, Track Namespace, Track Name, Subscriber Priority, Group Order, Filter Type and its Start / End fields, Parameters |
| 11 | Request ID, Track Alias, Track Namespace, Track Name, Subscriber Priority, Group Order, Forward, Filter Type and its fields, Parameters |
| 12–14 | Request ID, Track Namespace, Track Name, Subscriber Priority, Group Order, Forward, Filter Type and its fields, Parameters. The Track Alias comes back in SUBSCRIBE_OK. |
| 15–16, 18–23 | Request ID, Track Namespace, Track Name, Parameters |
| 17 | Request ID, Required Request ID Delta, Track Namespace, Track Name, Parameters |
In drafts 07–14 the publisher answers with SUBSCRIBE_OK or
SUBSCRIBE_ERROR; in drafts 15–23 with SUBSCRIBE_OK
or REQUEST_ERROR.
The filter: where delivery starts
The filter is the most consequential field. It answers from which point in the track do I want objects? — in terms of groups, because a group boundary is the only safe place to start decoding. It has three encodings, depending on the draft.
Drafts 07–19: a Filter Type
| Filter | Code | Start location | End | Use it when |
|---|---|---|---|---|
| Largest Object | 0x2 | {Largest.Group, Largest.Object + 1} | open | Join a live track now and get everything from here on |
| Next Group Start | 0x1 | {Largest.Group + 1, 0} | open | Join live at the next group boundary |
| AbsoluteStart | 0x3 | explicit {Group, Object} | open | Everything from a known point forward |
| AbsoluteRange | 0x4 | explicit {Group, Object} | End Group (End Group Delta in 17–19) | A bounded window of the track |
These are the names in drafts 12–19. Drafts 07–11 call 0x2 Latest Object. 0x1 is Latest Group in drafts 07–08, is
not defined in drafts 09–10, and is Next Group Start from draft-11. In drafts 07–14 the Filter Type
is a fixed SUBSCRIBE field; in drafts 15–19 it leads the filter parameter (0x21).
Drafts 20–21: the parameter length
LOCATION_FILTER has no type. Its byte length says which of
StartGroup, StartObject,
EndGroupDelta and EndObject are present. A zero
length means no filter; a lone StartGroup counts back from the next group; a
StartGroup and StartObject that are both 0 mean
the Next Object.
Drafts 22–23: a Location Filter Type
| Type | Name | Fields that follow | Meaning |
|---|---|---|---|
| 0x00 | None | — | No Location Filter |
| 0x01 | Relative Start | StartGroup | Starts at {Largest.Group + 1 − StartGroup, 0}; StartGroup 0 is the next group |
| 0x02 | Absolute Start | StartGroup, StartObject | Starts at that Location, open-ended |
| 0x03 | Absolute Start, Group End | + EndGroupDelta | Ends with the last Object of group StartGroup + EndGroupDelta |
| 0x04 | Absolute Range | + EndObject | Ends at {StartGroup + EndGroupDelta, EndObject} |
| 0x05 | Next Object | — | Starts at the next Object the publisher sends |
The same numbers mean different things than in drafts 07–19, and an Absolute Start of
{0, 0} is the beginning of the track: only type
0x05 means the Next Object. Any other type closes the session
(PROTOCOL_VIOLATION in draft-22, INVALID_PARAMETER
in draft-23). The full filter walkthrough is in
How MoQT subscriptions work.
A draft-23 SUBSCRIBE, sketched
SUBSCRIBE (Type 0x3)
Request ID = 4
Track Namespace = ["sports","live","game42"]
Track Name = "video"
Parameters:
OBJECT_DELIVERY_TIMEOUT (0x02) = 2000 ms
SUBSCRIBER_PRIORITY (0x20) = 0
LOCATION_FILTER (0x21) = Relative Start (0x01), StartGroup = 0
(starts at the next group)
GROUP_ORDER (0x22) = Ascending (0x1)
─► SUBSCRIBE_OK (Type 0x4)
Track Alias = 1
Parameters:
LARGEST_OBJECT (0x09) = {group 812, object 4}
Track Properties: (none)
With no PAUSED parameter the subscription starts unpaused. Parameters are
sent in ascending type order.
Frequently asked questions
What is the structure of the MoQT SUBSCRIBE message?
What is the difference between Track Namespace and Track Name in a SUBSCRIBE?
How do I pause a MoQT subscription?
What are the MoQT subscription filter types?
What does the relay send back to a SUBSCRIBE?
Related
- Track Namespace vs Track Name
- SUBSCRIBE_NAMESPACE and track namespace prefix matching
- How MoQT subscriptions work: filters, forwarding, and lifecycle
- MoQT control message type reference (every draft, 07–23)
- MoQT spec — the SUBSCRIBE message (draft-23, normative text)
- Subscribing to tracks — hands-on guide
Decode SUBSCRIBE on the wire
moqtap shows the exact filter, pause state, priority, and Largest Object your implementation sent and received — so you can see whether a stalled subscription is your filter, your auth, or the publisher.