jiebi/TextFlow huggingface dataset:
Original text:
Reason Phrase: Provides the reason for subscription error.  Final Group: The largest Group ID sent by the publisher in an OBJECT message in this track.

Reviewer comments:
To allow subscribers to indicate that no new subscriptions will be routed to the publisher for a given namespace. Happy to bikeshed on the name and whether we really need another frame for this.
The problem is not possible for the subscriber to reject an ANNOUNCE afterwards. This would be very useful, for example to inform a publisher that no more subscriptions will be routed to them for whatever reason. Examples: The auth parameter in ANNOUNCE has expired. Another publisher has sent an overriding ANNOUNCE (reconnect?) The broadcast has been closed remotely, ex. some moderation tool. Without this message, the only recourse for the subscriber is to close the connection. This won't work if multiple ANNOUNCE messages are pooled over the same connection, nor is it clean.
I propose two options: can be sent after . We add and to mirror . I personally hate 3 separate messages for a subscriber to close an announce, or a publisher to close a subscription. See
very trivial bikeshed comment ... I would prefer to call it something other RESET ( and also other places we use RESET ). I get the TCP analogy but I think that is the wrong way to think about this. I would prefer something like REVOKE.
how about ANNOUNCE_CANCEL to cancel an open announce ?
Conclusion is that we do need to add a new frame to end an ANNOUNCE. Additionally, there was a principle stated that we should not rely on closing the session as the typical or optimal way to close state and close either an ANNOUNCE or SUBSCRIBE.
On the call today, people tend to prefer ANNOUNCECLOSE over ANNOUNCECANCEL as a name. They also prefer merging SUBSCRIBERESET and SUBSCRIBEFIN into a single message, possibly SUBSCRIBE_CLOSE.
On an editorial note, it'd be nice to have subsections for publisher/subscriber to make it clearer to the reader.
If we're going to combine and , then we should just combine and now. My desire is to have four messages: publisher create: ANNOUNCE publisher close: UNANNOUNCE subscriber accept: ANNOUNCEOK subscriber close: ANNOUNCESTOP The same goes for subscriptions: subscriber create: SUBSCRIBE subscriber close: UNSUBSCRIBE publisher accept: SUBSCRIBEOK publisher close: SUBSCRIBESTOP

Revised text:
Reason Phrase: Provides the reason for subscription error. <ins> 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. </ins> Final Group: The largest Group ID sent by the publisher in an OBJECT message in this track.
Original text:
6.14.  The server sends a "GOAWAY" message to initiate session migration (session-migration) with an optional URI.

Reviewer comments:
To allow subscribers to indicate that no new subscriptions will be routed to the publisher for a given namespace. Happy to bikeshed on the name and whether we really need another frame for this.
The problem is not possible for the subscriber to reject an ANNOUNCE afterwards. This would be very useful, for example to inform a publisher that no more subscriptions will be routed to them for whatever reason. Examples: The auth parameter in ANNOUNCE has expired. Another publisher has sent an overriding ANNOUNCE (reconnect?) The broadcast has been closed remotely, ex. some moderation tool. Without this message, the only recourse for the subscriber is to close the connection. This won't work if multiple ANNOUNCE messages are pooled over the same connection, nor is it clean.
I propose two options: can be sent after . We add and to mirror . I personally hate 3 separate messages for a subscriber to close an announce, or a publisher to close a subscription. See
very trivial bikeshed comment ... I would prefer to call it something other RESET ( and also other places we use RESET ). I get the TCP analogy but I think that is the wrong way to think about this. I would prefer something like REVOKE.
how about ANNOUNCE_CANCEL to cancel an open announce ?
Conclusion is that we do need to add a new frame to end an ANNOUNCE. Additionally, there was a principle stated that we should not rely on closing the session as the typical or optimal way to close state and close either an ANNOUNCE or SUBSCRIBE.
On the call today, people tend to prefer ANNOUNCECLOSE over ANNOUNCECANCEL as a name. They also prefer merging SUBSCRIBERESET and SUBSCRIBEFIN into a single message, possibly SUBSCRIBE_CLOSE.
On an editorial note, it'd be nice to have subsections for publisher/subscriber to make it clearer to the reader.
If we're going to combine and , then we should just combine and now. My desire is to have four messages: publisher create: ANNOUNCE publisher close: UNANNOUNCE subscriber accept: ANNOUNCEOK subscriber close: ANNOUNCESTOP The same goes for subscriptions: subscriber create: SUBSCRIBE subscriber close: UNSUBSCRIBE publisher accept: SUBSCRIBEOK publisher close: SUBSCRIBESTOP

