Implement draft-05 SUBSCRIBE/SUBSCRIBE_UPDATE

PiperOrigin-RevId: 654109878
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc
index 8439ebf..aff5753 100644
--- a/quiche/quic/moqt/moqt_framer.cc
+++ b/quiche/quic/moqt/moqt_framer.cc
@@ -303,7 +303,9 @@
           WireVarInt62(message.subscribe_id), WireVarInt62(message.track_alias),
           WireStringWithVarInt62Length(message.track_namespace),
           WireStringWithVarInt62Length(message.track_name),
-          WireVarInt62(filter_type), WireVarInt62(string_params.size()),
+          WireUint8(message.subscriber_priority),
+          WireDeliveryOrder(message.group_order), WireVarInt62(filter_type),
+          WireVarInt62(string_params.size()),
           WireSpan<WireStringParameter>(string_params));
     case MoqtFilterType::kAbsoluteStart:
       return Serialize(
@@ -311,7 +313,9 @@
           WireVarInt62(message.subscribe_id), WireVarInt62(message.track_alias),
           WireStringWithVarInt62Length(message.track_namespace),
           WireStringWithVarInt62Length(message.track_name),
-          WireVarInt62(filter_type), WireVarInt62(*message.start_group),
+          WireUint8(message.subscriber_priority),
+          WireDeliveryOrder(message.group_order), WireVarInt62(filter_type),
+          WireVarInt62(*message.start_group),
           WireVarInt62(*message.start_object),
           WireVarInt62(string_params.size()),
           WireSpan<WireStringParameter>(string_params));
@@ -321,7 +325,9 @@
           WireVarInt62(message.subscribe_id), WireVarInt62(message.track_alias),
           WireStringWithVarInt62Length(message.track_namespace),
           WireStringWithVarInt62Length(message.track_name),
-          WireVarInt62(filter_type), WireVarInt62(*message.start_group),
+          WireUint8(message.subscriber_priority),
+          WireDeliveryOrder(message.group_order), WireVarInt62(filter_type),
+          WireVarInt62(*message.start_group),
           WireVarInt62(*message.start_object), WireVarInt62(*message.end_group),
           WireVarInt62(message.end_object.has_value() ? *message.end_object + 1
                                                       : 0),
@@ -400,7 +406,8 @@
       WireVarInt62(MoqtMessageType::kSubscribeUpdate),
       WireVarInt62(message.subscribe_id), WireVarInt62(message.start_group),
       WireVarInt62(message.start_object), WireVarInt62(end_group),
-      WireVarInt62(end_object), WireSpan<WireStringParameter>(string_params));
+      WireVarInt62(end_object), WireUint8(message.subscriber_priority),
+      WireSpan<WireStringParameter>(string_params));
 }
 
 quiche::QuicheBuffer MoqtFramer::SerializeAnnounce(
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc
index 6be927d..2a9ef38 100644
--- a/quiche/quic/moqt/moqt_framer_test.cc
+++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -294,6 +294,8 @@
               /*track_alias=*/4,
               /*track_namespace=*/"foo",
               /*track_name=*/"abcd",
+              /*subscriber_priority=*/0x20,
+              /*group_order=*/std::nullopt,
               start_group,
               start_object,
               end_group,
@@ -324,7 +326,7 @@
           }
           buffer = framer_.SerializeSubscribe(subscribe);
           // Go to the filter type.
