§5.1.

Subscriptions

All subscriptions begin in the Idle state. A subscription can be initiated and moved to the Pending state by either a publisher or a subscriber. A publisher initiates a subscription to a track by sending the PUBLISH message. The subscriber either accepts or rejects the subscription using PUBLISH_OK (Section 10.5) or REQUEST_ERROR. A subscriber initiates a subscription to a track by sending the SUBSCRIBE message. The publisher either accepts or rejects the subscription using SUBSCRIBE_OK or REQUEST_ERROR. Once either of these sequences is successful, the subscription moves to the Established state and can be updated by the subscriber using REQUEST_UPDATE. Either endpoint can terminate an Established subscription, moving it to the Terminated state. The subscriber terminates a subscription in the Pending (Subscriber) or Established states by sending STOP_SENDING. The publisher terminates a subscription in the Pending (Publisher) or Established states by sending PUBLISH_DONE and closing the stream.

This diagram shows the subscription state machine:

                              +--------+
                              |  Idle  |
                              +--------+
                                |    |
                      SUBSCRIBE |    | PUBLISH
                    (subscriber)|    | (publisher)
                                V    V
                   +--------------+ +--------------+
                   | Pending      | | Pending      |
              +----| (Subscriber) | | (Publisher)  |----+
              |    +--------------+ +--------------+    |
              |                 |    |                  |
REQUEST_ERROR |    SUBSCRIBE_OK |    | PUBLISH_OK       | REQUEST_ERROR
(publisher)   |      (publisher)|    | (subscriber)     | (subscriber)
              |                 V    V                  |
              |            +-------------+              |
              |            | Established | ------+
              |            |             |       | REQUEST_UPDATE
              |            +-------------+ <-----+
              |                 |    |                  |
              +--- STOP_SENDING |    | PUBLISH_DONE ----+
              |     (subscriber)|    | (publisher)      |
              |                 V    V                  |
              |            +-------------+              |
              +----------->| Terminated  | <------------+
                           +-------------+

A publisher MUST send exactly one SUBSCRIBE_OK or REQUEST_ERROR in response to a SUBSCRIBE. A subscriber MUST send exactly one PUBLISH_OK (Section 10.5) or REQUEST_ERROR in response to a PUBLISH. The peer SHOULD close the session with a protocol error if it receives more than one.

All Established subscriptions have a Forward State which is either 0 or 1. The publisher does not send Objects if the Forward State is 0, and does send them if the Forward State is 1. The initiator of the subscription sets the initial Forward State in either PUBLISH or SUBSCRIBE. The subscriber can send PUBLISH_OK or REQUEST_UPDATE to update the Forward State. Control messages, such as PUBLISH_DONE (Section 10.11) are sent regardless of the forward state.

A publisher MUST save the Largest Location communicated in SUBSCRIBE_OK, PUBLISH or REQUEST_UPDATE_OK that changes the Forward State from 0 to 1. This value is called the Joining Location and can be used in a Joining FETCH (see Section 10.12.2) while the subscription is in the Established state.

Either endpoint can initiate a subscription to a track without exchanging any prior messages other than SETUP. Relays MUST NOT send any PUBLISH messages without knowing the client is interested in and authorized to receive the content. The communication of intent and authorization can be accomplished by the client sending SUBSCRIBE_NAMESPACE, or conveyed in other mechanisms out of band.

An endpoint MAY SUBSCRIBE to a Track it is publishing, though only Relays are required to handle such a SUBSCRIBE. Such self-subscriptions are identical to subscriptions initiated by other endpoints, and all published Objects will be forwarded back to the endpoint, subject to priority and congestion response rules.

An endpoint MAY have multiple concurrent subscriptions to the same Track, each identified by a unique Request ID. A publisher MAY assign the same or different Track Aliases to these subscriptions.

When an Object matches the filters of multiple subscriptions to the same Track, the publisher MUST send the Object once for each matching subscription, even when those subscriptions share the same Track Alias. Because subscriptions can share a Track Alias, the subscriber re-applies each subscription's filter to determine which subscription a received Object belongs to. Subscribers SHOULD avoid overlapping filters across subscriptions to the same Track, as they are responsible for deduplicating any resulting duplicate Objects.

A publisher SHOULD begin sending incomplete objects when available to avoid incurring additional latency.

Publishers MAY start sending Objects on PUBLISH-initiated subscriptions before receiving a PUBLISH_OK response to reduce latency. Doing so can consume unnecessary resources in cases where the Subscriber rejects the subscription with REQUEST_ERROR or sets Forward State=0 in PUBLISH_OK. It can also result in the Subscriber dropping Objects if its buffering limits are exceeded (see Section 11.3 and Section 11.4.2).

