When Objects are sent on streams, the stream begins with a Subgroup or Fetch
Header and is followed by one or more sets of serialized Object fields.
If a stream ends gracefully (i.e., the stream terminates with a FIN) in the
middle of a serialized Object, the session SHOULD be closed with a
PROTOCOL_VIOLATION.
A publisher SHOULD NOT open more than one stream at a time with the same Subgroup Header field values.
10.4.1. Stream Cancellation
Streams aside from the control streams MAY be canceled due to congestion or other reasons by either the publisher or subscriber. Early termination of a unidirectional stream does not affect the MoQ application state, and therefore has no effect on outstanding subscriptions. Termination of a bidi request stream terminates the Subscription, Fetch, Track Status, Publish Namespace, or Subscribe Namespace request. When possible, Publishers SHOULD send a PUBLISH_DONE when terminating a subscription instead of abruptly terminating the associated control stream.
10.4.2. Subgroup Header
All Objects on a Subgroup stream belong to the track identified by Track Alias
(see Section 10.1) and the Subgroup indicated by 'Group ID' and Subgroup
ID indicated by the SUBGROUP_HEADER.
If an endpoint receives a subgroup with an unknown Track Alias, it MAY abandon the stream, or choose to buffer it for a brief period to handle reordering with the control message that establishes the Track Alias. The endpoint MAY withhold stream flow control beyond the SUBGROUP_HEADER until the Track Alias has been established. To prevent deadlocks, endpoints MUST allocate connection flow control to the control streams before allocating it to any data streams. Otherwise, a receiver might wait for a control message containing a Track Alias to release flow control, while the sender waits for flow control to send the message.
SUBGROUP_HEADER {
Type (i) = 0x10..0x15 / 0x18..0x1D / 0x30..0x35 / 0x38..0x3D,
Track Alias (vi64),
Group ID (vi64),
[Subgroup ID (vi64),]
[Publisher Priority (8),]
}
All Objects received on a stream opened with SUBGROUP_HEADER have an
Object Forwarding Preference = Subgroup.
The Type field in the SUBGROUP_HEADER takes the form 0b00X1XXXX (or the set of values from 0x10 to 0x1F, 0x30 to 0x3F), where bit 4 is always set to 1. However, not all Type values in this range are valid. The four low-order bits and bit 5 determine which fields are present in the header:
-
The PROPERTIES bit (0x01) indicates when the Properties field is present in all Objects in this Subgroup. When set to 1, the Object Properties structure defined in Section 10.2.1.2 is present in all Objects. When set to 0, the field is never present. Objects with no properties set Properties Length to 0.
-
The SUBGROUP_ID_MODE field (bits 1-2, mask 0x06) is a two-bit field that determines the encoding of the Subgroup ID. To extract this value, perform a bitwise AND with mask 0x06 and right-shift by 1 bit:
-
0b00: The Subgroup ID field is absent and the Subgroup ID is 0.
-
0b01: The Subgroup ID field is absent and the Subgroup ID is the Object ID of the first Object transmitted in this Subgroup.
-
0b10: The Subgroup ID field is present in the header.
-
0b11: Reserved for future use.
-
-
The END_OF_GROUP bit (0x08) indicates that this subgroup contains the largest Object in the Group. When set to 1, the subscriber can infer the final Object in the Group when the data stream is terminated by a FIN. In this case, Objects that have the same Group ID and an Object ID larger than the last Object received on the stream do not exist. This does not apply when the data stream is terminated with a RESET_STREAM or RESET_STREAM_AT.
-
The DEFAULT_PRIORITY bit (0x20) indicates when the Priority field is present. When set to 1, the Priority field is omitted and this Subgroup inherits the Publisher Priority specified in the control message that established the subscription. When set to 0, the Priority field is present in the Subgroup header.
The following Type values are invalid. If an endpoint receives a stream header
with any of these Type values, it MUST close the session with a
PROTOCOL_VIOLATION:
-
Type values with SUBGROUP_ID_MODE set to 0b11: 0x16, 0x17, 0x1E, 0x1F, 0x36, 0x37, 0x3E, 0x3F. This mode is reserved for future use.
-
Type values that do not match the form 0b00X1XXXX (i.e., Type values outside the ranges 0x10..0x1F and 0x30..0x3F, or values where bit 4 is not set).
To send an Object with Object Forwarding Preference = Subgroup, find the open
stream that is associated with the subscription, Group ID and Subgroup ID,
or open a new one and send the SUBGROUP_HEADER. Then serialize the
following fields.
The Object Status field is only sent if the Object Payload Length is zero.
The Object ID Delta + 1 is added to the previous Object ID in the Subgroup stream if there was one. The Object ID is the Object ID Delta if it's the first Object in the Subgroup stream. For example, a Subgroup of sequential Object IDs starting at 0 will have 0 for all Object ID Delta values. A consumer cannot infer information about the existence of Objects between the current and previous Object ID in the Subgroup (e.g. when Object ID Delta is non-zero) unless there is an Prior Object ID Gap extension header (see Section 11.8).
{
Object ID Delta (vi64),
[Properties (..),]
Object Payload Length (vi64),
[Object Status (vi64),]
[Object Payload (..),]
}
10.4.3. Closing Subgroup Streams
Subscribers will often need to know if they have received all objects in a Subgroup, particularly if they serve as a relay or cache. QUIC and Webtransport streams provide signals that can be used for this purpose. Closing Subgroups promptly frees system resources and often unlocks flow control credit to open more streams.
If a sender has delivered all objects in a Subgroup to the QUIC stream, except any Objects with Locations smaller than the subscription's Start Location, it MUST close the stream with a FIN.
If a sender closes the stream before delivering all such objects to the QUIC stream, it MUST use a RESET_STREAM or RESET_STREAM_AT [I-D.draft-ietf-quic-reliable-stream-reset] frame. This includes, but is not limited to:
-
An Object in an open Subgroup exceeding its Delivery Timeout
-
Early termination of subscription due to request cancellation
-
A publisher's decision to end the subscription early
-
A REQUEST_UPDATE moving the subscription's End Group to a smaller Group or the Start Location to a larger Location
-
Omitting a Subgroup Object due to the subscriber's Forward State
When RESET_STREAM_AT is used, the reliable_size SHOULD include the stream header so the receiver can identify the corresponding subscription and accurately account for reset data streams when handling PUBLISH_DONE (see Section 9.13). Publishers that reset data streams without using RESET_STREAM_AT with an appropriate reliable_size can cause subscribers to hold on to subscription state until a timeout expires.
A sender might send all objects in a Subgroup and the FIN on a QUIC stream, and then reset the stream. In this case, the receiving application would receive the FIN if and only if all objects were received. If the application receives all data on the stream and the FIN, it can ignore any RESET_STREAM it receives.
If a sender will not deliver any objects from a Subgroup, it MAY send a SUBGROUP_HEADER on a new stream, with no objects, and then send RESET_STREAM_AT with a reliable_size equal to the length of the stream header. This explicitly tells the receiver there is an unsent Subgroup.
A relay MUST NOT forward an Object on an existing Subgroup stream unless it is the next Object in that Subgroup. A relay determines that an Object is the next Object in the Subgroup if at least one of the following is true:
-
The Object ID is one greater than the previous Object sent on this Subgroup stream.
-
The Object was received on the same upstream Subgroup stream as the previously sent Object on the downstream Subgroup stream, with no other Objects in between.
-
It determined all Object IDs between the current and previous Object IDs on the Subgroup stream belong to different Subgroups or do not exist.
If the relay does not know if an Object is the next Object, it MUST reset the Subgroup stream and open a new one to forward it.
Since SUBSCRIBEs always end on a group boundary, an ending subscription can always cleanly close all its subgroups. A sender that terminates a stream early for any other reason (e.g., to handoff to a different sender) MUST use RESET_STREAM or RESET_STREAM_AT. Senders SHOULD terminate a stream on Group boundaries to avoid doing so.
An MOQT implementation that processes a stream FIN is assured it has received all objects in a subgroup from the start of the subscription. If a relay, it can forward stream FINs to its own subscribers once those objects have been sent. A relay MAY treat receipt of EndOfGroup or EndOfTrack objects as a signal to close corresponding streams even if the FIN has not arrived, as further objects on the stream would be a protocol violation.
Similarly, an EndOfGroup message indicates the maximum Object ID in the Group, so if all Objects in the Group have been received, a FIN can be sent on any stream where the entire subgroup has been sent. This might be complex to implement.
Processing a RESET_STREAM or RESET_STREAM_AT means that there might be other objects in the Subgroup beyond the last one received. A relay might immediately reset the corresponding downstream stream, or it might attempt to recover the missing Objects in an effort to send all the Objects in the subgroups and the FIN. It also might send RESET_STREAM_AT with reliable_size set to the last Object it has, so as to reliably deliver the Objects it has while signaling that other Objects might exist.
A subscriber MAY send a QUIC STOP_SENDING frame for a subgroup stream if the Group or Subgroup is no longer of interest to it. The publisher SHOULD respond with RESET_STREAM or RESET_STREAM_AT. If RESET_STREAM_AT is sent, note that the receiver has indicated no interest in the objects, so setting a reliable_size beyond the stream header is of questionable utility.
RESET_STREAM and STOP_SENDING on SUBSCRIBE data streams have no impact on other Subgroups in the Group or the subscription, although applications might cancel all Subgroups in a Group at once.
A publisher that receives a STOP_SENDING on a Subgroup stream SHOULD NOT attempt to open a new stream to deliver additional Objects in that Subgroup.
The application SHOULD use a relevant error code in RESET_STREAM or RESET_STREAM_AT, as defined below:
- INTERNAL_ERROR (0x0):
-
An implementation specific error.
- CANCELLED (0x1):
-
The subscriber or publisher cancelled the Request. For Subscriptions, PUBLISH_DONE (Section 9.13) will have a more detailed status code.
- DELIVERY_TIMEOUT (0x2):
-
The DELIVERY TIMEOUT Section 9.3.3 was exceeded for this stream.
- SESSION_CLOSED (0x3):
-
The publisher session is being closed.
- UNKNOWN_OBJECT_STATUS (0x4):
-
In response to a FETCH, the publisher is unable to determine the Status of the next Object in the requested range.
- TOO_FAR_BEHIND (0x5):
-
The corresponding subscription has exceeded the publisher's resource limits and is being terminated (see Section 9.3.3).
- EXCESSIVE_LOAD (0x9):
-
The publisher is overloaded and is resetting this stream.
- MALFORMED_TRACK (0x12):
-
A relay publisher detected that the track was malformed (see Section 2.4.2).
10.4.4. Fetch Header
When a stream begins with FETCH_HEADER, all objects on the stream belong to the
track requested in the Fetch message identified by Request ID.
FETCH_HEADER {
Type (vi64) = 0x5,
Request ID (vi64),
}
Each Object sent on a FETCH stream after the FETCH_HEADER has the following format:
{
Serialization Flags (vi64),
[Group ID (vi64),]
[Subgroup ID (vi64),]
[Object ID (vi64),]
[Publisher Priority (8),]
[Properties (..),]
Object Payload Length (vi64),
[Object Payload (..),]
}
The Serialization Flags field defines the serialization of the Object. It is a variable-length integer. When less than 128, the bits represent flags described below. The following additional values are defined:
| Value | Meaning |
|---|---|
| 0x8C | End of Non-Existent Range |
| 0x10C | End of Unknown Range |
Any other value is a PROTOCOL_VIOLATION.
10.4.4.1. Flags
The two least significant bits (LSBs) of the Serialization Flags form a two-bit field that defines the encoding of the Subgroup. To extract this value, the Subscriber performs a bitwise AND operation with the mask 0x03.
| Bitmask Result (Serialization Flags & 0x03) | Meaning |
|---|---|
| 0x00 | Subgroup ID is zero |
| 0x01 | Subgroup ID is the prior Object's Subgroup ID |
| 0x02 | Subgroup ID is the prior Object's Subgroup ID plus one |
| 0x03 | The Subgroup ID field is present |
The following table defines additional flags within the Serialization Flags field. Each flag is an independent boolean value, where a set bit (1) indicates the corresponding condition is true.
| Bitmask | Condition if set | Condition if not set (0) |
|---|---|---|
| 0x04 | Object ID field is present | Object ID is the prior Object's ID plus one |
| 0x08 | Group ID field is present | Group ID is the prior Object's Group ID |
| 0x10 | Priority field is present | Priority is the prior Object's Priority |
| 0x20 | Properties field is present | Properties field is not present |
| 0x40 | Datagram: ignore the two least significant bits | Decode the Subgroup ID as indicated by the two least significant bits |
If the first Object in the FETCH response uses a flag that references fields in
the prior Object, the Subscriber MUST close the session with a
PROTOCOL_VIOLATION.
The Object Properties structure is defined in Section 10.2.1.2.
When encoding an Object with a Forwarding Preference of "Datagram" (see Section 10.2.1.2), the object has no Subgroup ID. The publisher MUST SET bit 0x40 to '1'. When 0x40 is set, it SHOULD set the two least significant bits to zero and the subscriber MUST ignore the bits.
10.4.4.2. End of Range
When Serialization Flags indicates an End of Range (e.g. values 0x8C or 0x10C),
the Group ID and Object ID fields are present. Subgroup ID, Priority and
Properties are not present. All Objects with Locations between the last
serialized Object, if any, and this Location, inclusive, either do not exist
(when Serialization Flags is 0x8C) or are unknown (0x10C). A publisher SHOULD NOT use End of Non-Existent Range in a FETCH response except to split a range
of Objects that will not be serialized into those that are known not to exist
and those with unknown status.
When an Object follows an End of Range indicator and uses flags that reference the "prior Object", the prior Object fields are determined as follows:
-
Prior Group ID and prior Object ID: The values from the End of Range indicator.
-
Prior Subgroup ID: The Subgroup ID from the last actual Object before the End of Range indicator. If there was no prior Object, using a flag that references the prior Subgroup ID is a
PROTOCOL_VIOLATION. -
Prior Priority: The Priority from the last actual Object before the End of Range indicator. If there was no prior Object, using a flag that references the prior Priority is a
PROTOCOL_VIOLATION.