§9.4.

Streams

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.

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

9.4.2. Subgroup Header

All Objects on a Subgroup stream belong to the track identified by Track Alias (see Section 9.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) = 0x8..0xD,
  Track Alias (i),
  Group ID (i),
  [Subgroup ID (i),]
  Publisher Priority (8),
}
Figure 35: MOQT SUBGROUP_HEADER

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:

Table 13
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.

{
  Object ID (i),
  [Extension Headers Length (i),
  Extension headers (...)],
  Object Payload Length (i),
  [Object Status (i)],
  Object Payload (..),
}
Figure 36: MOQT Subgroup Object Fields

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.

9.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 SUBSCRIBE_DONE (see Section 8.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 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:

Table 14
Code Reason
0x0 Internal Error
0x1 Cancelled
0x2 Delivery Timeout
0x3 Session Closed
Internal Error:

An implementation specific error

Cancelled:

The subscriber requested cancellation via UNSUBSCRIBE, FETCH_CANCEL or STOP_SENDING, or the publisher ended the subscription, in which case SUBSCRIBE_DONE (Section 8.12) will have a more detailed status code.

Delivery Timeout:

The DELIVERY TIMEOUT Section 8.2.1.2 was exceeded for this stream

Session Closed:

The publisher session is being closed

9.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),
}
Figure 37: MOQT FETCH_HEADER

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 (..),
}
Figure 38: MOQT Fetch Object Fields

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 9.2.1) is set to the Object ID.

This is one section of the MoQT specification, rendered per-section for quick reference and citation. The authoritative text is draft-ietf-moq-transport-12 at the IETF.