5.1.1. Subscription State Management

A subscriber keeps subscription state until it cancels the request (see Section 3.3.3), or after receipt of a PUBLISH_DONE or REQUEST_ERROR. Note that PUBLISH_DONE does not usually indicate that state can immediately be destroyed, see Section 10.11.

The Publisher can destroy subscription state as soon as it has received STOP_SENDING. It MUST reset any open streams associated with the SUBSCRIBE.

The Publisher can also immediately delete subscription state after sending PUBLISH_DONE, but MUST NOT send it until it has closed all related streams.

A REQUEST_ERROR indicates no objects will be delivered, and both endpoints can immediately destroy relevant state. Objects MUST NOT be sent for requests that end with an error.

5.1.2. Location Filters

Subscribers can specify a Location filter on a subscription indicating to the publisher which Objects to send. Subscriptions without a filter pass all Objects published or received via upstream subscriptions.

All Location filters have a Start Location and an optional End Group Delta. Only objects published or received via a subscription having Locations greater than or equal to Start Location and strictly less than or equal to the End Group (when present) pass the filter.

Some Location filters are defined to be relative to the Largest Object. The Largest Object is the Object with the largest Location (Section 1.4.2) in the Track from the perspective of the publisher processing the message. Largest Object updates when the first byte of an Object with a Location larger than the previous value is published or received through a subscription.

A Location Filter has the following structure:

Location Filter {
  Filter Type (vi64),
  [Start Location (Location),]
  [End Group Delta (vi64),]
}

Filter Type can have one of the following values:

Largest Object (0x2): The filter Start Location is {Largest Object.Group, Largest Object.Object + 1} and Largest Object is communicated in SUBSCRIBE_OK. If no content has been delivered yet, the filter Start Location is {0, 0}. There is no End Group - the subscription is open ended. Note that due to network reordering or prioritization, relays can receive Objects with Locations smaller than Largest Object after the SUBSCRIBE is processed, but these Objects do not pass the Largest Object filter.

Next Group Start (0x1): The filter Start Location is {Largest Object.Group + 1, 0} and Largest Object is communicated in SUBSCRIBE_OK. If no content has been delivered yet, the filter Start Location is {0, 0}. There is no End Group - the subscription is open ended. For scenarios where the subscriber intends to start from more than one group in the future, it can use an AbsoluteStart filter instead.

AbsoluteStart (0x3): The filter Start Location is specified explicitly. The specified Start Location MAY be less than the Largest Object observed at the publisher. There is no End Group - the subscription is open ended. An AbsoluteStart filter with Start = {0, 0} is equivalent to an unfiltered subscription.

AbsoluteRange (0x4): The filter Start Location and End Group are specified explicitly. The specified Start Location MAY be less than the Largest Object observed at the publisher. If the specified End Group Delta is zero, the remainder of that Group passes the filter. Otherwise, the last Group ID to be delivered will be the Group ID in Start Location plus the End Group Delta. If the resulting Group ID would be greater than 2^64 - 1, the endpoint MUST close the session with a PROTOCOL_VIOLATION.

An endpoint that receives a filter type other than the above MUST close the session with PROTOCOL_VIOLATION.

If the publisher cannot satisfy the requested Location Filter (see Section 10.2.9) or if the entire End Group has already been published it SHOULD send a REQUEST_ERROR with code INVALID_RANGE. A publisher MUST NOT send objects from outside the requested range.

5.1.3. Range Filters

Range Filters are parameters in SUBSCRIBE, FETCH, or SUBSCRIBE_TRACKS that tell a publisher to filter tracks (via TRACK PROPERTY FILTER) and objects according to subscriber-provided criteria. Range filters are specified as ranges of integer values in Track and Object Properties and other Object header fields (Subgroup ID, Object ID, and Publisher Priority). There are five Range Filter parameter types, 0x25-0x29, as shown below.

SUBGROUP_FILTER { Type=0x25, Length, [SetID], Range... }
OBJECTID_FILTER { Type=0x26, Length, [SetID], Range... }
PRIORITY_FILTER { Type=0x27, Length, [SetID], Range... }
OBJECT_PROPERTY_FILTER { Type=0x28, Length, [SetID], [Property Type], Range... }
TRACK_PROPERTY_FILTER  { Type=0x29, Length, [SetID], [Property Type], Range... }
Range { Start, [End] }