Revised text:
6.14. <ins> The subscriber sends an "ANNOUNCE_CANCEL" control message to indicate it will stop sending new subscriptions for tracks within the provided Track Namespace. If a publisher recieves new subscriptions for that namespace after receiving an ANNOUNCE_CANCEL, it SHOULD close the session as a 'Protocol Violation'. Track Namespace: Identifies a track's namespace as defined in (track-name). 6.15. </ins> The server sends a "GOAWAY" message to initiate session migration (session-migration) with an optional URI.
Original text:
No Error: The session is being terminated without an error. <del> Generic Error: An unclassified error occurred. </del>  Unauthorized: The endpoint breached an agreement, which MAY have been pre-negotiated by the application.

Reviewer comments:
Internal server error has been useful in HTTP, so it makes sense to include something similar for MoQ.

Revised text:
No Error: The session is being terminated without an error.  <ins> Internal Error: An implementation specific error occurred. </ins> Unauthorized: The endpoint breached an agreement, which MAY have been pre-negotiated by the application.
Original text:
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 <del> until it expires, until the publisher of the track terminates the track with a SUBSCRIBE_DONE (see message-subscribe-done). </del>  Objects MUST NOT be sent for unsuccessful subscriptions, and if a subscriber receives a SUBSCRIBE_ERROR after receiving objects, it

