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 using
UNSUBSCRIBE, the publisher terminates a subscription in the Pending
(Publisher) or Established states using PUBLISH_DONE.
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
| +-------------+ <-----+
| | | |
+---- UNSUBSCRIBE | | 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.
A publisher MUST save the Largest Location communicated in PUBLISH or SUBSCRIBE_OK when establishing a subscription. This value can be used in a Joining FETCH (see Section 9.16.2) at any time while the subscription is active.
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.15) are sent regardless of the forward 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 sends UNSUBSCRIBE, 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.15.
The Publisher can destroy subscription state as soon as it has received UNSUBSCRIBE. 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. 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.1) 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 (i),
[Start Location (Location),]
[End Group (i),]
}
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 is the same group
specified in Start Location, the remainder of that Group passes the
filter. End Group MUST specify the same or a larger Group than specified in
Start Location.
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 with Filter
Type Largest Object followed by a Joining FETCH (see Section 9.16.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.1.1.3).
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.2.2.9).
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.