-          const uint8_t* read = BufferAtOffset(buffer, 12);
+          const uint8_t* read = BufferAtOffset(buffer, 14);
           EXPECT_EQ(static_cast<MoqtFilterType>(*read), expected_filter_type);
           EXPECT_GT(buffer.size(), 0);
           if (expected_filter_type == MoqtFilterType::kAbsoluteRange &&
@@ -344,6 +346,8 @@
       /*track_alias=*/4,
       /*track_namespace=*/"foo",
       /*track_name=*/"abcd",
+      /*subscriber_priority=*/0x20,
+      /*group_order=*/std::nullopt,
       /*start_group=*/std::optional<uint64_t>(4),
       /*start_object=*/std::optional<uint64_t>(3),
       /*end_group=*/std::optional<uint64_t>(3),
@@ -367,6 +371,8 @@
       /*track_alias=*/4,
       /*track_namespace=*/"foo",
       /*track_name=*/"abcd",
+      /*subscriber_priority=*/0x20,
+      /*group_order=*/std::nullopt,
       /*start_group=*/std::nullopt,
       /*start_object=*/std::optional<uint64_t>(3),
       /*end_group=*/std::nullopt,
@@ -386,6 +392,7 @@
       /*start_object=*/3,
       /*end_group=*/4,
       /*end_object=*/std::nullopt,
+      /*subscriber_priority=*/0xaa,
       /*authorization_info=*/"bar",
   };
   quiche::QuicheBuffer buffer;
@@ -404,6 +411,7 @@
       /*start_object=*/3,
       /*end_group=*/4,
       /*end_object=*/6,
+      /*subscriber_priority=*/0xaa,
       /*authorization_info=*/"bar",
   };
   quiche::QuicheBuffer buffer;
@@ -422,6 +430,7 @@
       /*start_object=*/3,
       /*end_group=*/std::nullopt,
       /*end_object=*/6,
+      /*subscriber_priority=*/0xaa,
       /*authorization_info=*/"bar",
   };
   quiche::QuicheBuffer buffer;
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h
index 584fcf9..1d9f312 100644
--- a/quiche/quic/moqt/moqt_messages.h
+++ b/quiche/quic/moqt/moqt_messages.h
@@ -243,6 +243,8 @@
   uint64_t track_alias;
   std::string track_namespace;
   std::string track_name;
+  MoqtPriority subscriber_priority;
+  std::optional<MoqtDeliveryOrder> group_order;
   // The combinations of these that have values indicate the filter type.
   // SG: Start Group; SO: Start Object; EG: End Group; EO: End Object;
   // (none): KLatestObject
@@ -312,6 +314,7 @@
   uint64_t start_object;
   std::optional<uint64_t> end_group;
   std::optional<uint64_t> end_object;