Reviewer comments:
Note from call. Fixe subscribeDone at same time. Define a new bool type. p[ick new leatter other than b
Also make sure to fix SUBSCRIBE_DONE at the same time.
NAME - I updated, have another read and see if it looks OK. Thanks
and introduced a field to SUBSCRIBEOK, SUBSCRIBEFIN, and SUBSCRIBE_RESET. If I read the notation correctly, it has a size of 1 bit, which makes it hard to parse the rest of the message. Could we encode that information in the message type by using two types per message instead of a 1-bit field?
I think the intent is that it is one byte not one bit.
Maybe it should have been:
or just a byte field with only 2 possible values allowed (1) and (0) ..
I agree we need to keep this byte aligned however we do it.
IMO just make it a VarInt (or byte), with only values of 0 and 1 currently valid. This encoding is temporary until we nail down all of the fields/flags so we shouldn't be making life difficult or efficient.
Individual Comment: Even if it's temporary, my preference is for a one-bit field and unused/reserved bits to align, because there are no error conditions.
Have a look at PR

Revised text:
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  <ins> until the publisher of the track terminates the track with a SUBSCRIBE_DONE (see message-subscribe-done). </ins> Objects MUST NOT be sent for unsuccessful subscriptions, and if a subscriber receives a SUBSCRIBE_ERROR after receiving objects, it
Original text:
subscribe-req. Expires: Time in milliseconds after which the subscription is no <del> longer valid.  A value of 0 indicates that the subscription stays active until it is explicitly unsubscribed. </del>  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

Reviewer comments:
Note from call. Fixe subscribeDone at same time. Define a new bool type. p[ick new leatter other than b
Also make sure to fix SUBSCRIBE_DONE at the same time.
NAME - I updated, have another read and see if it looks OK. Thanks
and introduced a field to SUBSCRIBEOK, SUBSCRIBEFIN, and SUBSCRIBE_RESET. If I read the notation correctly, it has a size of 1 bit, which makes it hard to parse the rest of the message. Could we encode that information in the message type by using two types per message instead of a 1-bit field?
I think the intent is that it is one byte not one bit.
Maybe it should have been:
or just a byte field with only 2 possible values allowed (1) and (0) ..
I agree we need to keep this byte aligned however we do it.
IMO just make it a VarInt (or byte), with only values of 0 and 1 currently valid. This encoding is temporary until we nail down all of the fields/flags so we shouldn't be making life difficult or efficient.
Individual Comment: Even if it's temporary, my preference is for a one-bit field and unused/reserved bits to align, because there are no error conditions.
Have a look at PR

Revised text:
subscribe-req. Expires: Time in milliseconds after which the subscription is no  <ins> longer valid.  A value of 0 indicates that the subscription does not expire or expires at an unknown time.  Expires is advisory and a subscription can end prior to the expiry time or last longer. </ins> 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
Original text:
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 <del> until it expires, until the publisher of the track terminates the track with a SUBSCRIBE_DONE (see message-subscribe-done). </del>  Objects MUST NOT be sent for unsuccessful subscriptions, and if a subscriber receives a SUBSCRIBE_ERROR after receiving objects, it

Reviewer comments:
And add a new SUBSCRIBE_DONE status code. Fixes part of , but it's still unclear how one might refresh a subscription without interruption.
Subscriptions always end with either SUBSCRIBEFIN or SUBSCRIBERESET, except when they Expire and then no control message is sent. This complicates the state machine and adds another case to handle. The subscriber needs to start a timer or something similar to know when the subscription will end, and it doesn't get to know the final group or object ID.
I'd like to get rid of , but my thought was that it's a signal to SUBSCRIBE again before the timeout to avoid interruption. Otherwise, there would be a SUBSCRIBE_RESET with an "expired" error code containing the final group/object.
I agree that a subscriber can deal with this field, but I don't know what purpose it serves for the publisher/relay? It's different from an idle connection timeout, because assuming there is data flowing for the subscription, it would never go idle. Unlike QUIC, I don't see value in having a 'silent close' for subscriptions, because typically we expect subscriptions to have data flowing at a somewhat regular interval. Also, there's no guarantee that the subscription will last until the expiry time, it can always end early with SUBSCRIBEFIN or SUBSCRIBERESET.
Based on last week's conversation, the Expires field doesn't change the state of the subscription by itself, a publisher would still have to send a FIN or RESET to close it. It's still unclear as to what a subscriber should do with it. There was a fair amount of discussion around refreshing one's authorization. It's unclear if that would be done with a SUBSCRIBE_UPDATE or something similar or via UNSUBSCRIBE/SUBSCRIBE?
Would the solution to this be when the relay expires a subscription, it sends a control message ? This might all become clearer once we figure out re authentication but at a high level, I want some way that the relays can clean up state on a time line that is under relay control particularly when poorly implemented clients make subscriptions.
I think this was resolved by -
LGTM

Revised text:
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  <ins> until the publisher of the track terminates the track with a SUBSCRIBE_DONE (see message-subscribe-done). </ins> Objects MUST NOT be sent for unsuccessful subscriptions, and if a subscriber receives a SUBSCRIBE_ERROR after receiving objects, it
Original text:
subscribe-req. Expires: Time in milliseconds after which the subscription is no <del> longer valid.  A value of 0 indicates that the subscription stays active until it is explicitly unsubscribed. </del>  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

Reviewer comments:
And add a new SUBSCRIBE_DONE status code. Fixes part of , but it's still unclear how one might refresh a subscription without interruption.
Subscriptions always end with either SUBSCRIBEFIN or SUBSCRIBERESET, except when they Expire and then no control message is sent. This complicates the state machine and adds another case to handle. The subscriber needs to start a timer or something similar to know when the subscription will end, and it doesn't get to know the final group or object ID.
I'd like to get rid of , but my thought was that it's a signal to SUBSCRIBE again before the timeout to avoid interruption. Otherwise, there would be a SUBSCRIBE_RESET with an "expired" error code containing the final group/object.
I agree that a subscriber can deal with this field, but I don't know what purpose it serves for the publisher/relay? It's different from an idle connection timeout, because assuming there is data flowing for the subscription, it would never go idle. Unlike QUIC, I don't see value in having a 'silent close' for subscriptions, because typically we expect subscriptions to have data flowing at a somewhat regular interval. Also, there's no guarantee that the subscription will last until the expiry time, it can always end early with SUBSCRIBEFIN or SUBSCRIBERESET.
Based on last week's conversation, the Expires field doesn't change the state of the subscription by itself, a publisher would still have to send a FIN or RESET to close it. It's still unclear as to what a subscriber should do with it. There was a fair amount of discussion around refreshing one's authorization. It's unclear if that would be done with a SUBSCRIBE_UPDATE or something similar or via UNSUBSCRIBE/SUBSCRIBE?
Would the solution to this be when the relay expires a subscription, it sends a control message ? This might all become clearer once we figure out re authentication but at a high level, I want some way that the relays can clean up state on a time line that is under relay control particularly when poorly implemented clients make subscriptions.
I think this was resolved by -
LGTM

Revised text:
subscribe-req. Expires: Time in milliseconds after which the subscription is no  <ins> longer valid.  A value of 0 indicates that the subscription does not expire or expires at an unknown time.  Expires is advisory and a subscription can end prior to the expiry time or last longer. </ins> 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
Original text:
StartGroup: The start Group ID.  Only present for "AbsoluteStart" and "AbsoluteRange" filter types. <del> StartObject: The start Object ID, plus 1.  A value of 0 means the entire group is requested.  Only present for "AbsoluteStart" and "AbsoluteRange" filter types. </del>  EndGroup: The end Group ID.  Only present for the "AbsoluteRange" filter type.

Reviewer comments:
I'm not totally sure what to do with the special value of 0 for StartObject and EndObject. If StartObject == 0, that's requesting the whole group. But if it's 1, that maps to a first object of zero, which is also consistent with requesting the whole group. For EndObject, the special value makes sense, because the subscriber might not know how many objects there are. There's also the error case where StartObject == 0 and EndObject != 0, or the reverse. IMO it would make more sense for EndObject to have the special value at zero, but not StartObject.
You're correct, this was an editorial error on my part. I did not intend to make StartObject have a special value.

Revised text:
StartGroup: The start Group ID.  Only present for "AbsoluteStart" and "AbsoluteRange" filter types.  <ins> StartObject: The start Object ID.  Only present for "AbsoluteStart" and "AbsoluteRange" filter types. </ins> EndGroup: The end Group ID.  Only present for the "AbsoluteRange" filter type.
Original text:
StartGroup: The start Group ID. <del> StartObject: The start Object ID, plus 1.  A value of 0 means the entire group is requested. </del>  EndGroup: The end Group ID, plus 1.  A value of 0 means the subscription is open-ended.

Reviewer comments:
I'm not totally sure what to do with the special value of 0 for StartObject and EndObject. If StartObject == 0, that's requesting the whole group. But if it's 1, that maps to a first object of zero, which is also consistent with requesting the whole group. For EndObject, the special value makes sense, because the subscriber might not know how many objects there are. There's also the error case where StartObject == 0 and EndObject != 0, or the reverse. IMO it would make more sense for EndObject to have the special value at zero, but not StartObject.
You're correct, this was an editorial error on my part. I did not intend to make StartObject have a special value.

Revised text:
StartGroup: The start Group ID.  <ins> StartObject: The start Object ID. </ins> EndGroup: The end Group ID, plus 1.  A value of 0 means the subscription is open-ended.
Original text:
(appendix.encoding). A segment MAY depend on any number of other segments.  The encoder <del> MUST indicate these dependecies on the wire via the "HEADERS" message (headers). </del>  The sender SHOULD NOT use this list of dependencies to determine which segment to transmit next.  The sender SHOULD use the delivery

Reviewer comments:
Shared between and
Merging because I don't think this is controversial, and we have two PRs doing the same thing.
This sounds a bit like an extension that just gets forwarded when not understood by a relay? How does APP interact with caches, for example? Can a single QUIC stream have SEGMENT and APP data interleaved? Might be too vague to leave in
The intention was to cache/forward it the same way as media. It's just meant for arbitrary metadata and as a replacement for the current approach of shoving metadata into the media bitstream. For example, in h.264 land it's common to put arbitrary content (non-standardized) in SEI messages. Even if we don't cache/forward the stream, it would still be nice to let the application use unidirectional streams. This basically says prefix the stream with a 2 if it's used by the application instead of Warp. This wouldn't work with relays which is why I had the idea of reusing HEADERS to tell the relay which application streams should be forwarded/cached and how.
Oh and it's missing because there's no encoding yet, but I was envisioning that SEGMENT and APP frames do not specify a length. They would consume the remainder of the stream so there's no way to use them in conjunction.
Is there an MP4 box that we can just use as a part of the layer? That would provide some form of uniformity.
That was my thought too, although it means every container will need some way of specifying application data. MP4 atoms require a length up-front so they would be limited to messages instead of unbounded streams.
the RTCP APP message never turned out to be as useful in RTP as people thought it would be ... that said, fan of being able to send whatever data in the payload. If it happens to be a protobuf app message instead of media, seem like MOQ shoudl not care, it should just deliver the bytes with not much care if they are audio, vidie, haptics, or an app custom media
We need to define the difference of APP message and Media data in terms of what is inside and how it is interacted with caching and dropping. Otherwise there is a risk that the APP message becomes the container for “whatever does not fit in the current RFC“ which renders the standard useless.

Revised text:
(appendix.encoding). A segment MAY depend on any number of other segments.  The encoder  <ins> MUST indicate these dependecies on the wire via the SEGMENT header (segment). </ins> The sender SHOULD NOT use this list of dependencies to determine which segment to transmit next.  The sender SHOULD use the delivery
Original text:
QUIC and HTTP/3 frames, but called messages to avoid the media terminology. <del> Each stream MUST start with a "HEADERS" message (headers) to indicates how the stream should be transmitted. </del> Messages SHOULD be sent over the same stream if ordering is desired. For example, "PAUSE" and "PLAY" messages SHOULD be sent on the same stream to avoid a race.

Reviewer comments:
Shared between and
Merging because I don't think this is controversial, and we have two PRs doing the same thing.
This sounds a bit like an extension that just gets forwarded when not understood by a relay? How does APP interact with caches, for example? Can a single QUIC stream have SEGMENT and APP data interleaved? Might be too vague to leave in
The intention was to cache/forward it the same way as media. It's just meant for arbitrary metadata and as a replacement for the current approach of shoving metadata into the media bitstream. For example, in h.264 land it's common to put arbitrary content (non-standardized) in SEI messages. Even if we don't cache/forward the stream, it would still be nice to let the application use unidirectional streams. This basically says prefix the stream with a 2 if it's used by the application instead of Warp. This wouldn't work with relays which is why I had the idea of reusing HEADERS to tell the relay which application streams should be forwarded/cached and how.
Oh and it's missing because there's no encoding yet, but I was envisioning that SEGMENT and APP frames do not specify a length. They would consume the remainder of the stream so there's no way to use them in conjunction.
Is there an MP4 box that we can just use as a part of the layer? That would provide some form of uniformity.
That was my thought too, although it means every container will need some way of specifying application data. MP4 atoms require a length up-front so they would be limited to messages instead of unbounded streams.
the RTCP APP message never turned out to be as useful in RTP as people thought it would be ... that said, fan of being able to send whatever data in the payload. If it happens to be a protobuf app message instead of media, seem like MOQ shoudl not care, it should just deliver the bytes with not much care if they are audio, vidie, haptics, or an app custom media
We need to define the difference of APP message and Media data in terms of what is inside and how it is interacted with caching and dropping. Otherwise there is a risk that the APP message becomes the container for “whatever does not fit in the current RFC“ which renders the standard useless.

Revised text:
QUIC and HTTP/3 frames, but called messages to avoid the media terminology.  Messages SHOULD be sent over the same stream if ordering is desired. For example, "PAUSE" and "PLAY" messages SHOULD be sent on the same stream to avoid a race.