Clean up Malformed Track definition per MOQT draft-14.
Due to the new subgroup encoding, those streams cannot have declining object IDs.
PiperOrigin-RevId: 804981345
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc
index 7005f76..1e73ae9 100644
--- a/quiche/quic/moqt/moqt_session.cc
+++ b/quiche/quic/moqt/moqt_session.cc
@@ -1729,10 +1729,6 @@
session_->OnMalformedTrack(track);
return;
}
- if (message.object_id < next_object_id_) {
- session_->OnMalformedTrack(track);
- return;
- }
if (end_of_message) {
next_object_id_ = message.object_id + 1;
if (message.object_status == MoqtObjectStatus::kEndOfTrack ||
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc
index 6d57458..967bc0a 100644
--- a/quiche/quic/moqt/moqt_session_test.cc
+++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -3709,37 +3709,6 @@
EXPECT_EQ(MoqtSessionPeer::remote_track(&session_, 0), nullptr);
}
-TEST_F(MoqtSessionTest, SubgroupStreamOutOfOrder) {
- MockSubscribeRemoteTrackVisitor remote_track_visitor;
- MoqtSessionPeer::CreateRemoteTrack(&session_, DefaultSubscribe(),
- /*track_alias=*/2, &remote_track_visitor);
- webtransport::test::MockStream control_stream;
- std::unique_ptr<MoqtControlParserVisitor> stream_input =
- MoqtSessionPeer::CreateControlStream(&session_, &control_stream);
- std::unique_ptr<MoqtDataParserVisitor> object_stream =
- MoqtSessionPeer::CreateIncomingDataStream(
- &session_, &mock_stream_,
- MoqtDataStreamType::Subgroup(/*subgroup_id=*/0, /*first_object_id=*/1,
- /*no_extension_headers=*/true));
- object_stream->OnObjectMessage(
- MoqtObject(/*track_alias=*/2, /*group_id=*/0, /*object_id=*/1,
- /*publisher_priority=*/0x80, /*extension_headers=*/"",
- MoqtObjectStatus::kNormal, /*subgroup_id=*/0,
- /*payload_length=*/3),
- "foo", true);
- EXPECT_CALL(mock_session_, GetStreamById(_))
- .WillRepeatedly(Return(&control_stream));
- EXPECT_CALL(control_stream,
- Writev(ControlMessageOfType(MoqtMessageType::kUnsubscribe), _));
- EXPECT_CALL(remote_track_visitor, OnMalformedTrack);
- object_stream->OnObjectMessage(
- MoqtObject(/*track_alias=*/2, /*group_id=*/0, /*object_id=*/1,
- /*publisher_priority=*/0x80, /*extension_headers=*/"",
- MoqtObjectStatus::kNormal, /*subgroup_id=*/0,
- /*payload_length=*/3),
- "bar", true);
-}
-
TEST_F(MoqtSessionTest, SubgroupStreamObjectAfterGroupEnd) {
MockSubscribeRemoteTrackVisitor remote_track_visitor;
MoqtSessionPeer::CreateRemoteTrack(&session_, DefaultSubscribe(),
diff --git a/quiche/quic/moqt/moqt_track.cc b/quiche/quic/moqt/moqt_track.cc
index dcb26f0..b61db08 100644
--- a/quiche/quic/moqt/moqt_track.cc
+++ b/quiche/quic/moqt/moqt_track.cc
@@ -169,11 +169,20 @@
bool UpstreamFetch::LocationIsValid(Location location, MoqtObjectStatus status,
bool end_of_message) {
- if (no_more_objects_) {
- return false;
+ if (end_of_track_.has_value()) {
+ // Cannot exceed or change end_of_track_.
+ if (location > end_of_track_) {
+ return false;
+ }
+ if (status == MoqtObjectStatus::kEndOfTrack && location != *end_of_track_) {
+ return false;
+ }
}
if (end_of_message && status == MoqtObjectStatus::kEndOfTrack) {
- no_more_objects_ = true;
+ if (highest_location_.has_value() && location < *highest_location_) {
+ return false;
+ }
+ end_of_track_ = location;
}
bool last_group_is_finished = last_group_is_finished_;
last_group_is_finished_ =
@@ -181,6 +190,11 @@
std::optional<Location> last_location = last_location_;
if (end_of_message) {
last_location_ = location;
+ if (!highest_location_.has_value()) {
+ highest_location_ = location;
+ } else {
+ highest_location_ = std::max(*highest_location_, location);
+ }
}
if (!last_location.has_value()) {
return true;
diff --git a/quiche/quic/moqt/moqt_track.h b/quiche/quic/moqt/moqt_track.h
index eeb3412..3568de6 100644
--- a/quiche/quic/moqt/moqt_track.h
+++ b/quiche/quic/moqt/moqt_track.h
@@ -350,9 +350,12 @@
private:
std::optional<MoqtDeliveryOrder> group_order_; // nullopt if not yet known.
+ // The last object received on the stream.
std::optional<Location> last_location_;
+ // The highest location received on the stream.
+ std::optional<Location> highest_location_;
bool last_group_is_finished_ = false; // Received EndOfGroup.
- bool no_more_objects_ = false; // Received EndOfTrack
+ std::optional<Location> end_of_track_; // Received EndOfTrack
quiche::QuicheWeakPtr<UpstreamFetchTask> task_;
diff --git a/quiche/quic/moqt/moqt_track_test.cc b/quiche/quic/moqt/moqt_track_test.cc
index 8ed99f9..4d8901d 100644
--- a/quiche/quic/moqt/moqt_track_test.cc
+++ b/quiche/quic/moqt/moqt_track_test.cc
@@ -281,6 +281,22 @@
fetch_.LocationIsValid(Location(2, 1), MoqtObjectStatus::kNormal, true));
}
+TEST_F(UpstreamFetchTest, LocationIsValidTwoEndsOfTrack) {
+ EXPECT_TRUE(fetch_.LocationIsValid(Location(1, 1),
+ MoqtObjectStatus::kEndOfTrack, true));
+ EXPECT_FALSE(fetch_.LocationIsValid(Location(1, 2),
+ MoqtObjectStatus::kEndOfTrack, true));
+}
+
+TEST_F(UpstreamFetchTest, LocationIsValidEndOfTrackTooLow) {
+ EXPECT_TRUE(
+ fetch_.LocationIsValid(Location(1, 2), MoqtObjectStatus::kNormal, true));
+ EXPECT_TRUE(
+ fetch_.LocationIsValid(Location(3, 0), MoqtObjectStatus::kNormal, true));
+ EXPECT_FALSE(fetch_.LocationIsValid(Location(2, 1),
+ MoqtObjectStatus::kEndOfTrack, true));
+}
+
} // namespace test
} // namespace moqt