+  MoqtPriority subscriber_priority;
   std::optional<std::string> authorization_info;
 };
 
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc
index c5a51d7..c9f2ed0 100644
--- a/quiche/quic/moqt/moqt_parser.cc
+++ b/quiche/quic/moqt/moqt_parser.cc
@@ -21,6 +21,27 @@
 
 namespace moqt {
 
+namespace {
+
+bool ParseDeliveryOrder(uint8_t raw_value,
+                        std::optional<MoqtDeliveryOrder>& output) {
+  switch (raw_value) {
+    case 0x00:
+      output = std::nullopt;
+      return true;
+    case 0x01:
+      output = MoqtDeliveryOrder::kAscending;
+      return true;
+    case 0x02:
+      output = MoqtDeliveryOrder::kDescending;
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
 // The buffering philosophy is complicated, to minimize copying. Here is an
 // overview:
 // If the entire message body is present (except for OBJECT payload), it is
@@ -402,11 +423,17 @@
 size_t MoqtParser::ProcessSubscribe(quic::QuicDataReader& reader) {
   MoqtSubscribe subscribe_request;
   uint64_t filter, group, object;
+  uint8_t group_order;
   if (!reader.ReadVarInt62(&subscribe_request.subscribe_id) ||
       !reader.ReadVarInt62(&subscribe_request.track_alias) ||
       !reader.ReadStringVarInt62(subscribe_request.track_namespace) ||
       !reader.ReadStringVarInt62(subscribe_request.track_name) ||
-      !reader.ReadVarInt62(&filter)) {
+      !reader.ReadUInt8(&subscribe_request.subscriber_priority) ||
+      !reader.ReadUInt8(&group_order) || !reader.ReadVarInt62(&filter)) {
+    return 0;
+  }
+  if (!ParseDeliveryOrder(group_order, subscribe_request.group_order)) {
+    ParseError("Invalid group order value in SUBSCRIBE message");
     return 0;
   }
   MoqtFilterType filter_type = static_cast<MoqtFilterType>(filter);
@@ -566,6 +593,7 @@
       !reader.ReadVarInt62(&subscribe_update.start_group) ||
       !reader.ReadVarInt62(&subscribe_update.start_object) ||
       !reader.ReadVarInt62(&end_group) || !reader.ReadVarInt62(&end_object) ||
+      !reader.ReadUInt8(&subscribe_update.subscriber_priority) ||
       !reader.ReadVarInt62(&num_params)) {
     return 0;
   }
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc
index 3cf061a..cf6b2e3 100644
--- a/quiche/quic/moqt/moqt_parser_test.cc
+++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -23,6 +23,9 @@
 
 namespace {
 
+using ::testing::HasSubstr;
+using ::testing::Optional;
+
 inline bool IsObjectMessage(MoqtMessageType type) {
   return (type == MoqtMessageType::kObjectStream ||
           type == MoqtMessageType::kObjectDatagram ||
@@ -625,6 +628,7 @@
   char subscribe[] = {
       0x03, 0x01, 0x02, 0x03, 0x66, 0x6f, 0x6f,  // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,              // track_name = "abcd"
+      0x20, 0x02,                                // priority = 0x20 descending
       0x02,                                      // filter_type = kLatestObject
       0x02,                                      // two params
       0x02, 0x03, 0x62, 0x61, 0x72,              // authorization_info = "bar"
@@ -632,8 +636,7 @@
   };
   parser.ProcessData(absl::string_view(subscribe, sizeof(subscribe)), false);
   EXPECT_EQ(visitor_.messages_received_, 0);
-  EXPECT_TRUE(visitor_.parsing_error_.has_value());
-  EXPECT_EQ(*visitor_.parsing_error_,
+  EXPECT_EQ(visitor_.parsing_error_,
             "AUTHORIZATION_INFO parameter appears twice in SUBSCRIBE");
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
@@ -642,6 +645,7 @@
   MoqtParser parser(kWebTrans, visitor_);
   char subscribe_update[] = {
       0x02, 0x02, 0x03, 0x01, 0x05, 0x06,  // start and end sequences
+      0xaa,                                // priority = 0xaa
       0x02,                                // 2 parameters
       0x02, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
       0x02, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
@@ -649,8 +653,7 @@
   parser.ProcessData(
       absl::string_view(subscribe_update, sizeof(subscribe_update)), false);
   EXPECT_EQ(visitor_.messages_received_, 0);
-  EXPECT_TRUE(visitor_.parsing_error_.has_value());
-  EXPECT_EQ(*visitor_.parsing_error_,
+  EXPECT_EQ(visitor_.parsing_error_,
             "AUTHORIZATION_INFO parameter appears twice in SUBSCRIBE_UPDATE");
   EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
 }
@@ -769,13 +772,14 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x01,                          // filter_type = kLatestGroup
       0x01,                          // 1 parameter
       0x02, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
   };
   parser.ProcessData(absl::string_view(subscribe, sizeof(subscribe)), false);
   EXPECT_EQ(visitor_.messages_received_, 1);
-  EXPECT_TRUE(visitor_.last_message_.has_value());
+  ASSERT_TRUE(visitor_.last_message_.has_value());
   MoqtSubscribe message =
       std::get<MoqtSubscribe>(visitor_.last_message_.value());
   EXPECT_FALSE(message.start_group.has_value());
@@ -790,6 +794,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x02,                          // filter_type = kLatestObject
       0x01,                          // 1 parameter
       0x02, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
@@ -805,12 +810,29 @@
   EXPECT_FALSE(message.end_object.has_value());
 }
 
+TEST_F(MoqtMessageSpecificTest, InvalidDeliveryOrder) {
+  MoqtParser parser(kRawQuic, visitor_);
+  char subscribe[] = {
+      0x03, 0x01, 0x02,              // id and alias
+      0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
+      0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x08,                    // priority = 0x20 ???
+      0x01,                          // filter_type = kLatestGroup
+      0x01,                          // 1 parameter
+      0x02, 0x03, 0x62, 0x61, 0x72,  // authorization_info = "bar"
+  };
+  parser.ProcessData(absl::string_view(subscribe, sizeof(subscribe)), false);
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_THAT(visitor_.parsing_error_, Optional(HasSubstr("group order")));
+}
+
 TEST_F(MoqtMessageSpecificTest, AbsoluteStart) {
   MoqtParser parser(kRawQuic, visitor_);
   char subscribe[] = {
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x03,                          // filter_type = kAbsoluteStart
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -834,6 +856,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x04,                          // filter_type = kAbsoluteStart
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -859,6 +882,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x04,                          // filter_type = kAbsoluteRange
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -884,6 +908,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x04,                          // filter_type = kAbsoluteRange
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -904,6 +929,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x04,                          // filter_type = kAbsoluteRange
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -919,6 +945,7 @@
   MoqtParser parser(kRawQuic, visitor_);
   char subscribe_update[] = {
       0x02, 0x02, 0x03, 0x01, 0x04, 0x07,  // start and end sequences
+      0x20,                                // priority
       0x00,                                // No parameters
   };
   parser.ProcessData(
@@ -930,6 +957,7 @@
   MoqtParser parser(kRawQuic, visitor_);
   char subscribe_update[] = {
       0x02, 0x02, 0x03, 0x01, 0x03, 0x06,  // start and end sequences
+      0x20,                                // priority
       0x01,                                // 1 parameter
       0x02, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
   };
@@ -946,6 +974,7 @@
       0x03, 0x01, 0x02,              // id and alias
       0x03, 0x66, 0x6f, 0x6f,        // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63, 0x64,  // track_name = "abcd"
+      0x20, 0x02,                    // priority = 0x20 descending
       0x04,                          // filter_type = kAbsoluteRange
       0x04,                          // start_group = 4
       0x01,                          // start_object = 1
@@ -978,6 +1007,7 @@
   MoqtParser parser(kRawQuic, visitor_);
   char subscribe_update[] = {
       0x02, 0x02, 0x03, 0x02, 0x00, 0x01,  // start and end sequences
+      0x20,                                // priority
       0x01,                                // 1 parameter
       0x02, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
   };
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc
index 58ff443..bde0055 100644
--- a/quiche/quic/moqt/moqt_session.cc
+++ b/quiche/quic/moqt/moqt_session.cc
@@ -48,6 +48,8 @@
 
 using ::quic::Perspective;
 
+constexpr MoqtPriority kDefaultSubscriberPriority = 0x80;
+
 bool PublisherHasData(const MoqtTrackPublisher& publisher) {
   absl::StatusOr<MoqtTrackStatusCode> status = publisher.GetTrackStatus();
   return status.ok() && DoesTrackStatusImplyHavingData(*status);
@@ -246,6 +248,8 @@
   MoqtSubscribe message;
   message.track_namespace = track_namespace;
   message.track_name = name;
+  message.subscriber_priority = kDefaultSubscriberPriority;
+  message.group_order = std::nullopt;
   message.start_group = start_group;
   message.start_object = start_object;
   message.end_group = std::nullopt;
@@ -269,6 +273,8 @@
   MoqtSubscribe message;
   message.track_namespace = track_namespace;
   message.track_name = name;
+  message.subscriber_priority = kDefaultSubscriberPriority;
+  message.group_order = std::nullopt;
   message.start_group = start_group;
   message.start_object = start_object;
   message.end_group = end_group;
@@ -296,6 +302,8 @@
   MoqtSubscribe message;
   message.track_namespace = track_namespace;
   message.track_name = name;
+  message.subscriber_priority = kDefaultSubscriberPriority;
+  message.group_order = std::nullopt;
   message.start_group = start_group;
   message.start_object = start_object;
   message.end_group = end_group;
@@ -313,6 +321,8 @@
   MoqtSubscribe message;
   message.track_namespace = track_namespace;
   message.track_name = name;
+  message.subscriber_priority = kDefaultSubscriberPriority;
+  message.group_order = std::nullopt;
   message.start_group = std::nullopt;
   message.start_object = std::nullopt;
   message.end_group = std::nullopt;
@@ -330,6 +340,8 @@
   MoqtSubscribe message;
   message.track_namespace = track_namespace;
   message.track_name = name;
+  message.subscriber_priority = kDefaultSubscriberPriority;
+  message.group_order = std::nullopt;
   // First object of current group.
   message.start_group = std::nullopt;
   message.start_object = 0;
@@ -716,7 +728,7 @@
                                                ? *message.end_object
                                                : UINT64_MAX);
   }
-  it->second->Update(start, end);
+  it->second->Update(start, end, message.subscriber_priority);
 }
 
 void MoqtSession::ControlStream::OnAnnounceMessage(
@@ -850,7 +862,9 @@
       session_(session),
       track_publisher_(track_publisher),
       track_alias_(subscribe.track_alias),
-      window_(SubscribeMessageToWindow(subscribe, *track_publisher)) {
+      window_(SubscribeMessageToWindow(subscribe, *track_publisher)),
+      subscriber_priority_(subscribe.subscriber_priority),
+      subscriber_delivery_order_(subscribe.group_order) {
   track_publisher->AddObjectListener(this);
   QUIC_DLOG(INFO) << ENDPOINT << "Created subscription for "
                   << subscribe.track_namespace << ":" << subscribe.track_name;
@@ -874,8 +888,10 @@
 }
 
 void MoqtSession::PublishedSubscription::Update(
-    FullSequence start, std::optional<FullSequence> end) {
+    FullSequence start, std::optional<FullSequence> end,
+    MoqtPriority subscriber_priority) {
   window_.UpdateStartEnd(start, end);
+  subscriber_priority_ = subscriber_priority;
   // TODO: reset streams that are no longer in-window.
   // TODO: send SUBSCRIBE_DONE if required.
   // TODO: send an error for invalid updates now that it's a part of draft-05.
diff --git a/quiche/quic/moqt/moqt_session.h b/quiche/quic/moqt/moqt_session.h
index 52af69d..4e6d172 100644
--- a/quiche/quic/moqt/moqt_session.h
+++ b/quiche/quic/moqt/moqt_session.h
@@ -19,6 +19,7 @@
 #include "quiche/quic/moqt/moqt_framer.h"
 #include "quiche/quic/moqt/moqt_messages.h"
 #include "quiche/quic/moqt/moqt_parser.h"
+#include "quiche/quic/moqt/moqt_priority.h"
 #include "quiche/quic/moqt/moqt_publisher.h"
 #include "quiche/quic/moqt/moqt_subscribe_windows.h"
 #include "quiche/quic/moqt/moqt_track.h"
@@ -301,8 +302,9 @@
     // conceptually simpler, as backpressure is less of a concern.
     void Backfill();
 
-    // Updates the window of the subscription in question.
-    void Update(FullSequence start, std::optional<FullSequence> end);
+    // Updates the window and other properties of the subscription in question.
+    void Update(FullSequence start, std::optional<FullSequence> end,
+                MoqtPriority subscriber_priority);
     // Checks if the specified sequence is within the window of this
     // subscription.
     bool InWindow(FullSequence sequence) { return window_.InWindow(sequence); }
@@ -328,6 +330,8 @@
     std::shared_ptr<MoqtTrackPublisher> track_publisher_;
     uint64_t track_alias_;
     SubscribeWindow window_;
+    MoqtPriority subscriber_priority_;
+    std::optional<MoqtDeliveryOrder> subscriber_delivery_order_;
     // Largest sequence number ever sent via this subscription.
     std::optional<FullSequence> largest_sent_;
     // Should be almost always accessed via `stream_map()`.
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc
index 9a1bf9c..22ab0d0 100644
--- a/quiche/quic/moqt/moqt_session_test.cc
+++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -304,6 +304,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/"foo",
       /*track_name=*/"bar",
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -438,6 +440,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/"foo",
       /*track_name=*/"bar",
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -641,6 +645,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/ftn.track_namespace,
       /*track_name=*/ftn.track_name,
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -698,6 +704,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/ftn.track_namespace,
       /*track_name=*/ftn.track_name,
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -758,6 +766,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/ftn.track_namespace,
       /*track_name=*/ftn.track_name,
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -809,6 +819,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/ftn.track_namespace,
       /*track_name=*/ftn.track_name,
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
@@ -1220,6 +1232,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/"foo",
       /*track_name=*/"bar",
+      /*subscriber_priority=*/0x80,
+      /*group_order=*/std::nullopt,
       /*start_group=*/0,
       /*start_object=*/0,
       /*end_group=*/std::nullopt,
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h
index 44a6405..91219f6 100644
--- a/quiche/quic/moqt/test_tools/moqt_test_message.h
+++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -422,6 +422,14 @@
       QUIC_LOG(INFO) << "SUBSCRIBE track name mismatch";
       return false;
     }
+    if (cast.subscriber_priority != subscribe_.subscriber_priority) {
+      QUIC_LOG(INFO) << "SUBSCRIBE subscriber priority mismatch";
+      return false;
+    }
+    if (cast.group_order != subscribe_.group_order) {
+      QUIC_LOG(INFO) << "SUBSCRIBE group order mismatch";
+      return false;
+    }
     if (cast.start_group != subscribe_.start_group) {
       QUIC_LOG(INFO) << "SUBSCRIBE start group mismatch";
       return false;
@@ -445,20 +453,24 @@
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv---v----vvvvvv---"); }
+  void ExpandVarints() override {
+    ExpandVarintsImpl("vvvv---v------vvvvvv---");
+  }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(subscribe_);
   }
 
  private:
-  uint8_t raw_packet_[21] = {
+  uint8_t raw_packet_[23] = {
       0x03, 0x01,
       0x02,  // id and alias
       0x03, 0x66, 0x6f,
       0x6f,  // track_namespace = "foo"
       0x04, 0x61, 0x62, 0x63,
       0x64,  // track_name = "abcd"
+      0x20,  // subscriber priority = 0x20
+      0x02,  // group order = descending
       0x03,  // Filter type: Absolute Start
       0x04,  // start_group = 4 (relative previous)
       0x01,  // start_object = 1 (absolute)
@@ -473,6 +485,8 @@
       /*track_alias=*/2,
       /*track_namespace=*/"foo",
       /*track_name=*/"abcd",
+      /*subscriber_priority=*/0x20,
+      /*group_order=*/MoqtDeliveryOrder::kDescending,
       /*start_group=*/4,
       /*start_object=*/1,
       /*end_group=*/std::nullopt,
@@ -701,6 +715,10 @@
       QUIC_LOG(INFO) << "SUBSCRIBE_UPDATE end group mismatch";
       return false;
     }
+    if (cast.subscriber_priority != subscribe_update_.subscriber_priority) {
+      QUIC_LOG(INFO) << "SUBSCRIBE_UPDATE subscriber priority mismatch";
+      return false;
+    }
     if (cast.authorization_info != subscribe_update_.authorization_info) {
       QUIC_LOG(INFO) << "SUBSCRIBE_UPDATE authorization info mismatch";
       return false;
@@ -708,15 +726,16 @@
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvvvvvvv---"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvvvvv-vvv---"); }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(subscribe_update_);
   }
 
  private:
-  uint8_t raw_packet_[12] = {
+  uint8_t raw_packet_[13] = {
       0x02, 0x02, 0x03, 0x01, 0x05, 0x06,  // start and end sequences
+      0xaa,                                // subscriber_priority
       0x01,                                // 1 parameter
       0x02, 0x03, 0x62, 0x61, 0x72,        // authorization_info = "bar"
   };
@@ -727,6 +746,7 @@
       /*start_object=*/1,
       /*end_group=*/4,
       /*end_object=*/5,
+      /*subscriber_priority=*/0xaa,
       /*authorization_info=*/"bar",
   };
 };