Each Range Filter is a sequence of Start/End (vi64) inclusive Range pairs prefixed with a Length (vi64) in bytes and a SetID (8 bits). The Track and Object Property Filters include an additional prefix for Property Type (vi64). The final End in a sequence of Ranges can be omitted to indicate no end.

Start is delta encoded from the prior Range's End or from 0 for the first Range, and End is delta encoded from the current Range's Start. Any delta encoding that results in a value that exceeds 2^64-1 MUST be rejected with REQUEST_ERROR with error code INVALID_FILTER. For example, to express ranges 3-5 and 10-15: the first Start is 3 (delta from 0), the first End is 2 (5 minus 3), the second Start is 5 (10 minus 5), and the second End is 5 (15 minus 10).

All filter parameters with the same SetID value are combined using logical "AND" operations, then all the resulting sets are combined using logical "OR" operations. The final result is SetID=0 OR SetID=1 OR ... SetID=255, where each SetID=i is the AND of filters with SetID=i.

The Track Property filter parameter MAY appear multiple times in a SUBSCRIBE_TRACKS message or REQUEST_UPDATE for it. All other filter parameters MAY appear multiple times in a FETCH, SUBSCRIBE, SUBSCRIBE_TRACKS, PUBLISH_OK, or REQUEST_UPDATE (on a subscription, from the subscriber only) message. If the same combination of Parameter Type, SetID, and Property Type (only in the Track and Object Property Filters) repeat in any message, an endpoint MUST reject this with REQUEST_ERROR with error code INVALID_FILTER.

In REQUEST_UPDATE, Length can be 0 to remove a filter parameter or non-zero to replace that entire filter parameter including all sets and Property Types. If a filter parameter is omitted from REQUEST_UPDATE, the value is unchanged. If omitted from other messages, the default is no filter.

Range Filters are only allowed if the setup option MAX_FILTER_RANGES is non-zero, which limits the total number of Ranges allowed in all Range Filter parameters for a given subscription or fetch. If this limit is exceeded, an endpoint MUST reject this with REQUEST_ERROR with error code INVALID_FILTER.

The Track Property Filter can be used in SUBSCRIBE_TRACKS to filter PUBLISH messages with required Track Property types and values. PUBLISH messages which pass the filter will be forwarded while those which do not pass it will not be forwarded nor will any Objects.

The Object Property Filter can be used to filter Objects with required Object Property types and values. It only filters Object Properties in the Object header, and does not evaluate Track Properties in PUBLISH messages.

5.1.4. Combining Filters

All filter types are combined using logical "AND" operations to further restrict which tracks and objects pass all filter criteria. This includes all Range Filters Section 5.1.3 and Location Filters Section 5.1.2, which can be evaluated in any order. The Forward parameter is also a type of filter. The publisher MUST forward only objects that pass all filters.

Pass = Forward AND Location Filters AND Range Filters

5.1.5. Joining an Ongoing Track

The MOQT Object model is designed with the concept that the beginning of a Group is a join point, so in order for a subscriber to join a Track, it needs to request an existing Group or wait for a future Group. Different applications will have different approaches for when to begin a new Group.

To join a Track at a past Group, the subscriber sends a SUBSCRIBE, PUBLISH_OK or REQUEST_UPDATE with Forward State 1 followed by a Joining FETCH (see Section 10.12.2) for the intended start Group, which can be relative. To join a Track at the next Group, the subscriber sends a SUBSCRIBE with Filter Type Next Group Start.

5.1.5.1. Dynamically Starting New Groups

While some publishers will deterministically create new Groups, other applications might want to only begin a new Group when needed. A subscriber joining a Track might detect that it is more efficient to request the Original Publisher create a new group than issue a Joining FETCH. Publishers indicate a Track supports dynamic group creation using the DYNAMIC_GROUPS parameter (Section 12.6).

One possible subscriber pattern is to SUBSCRIBE to a Track using Filter Type Largest Object and observe the Largest Location in the response. If the Object ID is below the application's threshold, the subscriber sends a FETCH for the beginning of the Group. If the Object ID is above the threshold and the Track supports dynamic groups, the subscriber sends a REQUEST_UPDATE message with the NEW_GROUP_REQUEST parameter equal to the Largest Location's Group, plus one (see Section 10.2.18).

Another possible subscriber pattern is to send a SUBSCRIBE with Filter Type Next Group Start and NEW_GROUP_REQUEST equal to 0. The value of DYNAMIC_GROUPS in SUBSCRIBE_OK will indicate if the publisher supports dynamic groups. A publisher that does will begin the next group as soon as practical.

This is one section of the MoQT specification, rendered per-section for quick reference and citation. The authoritative text is draft-ietf-moq-transport-19 at the IETF.