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