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 terminated 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 stream MAY be canceled due to congestion or other reasons by either the publisher or subscriber. Early termination of a stream does not affect the MoQ application state, and therefore has no effect on outstanding subscriptions.
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 in 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, the publisher MUST allocate connection flow control to the control stream before allocating it 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..0x1D,
Track Alias (i),
Group ID (i),
[Subgroup ID (i),]
Publisher Priority (8),
}
All Objects received on a stream opened with SUBGROUP_HEADER have an
Object Forwarding Preference = Subgroup.
There are 12 defined Type values for SUBGROUP_HEADER:
| Type | Subgroup ID | Subgroup ID | Extensions | Contains End |
|---|---|---|---|---|
| Field Present | Value | Present | of Group | |
| 0x10 | No | 0 | No | No |
| 0x11 | No | 0 | Yes | No |
| 0x12 | No | First Object ID | No | No |
| 0x13 | No | First Object ID | Yes | No |
| 0x14 | Yes | N/A | No | No |
| 0x15 | Yes | N/A | Yes | No |
| 0x18 | No | 0 | No | Yes |
| 0x19 | No | 0 | Yes | Yes |
| 0x1A | No | First Object ID | No | Yes |
| 0x1B | No | First Object ID | Yes | Yes |
| 0x1C | Yes | N/A | No | Yes |
| 0x1D | Yes | N/A | Yes | Yes |
For Type values where Contains End of Group is Yes, the last Object in this Subgroup stream before a FIN is the last Object in the Group. If the Subgroup stream is terminated with a RESET_STREAM or RESET_STREAM_AT, the receiver cannot determine the End of Group Object ID.
For Type values where Subgroup ID Field Present is No, there is no explicit Subgroup ID field in the header and the Subgroup ID is either 0 (for Types 0x10-11 and 0x18-19) or the Object ID of the first object transmitted in this subgroup (for Types 0x12-13 and 0x1A-1B).
For Type values where Extensions Present is No, Extensions Headers Length is not present and all Objects have no extensions. When Extensions Present is Yes, Extension Headers Length is present in all Objects in this subgroup. Objects with no extensions set Extension Headers Length to 0.
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 extesnion header (see Section 11.3).
{
Object ID Delta (i),
[Extension Headers Length (i),
Extension headers (...)],
Object Payload Length (i),
[Object Status (i)],
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 an open Subgroup exceeding its Delivery Timeout, early termination of subscription due to an UNSUBSCRIBE message, a publisher's decision to end the subscription early, or a SUBSCRIBE_UPDATE moving the subscription's End Group to a smaller Group or the Start Location to a larger Location. 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.12). 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 knows 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 knows 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, GroupDoesNotExist, 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.
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 requested cancellation via UNSUBSCRIBE, FETCH_CANCEL or STOP_SENDING, or the publisher ended the subscription, in which case PUBLISH_DONE (Section 9.12) will have a more detailed status code.
- DELIVERY_TIMEOUT (0x2):
-
The DELIVERY TIMEOUT Section 9.2.1.2 was exceeded for this stream.
- SESSION_CLOSED (0x3):
-
The publisher session is being closed.
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 (i) = 0x5,
Request ID (i),
}
Each object sent on a fetch stream after the FETCH_HEADER has the following format:
{
Group ID (i),
Subgroup ID (i),
Object ID (i),
Publisher Priority (8),
Extension Headers Length (i),
[Extension headers (...)],
Object Payload Length (i),
[Object Status (i)],
Object Payload (..),
}
The Object Status field is only sent if the Object Payload Length is zero.
The Subgroup ID field of an object with a Forwarding Preference of "Datagram" (see Section 10.2.1) is set to the Object ID.