When objects are sent on streams, the stream begins with a Subgroup Header and is followed by one or more sets of serialized object fields. If a stream ends gracefully 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.
8.4.1. Subgroup Header
When a stream begins with SUBGROUP_HEADER, all Objects on the stream
belong to the track requested in the Subscribe message identified by Track Alias
and the subgroup indicated by 'Group ID' and Subgroup ID.
SUBGROUP_HEADER {
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.
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.
{
Object ID (i),
Extension Headers Length (i),
[Extension headers (...)],
Object Payload Length (i),
[Object Status (i)],
Object Payload (..),
}
A publisher MUST NOT send an Object on a stream if its Object ID is less than a previously sent Object ID within a given group in that stream.
8.4.2. 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 before the beginning of a subscription, 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 end of the subscription to before the current Group or the start after the current Group. 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 SUBSCRIBE_DONE (see Section 7.19). 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.
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 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.
8.4.3. 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 Subscribe ID.
FETCH_HEADER {
Subscribe 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 8.1.1) is set to the Object ID.