§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 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 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 9.13) are sent regardless of the forward state.

A publisher MUST save the Largest Location communicated in SUBSCRIBE_OK, PUBLISH or REQUEST_OK (in response to a REQUEST_UPDATE) 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 9.14.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.

For a given Track, an endpoint can have at most one subscription to a Track acting as the publisher and at most one acting as a subscriber. If an endpoint receives a message attempting to establish a second subscription to a Track with the same role, it MUST fail that request with a DUPLICATE_SUBSCRIPTION error.

If a publisher receives a SUBSCRIBE request for a Track with an existing subscription in Pending (publisher) state, it MUST fail that request with a DUPLICATE_SUBSCRIPTION error. If a subscriber receives a PUBLISH for a Track with a subscription in the Pending (Subscriber) state, it MUST ensure the subscription it initiated transitions to the Terminated state before sending PUBLISH_OK.

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 10.3 and Section 10.4.2).

5.1.1. Subscription State Management

A subscriber keeps subscription state until it cancels the request (see Section 3.3.1), 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 9.13.

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. Subscription Filters

Subscribers can specify a 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 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 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 Subscription Filter has the following structure:

Subscription 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.

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

5.1.3. 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 9.14.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.3.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 11.5).

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 9.3.11).

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-17 at the IETF.