diff --git a/draft-ietf-moq-transport.md b/draft-ietf-moq-transport.md index 9d0a16296..7a38ff18c 100644 --- a/draft-ietf-moq-transport.md +++ b/draft-ietf-moq-transport.md @@ -97,7 +97,9 @@ discovery and subscription. * {{relays-moq}} covers behavior at the relay entities. -* {{message}} covers how messages are encoded on the wire. +* {{message}} covers how control messages are encoded on the wire. + +* {{data-streams}} covers how data messages are encoded on the wire. ## Motivation @@ -168,11 +170,11 @@ remains opaque and private. Client: -: The party initiating a MoQ transport session. +: The party initiating a Transport Session. Server: -: The party accepting an incoming transport session. +: The party accepting an incoming Transport Session. Endpoint: @@ -207,7 +209,7 @@ Downstream: : In the direction of the End Subscriber(s) -Transport session: +Transport Session: : A raw QUIC connection or a WebTransport session. @@ -223,7 +225,7 @@ Group: Object: : An object is an addressable unit whose payload is a sequence of - bytes. Objects form the base element in the MOQT model. See + bytes. Objects form the base element in the MOQT data model. See ({{model-object}}). Track: @@ -272,14 +274,6 @@ x (b): described in ({{?RFC9000, Section 16}}), followed by that many bytes of binary data -x (f): - -: Indicates that x is a flag and is encoded as a single byte with the - value 0 or 1. A value of 0 indicates the flag is false or off, while a - value of 1 indicates the flag is true or on. Any other value is a - protocol error and SHOULD terminate the session with a Protocol - Violation ({{session-termination}}). - x (tuple): : Indicates that x is a tuple, consisting of a variable length integer encoded @@ -292,10 +286,24 @@ be encoded using the least number of bytes possible to represent the required value. -# Object Model {#model} +# Object Data Model {#model} -MOQT has a hierarchical object model for data, comprised of objects, -groups and tracks. +MOQT has a hierarchical data model, comprised of tracks which contain +groups, and groups that contain objects. Inside of a group, the objects +can be organized into subgroups. + +To give an example of how an application might use this data model, +consider an application sending high and low resolution video using a +codec with temporal scalability. Each resolution is sent as a separate +track to allow the subscriber to pick the appropriate resolution given +the display environment and available bandwidth. Each "group of pictures" +in a video is sent as a group because the first frame is needed to +decode later frames. This allows the client to join at the logical points +where they can get the information to start decoding the stream. +The temporal layers are sent as separate sub groups to allow the +priority mechanism to favour the base layer when there is not enough +bandwidth to send both the base and enhancement layers. Each frame of +video on a given layer is sent as a single object. ## Objects {#model-object} @@ -309,23 +317,23 @@ An Object can become unavailable, but its contents MUST NOT change over time. Objects are comprised of two parts: metadata and a payload. The metadata is -never encrypted and is always visible to relays. The payload portion may be -encrypted, in which case it is only visible to the Original Publisher and End -Subscribers. The application is solely responsible for the content of the object -payload. This includes the underlying encoding, compression, any end-to-end -encryption, or authentication. A relay MUST NOT combine, split, or otherwise -modify object payloads. +never encrypted and is always visible to relays (see {{relays-moq}}). The +payload portion may be encrypted, in which case it is only visible to the +Original Publisher and End Subscribers. The application is solely responsible +for the content of the object payload. This includes the underlying encoding, +compression, any end-to-end encryption, or authentication. A relay MUST NOT +combine, split, or otherwise modify object payloads. -## Peeps {#model-peep} +## Subgroups {#model-subgroup} -A peep is a sequence of one or more objects from the same group -({{model-group}}) in ascending order by Object ID. Objects in a peep +A subgroup is a sequence of one or more objects from the same group +({{model-group}}) in ascending order by Object ID. Objects in a subgroup have a dependency and priority relationship consistent with sharing a QUIC stream. In some cases, a Group will be most effectively delivered using more than one QUIC stream. When a Track's forwarding preference (see {{object-fields}}) is "Track" or -"Datagram", Objects are not sent in Peeps, no Peep IDs are assigned, and the +"Datagram", Objects are not sent in Subgroups, no Subgroup IDs are assigned, and the description in the remainder of this section does not apply. QUIC streams offer in-order reliable delivery and the ability to cancel sending @@ -333,42 +341,42 @@ and retransmission of data. Furthermore, many implementations offer the ability to control the relative priority of streams, which allows control over the scheduling of sending data on active streams. -Every object within a Group belongs to exactly one Peep. +Every object within a Group belongs to exactly one Subgroup. -Objects from two peeps cannot be sent on the same QUIC stream. Objects from the -same Peep MUST NOT be sent on different QUIC streams, unless one of the streams -was reset prematurely, or upstream conditions have forced objects from a Peep +Objects from two subgroups cannot be sent on the same QUIC stream. Objects from the +same Subgroup MUST NOT be sent on different QUIC streams, unless one of the streams +was reset prematurely, or upstream conditions have forced objects from a Subgroup to be sent out of Object ID order. -Original publishers assign each Peep a Peep ID, and do so as they see fit. The -scope of a Peep ID is a Group, so Peeps from different Groups MAY share a Peep +Original publishers assign each Subgroup a Subgroup ID, and do so as they see fit. The +scope of a Subgroup ID is a Group, so Subgroups from different Groups MAY share a Subgroup ID without implying any relationship between them. In general, publishers assign -objects to peeps in order to leverage the features of QUIC streams as described +objects to subgroups in order to leverage the features of QUIC streams as described above. An example strategy for using QUIC stream properties follows. If object B is dependent on object A, then delivery of B can follow A, i.e. A and B can be usefully delivered over a single QUIC stream. Furthermore, in this example: -- If an object is dependent on all previous objects in a Peep, it is added to -that Peep. +- If an object is dependent on all previous objects in a Subgroup, it is added to +that Subgroup. -- If an object is not dependent on all of the objects in a Peep, it goes in -a different Peep. +- If an object is not dependent on all of the objects in a Subgroup, it goes in +a different Subgroup. -- There are often many ways to compose Peeps that meet these criteria. Where -possible, choose the composition that results in the fewest Peeps in a group +- There are often many ways to compose Subgroups that meet these criteria. Where +possible, choose the composition that results in the fewest Subgroups in a group to minimize the number of QUIC streams used. ## Groups {#model-group} -A group is a collection of objects and is a sub-unit of a track -({{model-track}}). Objects within a group SHOULD NOT depend on objects -in other groups. A group behaves as a join point for subscriptions. -A new subscriber might not want to receive the entire track, and may -instead opt to receive only the latest group(s). The publisher then -selectively transmits objects based on their group membership. +A group is a collection of objects and is a sub-unit of a track ({{model-track}}). +Groups SHOULD be indendepently useful, so objects within a group SHOULD NOT depend +on objects in other groups. A group provides a join point for subscriptions, so a +subscriber that does not want to receive the entire track can opt to receive only +the latest group(s). The publisher then selectively transmits objects based on +their group membership. ## Track {#model-track} @@ -449,15 +457,15 @@ path indicated by the URI, as described in {{WebTransport, Section 3}}. ### QUIC A MOQT server that is accessible via native QUIC can be identified by a -URI with a "moq" scheme. The "moq" URI scheme is defined as follows, +URI with a "moqt" scheme. The "moqt" URI scheme is defined as follows, using definitions from {{!RFC3986}}: ~~~~~~~~~~~~~~~ -moq-URI = "moqt" "://" authority path-abempty [ "?" query ] +moqt-URI = "moqt" "://" authority path-abempty [ "?" query ] ~~~~~~~~~~~~~~~ The `authority` portion MUST NOT contain a non-empty `host` portion. -The `moq` URI scheme supports the `/.well-known/` path prefix defined in +The `moqt` URI scheme supports the `/.well-known/` path prefix defined in {{!RFC8615}}. This protocol does not specify any semantics on the `path-abempty` and @@ -513,8 +521,9 @@ sent on unidirectional streams. Because there are no other uses of bidirectional streams, a peer MAY currently close the session as a 'Protocol Violation' if it receives a second bidirectional stream. -The control stream MUST NOT be abruptly closed at the underlying transport -layer. Doing so results in the session being closed as a 'Protocol Violation'. +The control stream MUST NOT be closed at the underlying transport layer while the +session is active. Doing so results in the session being closed as a +'Protocol Violation'. ## Stream Cancellation @@ -525,7 +534,7 @@ effect on outstanding subscriptions. ## Termination {#session-termination} -The transport session can be terminated at any point. When native QUIC +The Transport Session can be terminated at any point. When native QUIC is used, the session is closed using the CONNECTION\_CLOSE frame ({{QUIC, Section 19.19}}). When WebTransport is used, the session is closed using the CLOSE\_WEBTRANSPORT\_SESSION capsule ({{WebTransport, @@ -590,8 +599,8 @@ there are still open subscriptions on a connection. The GOAWAY message does not immediately impact subscription state. A subscriber SHOULD individually UNSUBSCRIBE for each existing subscription, while a publisher MAY reject new SUBSCRIBEs while in the draining state. When the server -is a subscriber, it SHOULD send a GOAWAY message prior to any UNSUBSCRIBE -messages. +is a subscriber, it SHOULD send a GOAWAY message to downstream subscribers +prior to any UNSUBSCRIBE messages to upstream publishers. After the client receives a GOAWAY, it's RECOMMENDED that the client waits until there are no more active subscriptions before closing the session with NO_ERROR. @@ -608,52 +617,70 @@ MoQ priorities allow a subscriber and original publisher to influence the transmission order of Objects within a session in the presence of congestion. +## Definitions + +MoQT maintains priorities between different _schedulable objects_. +A schedulable object in MoQT is either: + +1. An object that belongs to a subgroup where that object would be the next + object to be sent in that subgroup. +1. An object that belongs to a track with delivery preference Datagram. + +Since a single subgroup or datagram has a single publisher priority, it can be +useful to conceptualize this process as scheduling subgroups or datagrams +instead of individual objects on them. + +A _priority number_ is an unsigned integer with a value between 0 and 255. +A lower priority number indicates higher priority; the highest priority is 0. + +_Subscriber Priority_ is a priority number associated with an individual +subscription. It is specified in the SUBSCRIBE message, and can be updated via +SUBSCRIBE_UPDATE message. The subscriber priority of an individual schedulable +object is the subscriber priority of the subscription that caused that object +to be sent. When subscriber priority is changed, a best effort SHOULD be made +to change the apply that to all objects that have not been sent, but it is +implementation dependent what happens to objects that have already been +received and possibly scheduled. + +_Publisher Priority_ is a priority number associated with an individual +schedulable object. It is specified in the header of the respective subgroup or +datagram, and is the same for all objects in a single subgroup. + +_Group Order_ is a property of an invidual subscription. It can be either +'Ascending' (groups with lower group ID are sent first), or 'Descending' +(groups with higher group ID are sent first). The publisher communicates its +group order in the SUBSCRIBE_OK message; the subscriber can override it in its +SUBSCRIBE message. The group order of an existing subscription cannot be +changed. + +## Scheduling Algorithm + +When an MoQT publisher has multiple schedulable objects it can choose between, +the objects SHOULD be selected as follows: + +1. If two objects have a different subscriber priority associated with them, + the one with **the highest subscriber priority** is sent first. +1. If two objects have the same subscriber priority, but a different publisher + priority, the one with **the highest publisher priority** is sent first. +1. If two objects have the same subscriber and publisher priority, but belong + to two different groups of the same track received through the same + subscription, **the group order** of the associated subscription is used to + decide the one that is sent first. +1. If two objects belong to the same group of the same track received through + the same subscription, the one with **the lowest Subgroup ID** (for tracks + with delivery preference Subgroup), or **the lowest Object ID** (for tracks + with delivery preference Datagram) is sent first. + +This algorithm does not provide a well-defined ordering for objects that belong +to different subscriptions, but have the same subscriber and publisher +priority. The ordering in those cases is implementation-defined, though the +expectation is that all subscriptions will be able to send some data. + Given the critical nature of control messages and their relatively small size, the control stream SHOULD be prioritized higher than all subscribed Objects. -The subscriber indicates the priority of a subscription via the -Subscriber Priority field and the original publisher indicates priority -in every stream or datagram header. As such, the subscriber's priority is a -property of the subscription and the original publisher's priority is a -property of the Track and the Objects it contains. In both cases, a lower -value indicates a higher priority, with 0 being the highest priority. - -When Objects are contained in Peeps, all Objects in the Peep have the same -priority. - -The Subscriber Priority is considered first when selecting a subscription -to send data on within a given session. When two or more subscriptions -have equal subscriber priority, the original publisher priority is considered -next and can change within the track, so subscriptions are prioritized based -on the highest priority data available to send. For example, if the subscription -had data at priority 6 and priority 10 to send, the subscription priority would -be 6. When both the subscriber and original publisher priorities for a -subscription are equal, how much data to send from each subscription is -implementation-dependent, but the expectation is that all subscriptions will -be able to send some data. - -The subscriber's priority can be changed via a SUBSCRIBE_UPDATE message. -This updates the priority of all unsent data within the subscription, -though the details of the reprioitization are implementation-specific. - -Subscriptions have a Group Order of either 'Ascending' or 'Descending', -which indicates whether the lowest or highest Group Id SHOULD be sent first -when multiple Groups are available to send. A subscriber can specify either -'Ascending' or 'Descending' in the SUBSCRIBE message or they can specify they -want to use the Original Publisher's Group Order, which is indicated in -the corresponding SUBSCRIBE_OK. - -Within the same Group, and the same priority level, -Objects with a lower Object Id are always sent before objects with a -higher Object Id, regardless of the specified Group Order. If the group -contains more than one Peep and the priority varies between these Peeps, -higher priority Peeps are sent before lower priority Peeps. If the specified -priority of two Peeps in a Group are equal, the lower Peep ID has priority. -Within a Peep, Objects MUST be sent in increasing Object ID order. - -The Group Order cannot be changed via a SUBSCRIBE_UPDATE message, and -instead an UNSUBSCRIBE and SUBSCRIBE can be used. +## Considerations for Setting Priorities Relays SHOULD respect the subscriber and original publisher's priorities. Relays SHOULD NOT directly use Subscriber Priority or Group Order @@ -682,7 +709,10 @@ similar in functionality to Content Delivery Networks (CDNs). Additionally, relays serve as policy enforcement points by validating subscribe and publish requests at the edge of a network. -Relays can cache Objects, but are not required to. +Relays are endpoints, which means they terminate Transport Sessions in order to +have visibility of MoQ Object metadata. + +Relays MAY cache Objects, but are not required to. ## Subscriber Interactions @@ -692,13 +722,13 @@ interest. Relays MUST ensure subscribers are authorized to access the content associated with the track. The authorization information can be part of subscription request itself or part of the encompassing session. The specifics of how a relay authorizes a user are -outside the scope of this specification. +outside the scope of this specification. The subscriber is notified +of the result of the subscription via a +SUBSCRIBE_OK ({{message-subscribe-ok}}) or SUBSCRIBE_ERROR +{{message-subscribe-error}} control message. The entity receiving the +SUBSCRIBE MUST send only a single response to a given SUBSCRIBE of +either SUBSCRIBE_OK or SUBSCRIBE_ERROR. -The subscriber making the subscribe request is notified of the result of -the subscription, via SUBSCRIBE_OK ({{message-subscribe-ok}}) or the -SUBSCRIBE_ERROR {{message-subscribe-error}} control message. -The entity receiving the SUBSCRIBE MUST send only a single response to -a given SUBSCRIBE of either SUBSCRIBE_OK or SUBSCRIBE_ERROR. If a relay does not already have a subscription for the track, or if the subscription does not cover all the requested Objects, it will need to make an upstream subscription. The relay SHOULD NOT @@ -710,13 +740,22 @@ subscribers for each track. Each new OBJECT belonging to the track within the subscription range is forwarded to each active subscriber, dependent on the congestion response. A subscription remains active until the publisher of the track terminates the -track with a SUBSCRIBE_DONE (see {{message-subscribe-done}}). +subscription with a SUBSCRIBE_DONE (see {{message-subscribe-done}}). + +A caching relay saves Objects to its cache identified by the Object's +Full Track Name, Group ID and Object ID. Relays MUST be able to +process objects for the same Full Track Name from multiple +publishers and forward objects to active matching subscriptions. +If multiple objects are received with the same Full Track Name, +Group ID and Object ID, Relays MAY ignore subsequently received Objects +or MAY use them to update the cache. Implementations that update the +cache need to be protect against cache poisoning. Objects MUST NOT be sent for unsuccessful subscriptions, and if a subscriber receives a SUBSCRIBE_ERROR after receiving objects, it MUST close the session with a 'Protocol Violation'. -A relay MUST not reorder or drop objects received on a multi-object stream when +A relay MUST NOT reorder or drop objects received on a multi-object stream when forwarding to subscribers, unless it has application specific information. Relays MAY aggregate authorized subscriptions for a given track when @@ -737,6 +776,12 @@ as defined below: |------|---------------------------| | 0x2 | Retry Track Alias | |------|---------------------------| +| 0x3 | Track Does Not Exist | +|------|---------------------------| +| 0x4 | Unauthorized | +|------|---------------------------| +| 0x5 | Timeout | +|------|---------------------------| The application SHOULD use a relevant status code in SUBSCRIBE_DONE, as defined below: @@ -759,24 +804,44 @@ SUBSCRIBE_DONE, as defined below: | 0x6 | Expired | |------|---------------------------| +### Graceful Publisher Relay Switchover + +This section describes behavior a subscriber MAY implement +to allow for a better user experience when a relay sends a GOAWAY. + +When a subscriber receives the GOAWAY message, it starts the process +of connecting to a new relay and sending the SUBSCRIBE requests for +all active subscriptions to the new relay. The new relay will send a +response to the subscribes and if they are successful, the subscriptions +to the old relay can be stopped with an UNSUBSCRIBE. + + ## Publisher Interactions Publishing through the relay starts with publisher sending ANNOUNCE control message with a `Track Namespace` ({{model-track}}). +The announce enables the relay to know which publisher to forward a +SUBSCRIBE to. Relays MUST ensure that publishers are authorized by: - Verifying that the publisher is authorized to publish the content associated with the set of tracks whose Track Namespace matches the - announced namespace. Specifics of where the authorization happens, - either at the relays or forwarded for further processing, depends on - the way the relay is managed and is application specific (typically - based on prior business agreement). + announced namespace. Where the authorization and identification of + the publisher occurs depends on the way the relay is managed and + is application specific. Relays respond with an ANNOUNCE_OK or ANNOUNCE_ERROR control message providing the result of announcement. The entity receiving the ANNOUNCE MUST send only a single response to a given ANNOUNCE of -either ANNOUNCE_OK or ANNOUNCE_ERROR. When a publisher wants to stop +either ANNOUNCE_OK or ANNOUNCE_ERROR. + +A Relay can receive announcements from multiple publishers for the same +Track Namespace and it SHOULD respond with the same response to each of the +publishers, as though it was responding to an ANNOUNCE +from a single publisher for a given tracknamespace. + +When a publisher wants to stop new subscriptions for an announced namespace it sends an UNANNOUNCE. A subscriber indicates it will no longer route subscriptions for a namespace it previously responded ANNOUNCE_OK to by sending an @@ -788,6 +853,16 @@ match on track namespace unless otherwise negotiated by the application. For example, a SUBSCRIBE namespace=foobar message will be forwarded to the session that sent ANNOUNCE namespace=foobar. +When a relay receives an incoming SUBSCRIBE request that triggers an +upstream subscription, it SHOULD send a SUBSCRIBE request to each +publisher that has announced the subscription's namespace, unless it +already has an active subscription for the Objects requested by the +incoming SUBSCRIBE request from all available publishers. + +When a relay receives an incoming ANNOUCE for a given namespace, for +each active upstream subscription that matches that namespace, it SHOULD send a +SUBSCRIBE to the publisher that sent the ANNOUNCE. + OBJECT message headers carry a short hop-by-hop `Track Alias` that maps to the Full Track Name (see {{message-subscribe-ok}}). Relays use the `Track Alias` of an incoming OBJECT message to identify its track and find @@ -795,6 +870,35 @@ the active subscribers for that track. Relays MUST forward OBJECT messages to matching subscribers in accordance to each subscription's priority, group order, and delivery timeout. +### Graceful Publisher Network Switchover + +This section describes behavior that a publisher MAY +choose to implement to allow for a better users experience when +switching between networks, such as WiFi to Cellular or vice versa. + +If the original publisher detects it is likely to need to switch networks, +for example because the WiFi signal is getting weaker, and it does not +have QUIC connection migration available, it establishes a new session +over the new interface and sends an ANNOUCE. The relay will forward +matching subscribes and the publisher publishes objects on both sessions. +Once the subscriptions have migrated over to session on the new network, +the publisher can stop publishing objects on the old network. The relay +will drop duplicate objects received on both subscriptions. +Ideally, the subscriptions downstream from the relay do no observe this +change, and keep receiving the objects on the same subscription. + +### Graceful Publisher Relay Switchover + +This section describes behavior that a publisher MAY choose to implement +to allow for a better user experience when a relay sends them a GOAWAY. + +When a publisher receives a GOAWAY, it starts the process of +connecting to a new relay and sends announces, but it does not immediately +stop publishing objects to the old relay. The new relay will send +subscribes and the publisher can start sending new objects to the new relay +instead of the old relay. Once objects are going to the new relay, +the announcement and subscription to the old relay can be stopped. + ## Relay Object Handling MOQT encodes the delivery information for a stream via OBJECT headers @@ -824,6 +928,7 @@ formatted as follows: ~~~ MOQT Control Message { Message Type (i), + Message Length (i), Message Payload (..), } ~~~ @@ -860,13 +965,25 @@ MOQT Control Message { |-------|-----------------------------------------------------| | 0x10 | GOAWAY ({{message-goaway}}) | |-------|-----------------------------------------------------| -| 0x11 | SUBSCRIBE_NAMESPACE ({{message-subscribe-ns}}) | +| 0x11 | SUBSCRIBE_ANNOUNCES ({{message-subscribe-ns}}) | +|-------|-----------------------------------------------------| +| 0x12 | SUBSCRIBE_ANNOUNCES_OK ({{message-sub-ann-ok}}) | +|-------|-----------------------------------------------------| +| 0x13 | SUBSCRIBE_ANNOUNCES_ERROR ({{message-sub-ann-error}}| +|-------|-----------------------------------------------------| +| 0x14 | UNSUBSCRIBE_ANNOUNCES ({{message-unsub-ann}}) | +|-------|-----------------------------------------------------| +| 0x15 | MAX_SUBSCRIBE_ID ({{message-max-subscribe-id}}) | |-------|-----------------------------------------------------| -| 0x12 | SUBSCRIBE_NAMESPACE_OK ({{message-sub-ns-ok}}) | +| 0x1A | SUBSCRIBES_BLOCKED ({{message-subscribes-blocked}}) | |-------|-----------------------------------------------------| -| 0x13 | SUBSCRIBE_NAMESPACE_ERROR ({{message-sub-ns-error}} | +| 0x16 | FETCH ({{message-fetch}}) | |-------|-----------------------------------------------------| -| 0x14 | UNSUBSCRIBE_NAMESPACE ({{message-unsub-ns}}) | +| 0x17 | FETCH_CANCEL ({{message-fetch-cancel}}) | +|-------|-----------------------------------------------------| +| 0x18 | FETCH_OK ({{message-fetch-ok}}) | +|-------|-----------------------------------------------------| +| 0x19 | FETCH_ERROR ({{message-fetch-error}}) | |-------|-----------------------------------------------------| | 0x40 | CLIENT_SETUP ({{message-setup}}) | |-------|-----------------------------------------------------| @@ -874,6 +991,9 @@ MOQT Control Message { |-------|-----------------------------------------------------| An endpoint that receives an unknown message type MUST close the session. +Control messages have a length to make parsing easier, but no control +messages are intended to be ignored. If the length does not match the +length of the message content, the receiver MUST close the session. ## Parameters {#params} @@ -924,7 +1044,7 @@ these parameters to appear in Setup messages. #### AUTHORIZATION INFO {#authorization-info} AUTHORIZATION INFO parameter (key 0x02) identifies a track's authorization -information in a SUBSCRIBE, SUBSCRIBE_NAMESPACE or ANNOUNCE message. This +information in a SUBSCRIBE, SUBSCRIBE_ANNOUNCES or ANNOUNCE message. This parameter is populated for cases where the authorization is required at the track level. The value is an ASCII string. @@ -979,14 +1099,18 @@ setup parameters. TODO: describe GREASE for those. The wire format of the Setup messages are as follows: ~~~ -CLIENT_SETUP Message Payload { +CLIENT_SETUP Message { + Type (i) = 0x40, + Length (i), Number of Supported Versions (i), Supported Version (i) ..., Number of Parameters (i) ..., Setup Parameters (..) ..., } -SERVER_SETUP Message Payload { +SERVER_SETUP Message { + Type (i) = 0x41, + Length (i), Selected Version (i), Number of Parameters (i) ..., Setup Parameters (..) ..., @@ -1020,33 +1144,6 @@ identified as 0xff00000D. ### Setup Parameters {#setup-params} -#### ROLE {#role} - -The ROLE parameter (key 0x00) allows each endpoint to independently specify what -functionality they support for the session. It has three possible values, -which are of type varint: - -0x01: Publisher - -: The endpoint can process subscriptions and send objects, but not subscribe. - The endpoint MUST NOT send a SUBSCRIBE message and an ANNOUNCE MUST NOT be - sent to it. - -0x02: Subscriber - -: The endpoint can send subscriptions and receive objects, but not publish. - The endpoint MUST NOT send an ANNOUNCE message and a SUBSCRIBE MUST NOT be - sent to it. - -0x03: PubSub - -: The endpoint can act as a publisher or subscriber, and can send or process - any message type. - -Both endpoints MUST send a ROLE parameter with one of the three values -specified above. Both endpoints MUST close the session if the ROLE -parameter is missing or is not one of the three above-specified values. - #### PATH {#path} The PATH parameter (key 0x01) allows the client to specify the path of @@ -1055,7 +1152,7 @@ the server, or when WebTransport is used. If the peer receives a PATH parameter from the server, or when WebTransport is used, it MUST close the connection. It follows the URI formatting rules {{!RFC3986}}. -When connecting to a server using a URI with the "moq" scheme, the +When connecting to a server using a URI with the "moqt" scheme, the client MUST set the PATH parameter to the `path-abempty` portion of the URI; if `query` is present, the client MUST concatenate `?`, followed by the `query` portion of the URI to the parameter. @@ -1066,7 +1163,6 @@ The MAX_SUBSCRIBE_ID parameter (key 0x02) communicates an initial value for the Maximum Subscribe ID to the receiving subscriber. The default value is 0, so if not specified, the peer MUST NOT create subscriptions. - ## GOAWAY {#message-goaway} The server sends a `GOAWAY` message to initiate session migration ({{session-migration}}) with an optional URI. @@ -1078,7 +1174,10 @@ receives multiple GOAWAY messages. ~~~ GOAWAY Message { - New Session URI (b) + Type (i) = 0x10, + Length (i), + New Session URI Length (i), + New Session URI (..), } ~~~ {: #moq-transport-goaway-format title="MOQT GOAWAY Message"} @@ -1092,7 +1191,13 @@ GOAWAY Message { ## SUBSCRIBE {#message-subscribe-req} -### Filter Types {#sub-filter} +A subscription causes the publisher to send newly published objects for a track. +A subscriber MUST NOT make multiple active subscriptions for a track within a +single session and publishers SHOULD treat this as a protocol violation. +The only objects prior to the current object that can be requested are those +in the current group. + +**Filter Types** The subscriber specifies a filter on the subscription to allow the publisher to identify which objects need to be delivered. @@ -1108,34 +1213,41 @@ the current object of the current group. If no content has been delivered yet, the subscription starts with the first published or received group. AbsoluteStart (0x3): Specifies an open-ended subscription beginning -from the object identified in the StartGroup and StartObject fields. +from the object identified in the StartGroup and StartObject fields. If the +StartGroup is prior to the current group, the publisher MUST reply with a +SUBSCRIBE_ERROR with code 'Invalid Range'. AbsoluteRange (0x4): Specifies a closed subscription starting at StartObject in StartGroup and ending at EndObject in EndGroup. The start and end of the -range are inclusive. EndGroup and EndObject MUST specify the same or a later -object than StartGroup and StartObject. +range are inclusive. EndGroup MUST specify the same or a later group than +StartGroup. If the StartGroup is prior to the current group, the publisher MUST +reply with a SUBSCRIBE_ERROR with code 'Invalid Range'. A filter type other than the above MUST be treated as error. - -### SUBSCRIBE Format -A subscriber issues a SUBSCRIBE to a publisher to request a track. +If a subscriber wants to subscribe to Objects both before and after +the Latest Object, it can send a SUBSCRIBE for the Latest Object +followed by a FETCH. Depending upon the application, one might want to send +both messages at the same time or wait for the first to return before sending +the second. The format of SUBSCRIBE is as follows: ~~~ SUBSCRIBE Message { + Type (i) = 0x3, + Length (i), Subscribe ID (i), Track Alias (i), Track Namespace (tuple), - Track Name (b), + Track Name Length (i), + Track Name (..), Subscriber Priority (8), Group Order (8), Filter Type (i), [StartGroup (i), StartObject (i)], - [EndGroup (i), - EndObject (i)], + [EndGroup (i)], Number of Parameters (i), Subscribe Parameters (..) ... } @@ -1170,7 +1282,6 @@ used. Values larger than 0x2 are a protocol error. * Filter Type: Identifies the type of filter, which also indicates whether the StartGroup/StartObject and EndGroup/EndObject fields will be present. -See ({{sub-filter}}). * StartGroup: The start Group ID. Only present for "AbsoluteStart" and "AbsoluteRange" filter types. @@ -1178,10 +1289,8 @@ See ({{sub-filter}}). * StartObject: The start Object ID. Only present for "AbsoluteStart" and "AbsoluteRange" filter types. -* EndGroup: The end Group ID. Only present for the "AbsoluteRange" filter type. - -* EndObject: The end Object ID, plus 1. A value of 0 means the entire group is -requested. Only present for the "AbsoluteRange" filter type. +* EndGroup: The end Group ID, inclusive. Only present for the "AbsoluteRange" +filter type. * Subscribe Parameters: The parameters are defined in {{version-specific-params}}. @@ -1189,9 +1298,9 @@ On successful subscription, the publisher MUST reply with a SUBSCRIBE_OK, allowing the subscriber to determine the start group/object when not explicitly specified and the publisher SHOULD start delivering objects. -If a publisher cannot satisfy the requested start or end for the subscription it -MAY send a SUBSCRIBE_ERROR with code 'Invalid Range'. A publisher MUST NOT send -objects from outside the requested start and end. +If a publisher cannot satisfy the requested start or end or if the end has +already been published it SHOULD send a SUBSCRIBE_ERROR with code 'Invalid Range'. +A publisher MUST NOT send objects from outside the requested start and end. ## SUBSCRIBE_UPDATE {#message-subscribe-update-req} @@ -1215,11 +1324,12 @@ The format of SUBSCRIBE_UPDATE is as follows: ~~~ SUBSCRIBE_UPDATE Message { + Type (i) = 0x2, + Length (i), Subscribe ID (i), StartGroup (i), StartObject (i), EndGroup (i), - EndObject (i), Subscriber Priority (8), Number of Parameters (i), Subscribe Parameters (..) ... @@ -1237,9 +1347,6 @@ This MUST match an existing Subscribe ID. * EndGroup: The end Group ID, plus 1. A value of 0 means the subscription is open-ended. -* EndObject: The end Object ID, plus 1. A value of 0 means the entire group is -requested. - * Subscriber Priority: Specifies the priority of a subscription relative to other subscriptions in the same session. Lower numbers get higher priority. See {{priorities}}. @@ -1258,6 +1365,8 @@ The format of `UNSUBSCRIBE` is as follows: ~~~ UNSUBSCRIBE Message { + Type (i) = 0xA, + Length (i), Subscribe ID (i) } ~~~ @@ -1265,14 +1374,124 @@ UNSUBSCRIBE Message { * Subscribe ID: Subscription Identifier as defined in {{message-subscribe-req}}. + +## FETCH {#message-fetch} + +A subscriber issues a FETCH to a publisher to request a range of already published +objects within a track. The publisher responding to a FETCH is responsible for retrieving +all available Objects. If there are gaps between Objects, the publisher omits them from the +fetch response. All omitted objects have status Object Not Available. + +A publisher responds to a FETCH request with either a FETCH_OK or a FETCH_ERROR +message. If it responds with FETCH_OK, the publisher creates a new unidirectional +stream that is used to send the Objects. A relay MAY start sending objects immediately +in response to a FETCH, even if sending the FETCH_OK takes longer because it requires +going upstream to populate the latest object. + +The Object Forwarding Preference does not apply to fetches. + +Fetch specifies an inclusive range of Objects starting at StartObject +in StartGroup and ending at EndObject in EndGroup. EndGroup and EndObject MUST +specify the same or a later object than StartGroup and StartObject. + +The format of FETCH is as follows: + +~~~ +FETCH Message { + Type (i) = 0x16, + Length (i), + Subscribe ID (i), + Track Namespace (tuple), + Track Name Length (i), + Track Name (..), + Subscriber Priority (8), + Group Order (8), + StartGroup (i), + StartObject (i), + EndGroup (i), + EndObject (i), + Number of Parameters (i), + Parameters (..) ... +} +~~~ +{: #moq-transport-fetch-format title="MOQT FETCH Message"} + +* Subscribe ID: The Subscribe ID identifies a given fetch request. Subscribe ID +is a variable length integer that MUST be unique and monotonically increasing +within a session. + +* Track Namespace: Identifies the namespace of the track as defined in +({{track-name}}). + +* Track Name: Identifies the track name as defined in ({{track-name}}). + +* Subscriber Priority: Specifies the priority of a fetch request relative to +other subscriptions or fetches in the same session. Lower numbers get higher +priority. See {{priorities}}. + +* Group Order: Allows the subscriber to request Objects be delivered in +Ascending (0x1) or Descending (0x2) order by group. See {{priorities}}. +A value of 0x0 indicates the original publisher's Group Order SHOULD be +used. Values larger than 0x2 are a protocol error. + +* StartGroup: The start Group ID. + +* StartObject: The start Object ID. + +* EndGroup: The end Group ID. + +* EndObject: The end Object ID, plus 1. A value of 0 means the entire group is +requested. + +* Parameters: The parameters are defined in {{version-specific-params}}. + + +Objects that are not yet published will not be retrieved by a FETCH. +The latest available Object is indicated in the FETCH_OK, and is the last +Object a fetch will return if the EndGroup and EndObject have not yet been +published. + +A publisher MUST send fetched groups in the determined group order, either +ascending or descending. Within each group, objects are sent in Object ID order; +subgroup ID is not used for ordering. + +If StartGroup/StartObject is greater than the latest published Object group, +the publisher MUST return FETCH_ERROR with error code 'No Objects'. + +A publisher MUST send fetched groups in group order, either ascending or +descending. Within each group, objects are sent in Object ID order; +subgroup ID is not used for ordering. + +## FETCH_CANCEL {#message-fetch-cancel} + +A subscriber issues a `FETCH_CANCEL` message to a publisher indicating it is no +longer interested in receiving Objects for the fetch specified by 'Subscribe ID'. +The publisher SHOULD close the unidirectional stream as soon as possible. + +The format of `FETCH_CANCEL` is as follows: + +~~~ +FETCH_CANCEL Message { + Type (i) = 0x17, + Length (i), + Subscribe ID (i) +} +~~~ +{: #moq-transport-fetch-cancel title="MOQT FETCH_CANCEL Message"} + +* Subscribe ID: Subscription Identifier as defined in {{message-fetch}}. + + ## ANNOUNCE_OK {#message-announce-ok} The subscriber sends an ANNOUNCE_OK control message to acknowledge the successful authorization and acceptance of an ANNOUNCE message. ~~~ -ANNOUNCE_OK +ANNOUNCE_OK Message { + Type (i) = 0x7, + Length (i), Track Namespace (tuple), } ~~~ @@ -1287,11 +1506,14 @@ The subscriber sends an ANNOUNCE_ERROR control message for tracks that failed authorization. ~~~ -ANNOUNCE_ERROR +ANNOUNCE_ERROR Message { + Type (i) = 0x8, + Length (i), Track Namespace (tuple), Error Code (i), - Reason Phrase (b), + Reason Phrase Length (i), + Reason Phrase (..), } ~~~ {: #moq-transport-announce-error format title="MOQT ANNOUNCE_ERROR Message"} @@ -1315,9 +1537,12 @@ receiving an ANNOUNCE_CANCEL, it SHOULD close the session as a ~~~ ANNOUNCE_CANCEL Message { + Type (i) = 0xC, + Length (i), Track Namespace (tuple), Error Code (i), - Reason Phrase (b), + Reason Phrase Length (i), + Reason Phrase Length (..), } ~~~ {: #moq-transport-announce-cancel-format title="MOQT ANNOUNCE_CANCEL Message"} @@ -1339,70 +1564,77 @@ A TRACK_STATUS message MUST be sent in response to each TRACK_STATUS_REQUEST. ~~~ TRACK_STATUS_REQUEST Message { + Type (i) = 0xD, + Length (i), Track Namespace (tuple), - Track Name (b), + Track Name Length (i), + Track Name (..), } ~~~ {: #moq-track-status-request-format title="MOQT TRACK_STATUS_REQUEST Message"} -## SUBSCRIBE_NAMESPACE {#message-subscribe-ns} +## SUBSCRIBE_ANNOUNCES {#message-subscribe-ns} -The subscriber sends the SUBSCRIBE_NAMESPACE control message to a publisher to -request the current set of matching announcements, as well as future updates to -the set. +The subscriber sends the SUBSCRIBE_ANNOUNCES control message to a publisher +to request the current set of matching announcements, as well as future updates +to the set. ~~~ -SUBSCRIBE_NAMESPACE Message { +SUBSCRIBE_ANNOUNCES Message { + Type (i) = 0x11, + Length (i), Track Namespace Prefix (tuple), Number of Parameters (i), Parameters (..) ..., } ~~~ -{: #moq-transport-subscribe-ns-format title="MOQT SUBSCRIBE_NAMESPACE Message"} +{: #moq-transport-subscribe-ns-format title="MOQT SUBSCRIBE_ANNOUNCES Message"} * Track Namespace Prefix: An ordered N-Tuple of byte fields which are matched against track namespaces known to the publisher. For example, if the publisher is a relay that has received ANNOUNCE messages for namespaces ("example.com", "meeting=123", "participant=100") and ("example.com", "meeting=123", -"participant=200"), a SUBSCRIBE_NAMESPACE for ("example.com", "meeting=123") +"participant=200"), a SUBSCRIBE_ANNOUNCES for ("example.com", "meeting=123") would match both. * Parameters: The parameters are defined in {{version-specific-params}}. -The publisher will respond with SUBSCRIBE_NAMESPACE_OK or -SUBSCRIBE_NAMESPACE_ERROR. If the SUBSCRIBE_NAMESPACE is successful, +The publisher will respond with SUBSCRIBE_ANNOUNCES_OK or +SUBSCRIBE_ANNOUNCES_ERROR. If the SUBSCRIBE_ANNOUNCES is successful, the publisher will forward any matching ANNOUNCE messages to the subscriber that it has not yet sent. If the set of matching ANNOUNCE messages changes, the publisher sends the corresponding ANNOUNCE or UNANNOUNCE message. A subscriber cannot make overlapping namespace subscriptions on a single -session. Within a session, if a publisher receives a SUBSCRIBE_NAMESPACE with a -Track Namespace Prefix that is a prefix of an earlier SUBSCRIBE_NAMESPACE or -vice versa, it MUST respond with SUBSCRIBE_NAMESPACE_ERROR, with error code -SUBSCRIBE_NAMESPACE_OVERLAP. +session. Within a session, if a publisher receives a SUBSCRIBE_ANNOUNCES +with a Track Namespace Prefix that is a prefix of an earlier +SUBSCRIBE_ANNOUNCES or vice versa, it MUST respond with +SUBSCRIBE_ANNOUNCES_ERROR, with error code SUBSCRIBE_ANNOUNCES_OVERLAP. The publisher MUST ensure the subscriber is authorized to perform this namespace subscription. -SUBSCRIBE_NAMESPACE is not required for a publisher to send ANNOUNCE and +SUBSCRIBE_ANNOUNCES is not required for a publisher to send ANNOUNCE and UNANNOUNCE messages to a subscriber. It is useful in applications or relays where subscribers are only interested in or authorized to access a subset of available announcements. -## UNSUBSCRIBE_NAMESPACE {#message-unsub-ns} +## UNSUBSCRIBE_ANNOUNCES {#message-unsub-ann} -A subscriber issues a `UNSUBSCRIBE_NAMESPACE` message to a publisher indicating -it is no longer interested in ANNOUNCE and UNANNOUNCE messages for the specified -track namespace prefix. +A subscriber issues a `UNSUBSCRIBE_ANNOUNCES` message to a publisher +indicating it is no longer interested in ANNOUNCE and UNANNOUNCE messages for +the specified track namespace prefix. -The format of `UNSUBSCRIBE_NAMESPACE` is as follows: +The format of `UNSUBSCRIBE_ANNOUNCES` is as follows: ~~~ -UNSUBSCRIBE_NAMESPACE Message { +UNSUBSCRIBE_ANNOUNCES Message { + Type (i) = 0x14, + Length (i), Track Namespace Prefix (tuple) } ~~~ -{: #moq-transport-unsub-ns-format title="MOQT UNSUBSCRIBE Message"} +{: #moq-transport-unsub-ann-format title="MOQT UNSUBSCRIBE Message"} * Track Namespace Prefix: As defined in {{message-subscribe-ns}}. @@ -1414,10 +1646,12 @@ subscriptions. ~~~ SUBSCRIBE_OK { + Type (i) = 0x4, + Length (i), Subscribe ID (i), Expires (i), Group Order (8), - ContentExists (f), + ContentExists (8), [Largest Group ID (i)], [Largest Object ID (i)], Number of Parameters (i), @@ -1439,7 +1673,8 @@ Values of 0x0 and those larger than 0x2 are a protocol error. * ContentExists: 1 if an object has been published on this track, 0 if not. If 0, then the Largest Group ID and Largest Object ID fields will not be -present. +present. Any other value is a protocol error and MUST terminate the +session with a Protocol Violation ({{session-termination}}). * Largest Group ID: The largest Group ID available for this track. This field is only present if ContentExists has a value of 1. @@ -1457,9 +1692,12 @@ failed SUBSCRIBE. ~~~ SUBSCRIBE_ERROR { + Type (i) = 0x5, + Length (i), Subscribe ID (i), Error Code (i), - Reason Phrase (b), + Reason Phrase Length (i), + Reason Phrase (..), Track Alias (i), } ~~~ @@ -1477,6 +1715,71 @@ SUBSCRIBE_ERROR ({{session-termination}}). +## FETCH_OK {#message-fetch-ok} + +A publisher sends a FETCH_OK control message in response to successful fetches. +A publisher MAY send Objects in response to a FETCH before the FETCH_OK message is sent, +but the FETCH_OK MUST NOT be sent until the latest group and object are known. + +~~~ +FETCH_OK +{ + Type (i) = 0x18, + Length (i), + Subscribe ID (i), + Group Order (8), + End Of Track (8), + Largest Group ID (i), + Largest Object ID (i), + Number of Parameters (i), + Subscribe Parameters (..) ... +} +~~~ +{: #moq-transport-fetch-ok format title="MOQT FETCH_OK Message"} + +* Subscribe ID: Fetch Identifier as defined in {{message-fetch}}. + +* Group Order: Indicates the fetch will be delivered in +Ascending (0x1) or Descending (0x2) order by group. See {{priorities}}. +Values of 0x0 and those larger than 0x2 are a protocol error. + +* End Of Track: 1 if all objects have been published on this track, so +the Largest Group ID and Object Id indicate the last Object in the track, +0 if not. + +* Largest Group ID: The largest Group ID available for this track. This field +is only present if ContentExists has a value of 1. + +* Largest Object ID: The largest Object ID available within the largest Group ID +for this track. This field is only present if ContentExists has a value of 1. + +* Subscribe Parameters: The parameters are defined in {{version-specific-params}}. + +## FETCH_ERROR {#message-fetch-error} + +A publisher sends a FETCH_ERROR control message in response to a +failed FETCH. + +~~~ +FETCH_ERROR +{ + Type (i) = 0x19, + Length (i), + Subscribe ID (i), + Error Code (i), + Reason Phrase Length (i), + Reason Phrase (..), +} +~~~ +{: #moq-transport-fetch-error format title="MOQT FETCH_ERROR Message"} + +* Subscribe ID: Subscription Identifier as defined in {{message-subscribe-req}}. + +* Error Code: Identifies an integer error code for fetch failure. + +* Reason Phrase: Provides the reason for fetch error. + + ## SUBSCRIBE_DONE {#message-subscribe-done} A publisher sends a `SUBSCRIBE_DONE` message to indicate it is done publishing @@ -1487,10 +1790,13 @@ The format of `SUBSCRIBE_DONE` is as follows: ~~~ SUBSCRIBE_DONE Message { + Type (i) = 0xB, + Length (i), Subscribe ID (i), Status Code (i), - Reason Phrase (b), - ContentExists (f), + Reason Phrase Length (i), + Reason Phrase (..), + ContentExists (8), [Final Group (i)], [Final Object (i)], } @@ -1505,6 +1811,8 @@ SUBSCRIBE_DONE Message { * ContentExists: 1 if an object has been published for this subscription, 0 if not. If 0, then the Final Group and Final Object fields will not be present. +Any other value is a protocol error and MUST terminate the session with a +Protocol Violation ({{session-termination}}). * Final Group: The largest Group ID sent by the publisher in an OBJECT message in this track. @@ -1524,15 +1832,41 @@ value is a 'Protocol Violation'. ~~~ MAX_SUBSCRIBE_ID { + Type (i) = 0x15, + Length (i), Subscribe ID (i), } ~~~ {: #moq-transport-max-subscribe-id format title="MOQT MAX_SUBSCRIBE_ID Message"} * Subscribe ID: The new Maximum Subscribe ID for the session. If a Subscribe ID -equal or larger than this is received in any message, including SUBSCRIBE, -the publisher MUST close the session with an error of 'Too Many Subscribes'. -More on Subscribe ID in {{message-subscribe-req}}. +{{message-subscribe-req}} equal or larger than this is received by the publisher +that sent the MAX_SUBSCRIBE_ID, the publisher MUST close the session with an +error of 'Too Many Subscribes'. + +## SUBSCRIBES_BLOCKED {#message-subscribes-blocked} + +The SUBSCRIBES_BLOCKED message is sent when a subscriber would like to begin +a new subscription, but cannot because the Subscribe ID would exceed the +Maximum Subscribe ID value sent by the peer. The subscriber SHOULD send only +one SUBSCRIBES_BLOCKED for a given Maximum Subscribe ID. + +A publisher MAY send a MAX_SUBSCRIBE_ID upon receipt of SUBSCRIBES_BLOCKED, +but it MUST NOT rely on SUBSCRIBES_BLOCKED to trigger sending a +MAX_SUBSCRIBE_ID, because sending SUBSCRIBES_BLOCKED is not required. + +~~~ +SUBSCRIBES_BLOCKED +{ + Type (i) = 0x1A, + Length (i), + Maximum Subscribe ID (i), +} +~~~ +{: #moq-transport-subscribes-blocked format title="MOQT SUBSCRIBES_BLOCKED Message"} + +* Maximum Subscribe ID: The Maximum Subscribe ID for the session on which the subscriber +is blocked. More on Subscribe ID in {{message-subscribe-req}}. ## ANNOUNCE {#message-announce} @@ -1544,6 +1878,8 @@ publish tracks under this namespace. ~~~ ANNOUNCE Message { + Type (i) = 0x6, + Length (i), Track Namespace (tuple), Number of Parameters (i), Parameters (..) ..., @@ -1565,6 +1901,8 @@ within the provided Track Namespace. ~~~ UNANNOUNCE Message { + Type (i) = 0x9, + Length (i), Track Namespace (tuple), } ~~~ @@ -1581,8 +1919,11 @@ to a TRACK_STATUS_REQUEST message. ~~~ TRACK_STATUS Message { + Type (i) = 0xE, + Length (i), Track Namespace (tuple), - Track Name (b), + Track Name Length(i), + Track Name (..), Status Code (i), Last Group ID (i), Last Object ID (i), @@ -1626,35 +1967,42 @@ The receiver of multiple TRACK_STATUS messages for a track uses the information from the latest arriving message, as they are delivered in order on a single stream. -## SUBSCRIBE_NAMESPACE_OK {#message-sub-ns-ok} +## SUBSCRIBE_ANNOUNCES_OK {#message-sub-ann-ok} -A publisher sends a SUBSCRIBE_NAMESPACE_OK control message for successful +A publisher sends a SUBSCRIBE_ANNOUNCES_OK control message for successful namespace subscriptions. ~~~ -SUBSCRIBE_NAMESPACE_OK +SUBSCRIBE_ANNOUNCES_OK { + Type (i) = 0x12, + Length (i), Track Namespace Prefix (tuple), } ~~~ -{: #moq-transport-sub-ns-ok format title="MOQT SUBSCRIBE_NAMESPACE_OK Message"} +{: #moq-transport-sub-ann-ok format title="MOQT SUBSCRIBE_ANNOUNCES_OK +Message"} * Track Namespace Prefix: As defined in {{message-subscribe-ns}}. -## SUBSCRIBE_NAMESPACE_ERROR {#message-sub-ns-error} +## SUBSCRIBE_ANNOUNCES_ERROR {#message-sub-ann-error} -A publisher sends a SUBSCRIBE_NAMESPACE_ERROR control message in response to a -failed SUBSCRIBE_NAMESPACE. +A publisher sends a SUBSCRIBE_ANNOUNCES_ERROR control message in response to +a failed SUBSCRIBE_ANNOUNCES. ~~~ -SUBSCRIBE_NAMESPACE_ERROR +SUBSCRIBE_ANNOUNCES_ERROR { + Type (i) = 0x13, + Length (i), Track Namespace Prefix (tuple), Error Code (i), - Reason Phrase (b), + Reason Phrase Length (i), + Reason Phrase (..), } ~~~ -{: #moq-transport-sub-ns-error format title="MOQT SUBSCRIBE_NAMESPACE_ERROR Message"} +{: #moq-transport-sub-ann-error format +title="MOQT SUBSCRIBE_ANNOUNCES_ERROR Message"} * Track Namespace Prefix: As defined in {{message-subscribe-ns}}. @@ -1664,6 +2012,7 @@ failure. * Reason Phrase: Provides the reason for the namespace subscription error. + # Data Streams {#data-streams} A publisher sends Objects matching a subscription on Data Streams. @@ -1671,15 +2020,15 @@ A publisher sends Objects matching a subscription on Data Streams. All unidirectional MOQT streams, as well as all datagrams, start with a variable-length integer indicating the type of the stream in question. -|-------|-----------------------------------------------------| -| ID | Stream Type | -|------:|:----------------------------------------------------| -| 0x1 | OBJECT_DATAGRAM ({{object-datagram}}) | -|-------|-----------------------------------------------------| -| 0x2 | STREAM_HEADER_TRACK ({{stream-header-track}}) | -|-------|-----------------------------------------------------| -| 0x4 | STREAM_HEADER_PEEP ({{stream-header-peep}}) | -|-------|-----------------------------------------------------| +|-------|-------------------------------------------------------| +| ID | Stream Type | +|------:|:------------------------------------------------------| +| 0x1 | OBJECT_DATAGRAM ({{object-datagram}}) | +|-------|-------------------------------------------------------| +| 0x4 | STREAM_HEADER_SUBGROUP ({{stream-header-subgroup}}) | +|-------|-------------------------------------------------------| +| 0x5 | FETCH_HEADER ({{fetch-header}}) | +|-------|-------------------------------------------------------| An endpoint that receives an unknown stream type MUST close the session. @@ -1711,10 +2060,10 @@ group. the Object {{priorities}}. * Object Forwarding Preference: An enumeration indicating how a publisher sends -an object. The preferences are Track, Peep, and Datagram. An Object +an object. The preferences are Track, Subgroup, and Datagram. An Object MUST be sent according to its `Object Forwarding Preference`, described below. -* Peep ID: The object is a member of the indicated peep ID ({{model-peep}}) +* Subgroup ID: The object is a member of the indicated subgroup ID ({{model-subgroup}}) within the group. This field is omitted if the Object Forwarding Preference is Track or Datagram. @@ -1743,7 +2092,7 @@ are beyond the end of a group or track. GroupID. This is sent right after the last object in the group. If the ObjectID is 0, it indicates there are no Objects in this Group. This SHOULD be cached. A publisher MAY use an end of - Group object to signal the end of all open Peeps in a Group. + Group object to signal the end of all open Subgroups in a Group. * 0x4 := Indicates end of Track and Group. GroupID is one greater than the largest group produced in this track and the ObjectId is @@ -1751,9 +2100,6 @@ are beyond the end of a group or track. group. This is sent right after the last object in the track. This SHOULD be cached. -* 0x5 := Indicates end of Peep. Object ID is one greater than the largest - normal object ID in the Peep. - Any other value SHOULD be treated as a protocol error and terminate the session with a Protocol Violation ({{session-termination}}). Any object with a status code other than zero MUST have an empty payload. @@ -1767,14 +2113,13 @@ An `OBJECT_DATAGRAM` message carries a single object in a datagram. An Object received in an `OBJECT_DATAGRAM` message has an `Object Forwarding Preference` = `Datagram`. To send an Object with `Object -Forwarding Preference` = `Datagram`, determine the length of the fields and +Forwarding Preference` = `Datagram`, determine the length of the header and payload and send the Object as datagram. In certain scenarios where the object size can be larger than maximum datagram size for the session, the Object will be dropped. ~~~ OBJECT_DATAGRAM Message { - Subscribe ID (i), Track Alias (i), Group ID (i), Object ID (i), @@ -1788,8 +2133,8 @@ OBJECT_DATAGRAM Message { ## Streams -When objects are sent on streams, the stream begins with a stream -header message and is followed by one or more sets of serialized object fields. +When objects are sent on streams, the stream begins with a stream header +message and is followed by one or more sets of serialized object fields. If a stream ends gracefully in the middle of a serialized Object, terminate the session with a Protocol Violation. @@ -1799,84 +2144,141 @@ header message type and fields. TODO: figure out how a relay closes these streams -### Stream Header Track -When a stream begins with `STREAM_HEADER_TRACK`, all objects on the stream -belong to the track requested in the Subscribe message identified by `Subscribe -ID`. All objects on the stream have the `Publisher Priority` specified in the -stream header. +### Stream Header Subgroup + +When a stream begins with `STREAM_HEADER_SUBGROUP`, 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`. ~~~ -STREAM_HEADER_TRACK Message { - Subscribe ID (i) +STREAM_HEADER_SUBGROUP Message { Track Alias (i), + Group ID (i), + Subgroup ID (i), Publisher Priority (8), } ~~~ -{: #stream-header-track-format title="MOQT STREAM_HEADER_TRACK Message"} +{: #stream-header-subgroup-format title="MOQT STREAM_HEADER_SUBGROUP Message"} + +All Objects received on a stream opened with `STREAM_HEADER_SUBGROUP` have an +`Object Forwarding Preference` = `Subgroup`. -All Objects received on a stream opened with STREAM_HEADER_TRACK have an `Object -Forwarding Preference` = `Track`. +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 `STREAM_HEADER_SUBGROUP`. Then serialize the +following fields. -To send an Object with `Object Forwarding Preference` = `Track`, find the open -stream that is associated with the subscription, or open a new one and send the -`STREAM_HEADER_TRACK` if needed, then serialize the following object fields. The Object Status field is only sent if the Object Payload Length is zero. ~~~ { - Group ID (i), Object ID (i), Object Payload Length (i), [Object Status (i)], Object Payload (..), } ~~~ -{: #object-track-format title="MOQT Track Stream Object Fields"} - -A publisher MUST NOT send an Object on a stream if its Group ID is less than a -previously sent Group ID on that stream, or if its Object ID is less than or -equal to a previously sent Object ID with the same Group ID. +{: #object-group-format title="MOQT Group Stream Object Fields"} -### Stream Header Peep +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. -When a stream begins with `STREAM_HEADER_PEEP`, all objects on the stream -belong to the track requested in the Subscribe message identified by `Subscribe -ID` and the peep indicated by 'Group ID' and `Peep ID`. +### 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. + +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 STREAM_HEADER_SUBGROUP 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. + +### Fetch Header {#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`. ~~~ -STREAM_HEADER_PEEP Message { +FETCH_HEADER Message { Subscribe ID (i), - Track Alias (i), - Group ID (i), - Peep ID (i), - Publisher Priority (8), } ~~~ -{: #stream-header-peep-format title="MOQT STREAM_HEADER_PEEP Message"} - -All Objects received on a stream opened with `STREAM_HEADER_PEEP` have an -`Object Forwarding Preference` = `Peep`. +{: #fetch-header-format title="MOQT FETCH_HEADER Message"} -To send an Object with `Object Forwarding Preference` = `Peep`, find the open -stream that is associated with the subscription, `Group ID` and `Peep ID`, -or open a new one and send the `STREAM_HEADER_PEEP`. Then serialize the -following fields. -The Object Status field is only sent if the Object Payload Length is zero. +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), Object Payload Length (i), [Object Status (i)], Object Payload (..), } ~~~ -{: #object-group-format title="MOQT Group Stream Object Fields"} +{: #object-fetch-format title="MOQT Fetch 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. +The Object Status field is only sent if the Object Payload Length is zero. ## Examples @@ -1884,7 +2286,6 @@ Sending a track on one stream: ~~~ STREAM_HEADER_TRACK { - Subscribe ID = 1 Track Alias = 1 Publisher Priority = 0 } @@ -1902,16 +2303,15 @@ STREAM_HEADER_TRACK { } ~~~ -Sending a peep on one stream: +Sending a subgroup on one stream: ~~~ Stream = 2 -STREAM_HEADER_PEEP { - Subscribe ID = 2 +STREAM_HEADER_SUBGROUP { Track Alias = 2 Group ID = 0 - Peep ID = 0 + Subgroup ID = 0 Publisher Priority = 0 } {