Sign in

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?
In drafts 15–23 the SUBSCRIBE body carries a Request ID, the Track Namespace (a tuple) and the Track Name (bytes) that identify the track, and a list of parameters; draft-17 also has a Required Request ID Delta after the Request ID. Subscriber priority (SUBSCRIBER_PRIORITY, 0x20), group order (GROUP_ORDER, 0x22), the pause state (FORWARD 0x10 in drafts 15–22, PAUSED 0x11 in draft-23) and the filter (0x21) are parameters. In drafts 07–14 the priority, group order and filter are fixed fields of the body, as is Forward in drafts 11–14. The Track Alias is sent by the subscriber in SUBSCRIBE in drafts 07–11, and assigned by the publisher in SUBSCRIBE_OK from draft-12.
What is the difference between Track Namespace and Track Name in a SUBSCRIBE?
Both appear in every SUBSCRIBE. The Track Namespace is an ordered tuple that groups related tracks (e.g. ["sports","live"]); the Track Name is a byte string naming one track within it (e.g. "video"). Namespaces are prefix-matched in the discovery messages, but in a SUBSCRIBE the two together address exactly one track. In draft-23 the Track Name must not be empty.
How do I pause a MoQT subscription?
In drafts 15–22, send REQUEST_UPDATE (SUBSCRIBE_UPDATE in draft-15) with FORWARD (0x10) set to 0, and set it to 1 to resume; a SUBSCRIBE without FORWARD starts forwarding. In draft-23, send REQUEST_UPDATE with PAUSED (0x11) set to 1, and set it to 0 to resume; a SUBSCRIBE without PAUSED starts unpaused. In drafts 11–14, Forward is a fixed SUBSCRIBE field with the same meaning as FORWARD. A paused subscription stays established; the publisher just sends it no Objects.
What are the MoQT subscription filter types?
Drafts 12–19 define four Filter Types: Next Group Start (0x1) starts at the next group boundary; Largest Object (0x2) starts at the next Object after the largest one published; AbsoluteStart (0x3) starts at an explicit {group, object}; and AbsoluteRange (0x4) also has an explicit end group. Drafts 07–11 call 0x2 Latest Object; drafts 07–08 have Latest Group at 0x1 and drafts 09–10 have no 0x1. In drafts 20 and 21 the LOCATION_FILTER parameter has no type: its byte length says which of StartGroup, StartObject, EndGroupDelta and EndObject are present, a zero length means no filter, and StartGroup and StartObject both 0 mean the Next Object. Drafts 22 and 23 use a Location Filter Type from 0x00 to 0x05, in which the same numbers mean different things than in drafts 12–19. From draft-19, Range Filters (0x25–0x28) further restrict which Objects are delivered.
What does the relay send back to a SUBSCRIBE?
Exactly one response: SUBSCRIBE_OK, or SUBSCRIBE_ERROR in drafts 07–14 and REQUEST_ERROR in drafts 15–23. From draft-12, SUBSCRIBE_OK carries the Track Alias that the data streams use for this track. From draft-15 it also carries parameters such as LARGEST_OBJECT (where the live edge is) and EXPIRES, and from draft-16 these are followed by the track’s Track Properties (Track Extensions in draft-16). An error carries an error code such as unauthorized or does-not-exist, which usually means a namespace or name mismatch.

Related

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.