Split SUBSCRIBE_NAMESPACE and SUBSCRIBE_TRACKS. Remove SubscribeNamespaceOption, maintain SUBSCRIBE_NAMESPACE functionality, and provide stubs and parser/framer support for SUBSCRIBE_TRACKS. Part of draft-18 migration. PiperOrigin-RevId: 949714141
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc index d446967..81bd37a 100644 --- a/quiche/quic/moqt/moqt_framer.cc +++ b/quiche/quic/moqt/moqt_framer.cc
@@ -612,7 +612,14 @@ return SerializeControlMessage( MoqtMessageType::kSubscribeNamespace, WireMoqVarInt(message.request_id), WireTrackNamespace(message.track_namespace_prefix), - WireMoqVarInt(message.subscribe_options), + WireKeyValuePairList(message.parameters.ToKeyValuePairList())); +} + +quiche::QuicheBuffer MoqtFramer::SerializeSubscribeTracks( + const MoqtSubscribeTracks& message) { + return SerializeControlMessage( + MoqtMessageType::kSubscribeTracks, WireMoqVarInt(message.request_id), + WireTrackNamespace(message.track_namespace_prefix), WireKeyValuePairList(message.parameters.ToKeyValuePairList())); }
diff --git a/quiche/quic/moqt/moqt_framer.h b/quiche/quic/moqt/moqt_framer.h index 0d8df94..e449986 100644 --- a/quiche/quic/moqt/moqt_framer.h +++ b/quiche/quic/moqt/moqt_framer.h
@@ -68,6 +68,8 @@ quiche::QuicheBuffer SerializeGoAway(const MoqtGoAway& message); quiche::QuicheBuffer SerializeSubscribeNamespace( const MoqtSubscribeNamespace& message); + quiche::QuicheBuffer SerializeSubscribeTracks( + const MoqtSubscribeTracks& message); quiche::QuicheBuffer SerializeMaxRequestId(const MoqtMaxRequestId& message); quiche::QuicheBuffer SerializeFetch(const MoqtFetch& message); quiche::QuicheBuffer SerializeFetchCancel(const MoqtFetchCancel& message);
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc index 2000e69..3a6992a 100644 --- a/quiche/quic/moqt/moqt_framer_test.cc +++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -55,6 +55,7 @@ MoqtMessageType::kTrackStatus, MoqtMessageType::kGoAway, MoqtMessageType::kSubscribeNamespace, + MoqtMessageType::kSubscribeTracks, MoqtMessageType::kMaxRequestId, MoqtMessageType::kFetch, MoqtMessageType::kFetchCancel, @@ -187,6 +188,10 @@ auto data = std::get<MoqtSubscribeNamespace>(structured_data); return framer_.SerializeSubscribeNamespace(data); } + case moqt::MoqtMessageType::kSubscribeTracks: { + auto data = std::get<MoqtSubscribeTracks>(structured_data); + return framer_.SerializeSubscribeTracks(data); + } case moqt::MoqtMessageType::kMaxRequestId: { auto data = std::get<MoqtMaxRequestId>(structured_data); return framer_.SerializeMaxRequestId(data);
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc index 3512ecb..d018263 100644 --- a/quiche/quic/moqt/moqt_messages.cc +++ b/quiche/quic/moqt/moqt_messages.cc
@@ -103,6 +103,8 @@ return "GOAWAY"; case MoqtMessageType::kSubscribeNamespace: return "SUBSCRIBE_NAMESPACE"; + case MoqtMessageType::kSubscribeTracks: + return "SUBSCRIBE_TRACKS"; case MoqtMessageType::kMaxRequestId: return "MAX_REQUEST_ID"; case MoqtMessageType::kPublish:
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h index b05a392..98fa82e 100644 --- a/quiche/quic/moqt/moqt_messages.h +++ b/quiche/quic/moqt/moqt_messages.h
@@ -213,13 +213,14 @@ kTrackStatus = 0x0d, kNamespaceDone = 0x0e, kGoAway = 0x10, - kSubscribeNamespace = 0x11, kMaxRequestId = 0x15, kFetch = 0x16, kFetchCancel = 0x17, kFetchOk = 0x18, kRequestsBlocked = 0x1a, kPublish = 0x1d, + kSubscribeNamespace = 0x50, + kSubscribeTracks = 0x51, kSetup = 0x2f00, // QUICHE-specific extensions. @@ -432,7 +433,12 @@ struct QUICHE_EXPORT MoqtSubscribeNamespace { uint64_t request_id; TrackNamespace track_namespace_prefix; - SubscribeNamespaceOption subscribe_options; + MessageParameters parameters; +}; + +struct QUICHE_EXPORT MoqtSubscribeTracks { + uint64_t request_id; + TrackNamespace track_namespace_prefix; MessageParameters parameters; };
diff --git a/quiche/quic/moqt/moqt_namespace_stream.cc b/quiche/quic/moqt/moqt_namespace_stream.cc index 4d32218..c561373 100644 --- a/quiche/quic/moqt/moqt_namespace_stream.cc +++ b/quiche/quic/moqt/moqt_namespace_stream.cc
@@ -277,9 +277,8 @@ } add_callback_ = nullptr; QUICHE_DCHECK(task_ == nullptr); - task_ = - application_(message.track_namespace_prefix, message.subscribe_options, - message.parameters, ResponseCallback(request_id_)); + task_ = application_(message.track_namespace_prefix, message.parameters, + ResponseCallback(request_id_)); if (task_ != nullptr) { task_->SetObjectsAvailableCallback([this]() { ProcessNamespaces(); }); }
diff --git a/quiche/quic/moqt/moqt_namespace_stream_test.cc b/quiche/quic/moqt/moqt_namespace_stream_test.cc index c38258f..fe14190 100644 --- a/quiche/quic/moqt/moqt_namespace_stream_test.cc +++ b/quiche/quic/moqt/moqt_namespace_stream_test.cc
@@ -318,8 +318,7 @@ testing::MockFunction<bool(const TrackNamespace&)> add_callback_; testing::MockFunction<void(const TrackNamespace&)> remove_callback_; testing::MockFunction<std::unique_ptr<MoqtNamespaceTask>( - const TrackNamespace&, SubscribeNamespaceOption, const MessageParameters&, - MoqtResponseCallback)> + const TrackNamespace&, const MessageParameters&, MoqtResponseCallback)> mock_application_; MoqtIncomingSubscribeNamespaceCallback application_callback_; MoqtSubscribeNamespaceResponseStream stream_; @@ -329,7 +328,6 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; ObjectsAvailableCallback callback; @@ -338,8 +336,7 @@ ok.parameters.expires = quic::QuicTimeDelta::FromSeconds(60); EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(ok.parameters); auto task = @@ -400,7 +397,6 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; ObjectsAvailableCallback callback; @@ -409,8 +405,7 @@ ok.parameters.expires = quic::QuicTimeDelta::FromSeconds(60); EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(ok.parameters); auto task = @@ -431,13 +426,11 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MoqtRequestErrorInfo{ RequestErrorCode::kInternalError, @@ -453,14 +446,12 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; MockNamespaceTask* task_ptr = nullptr; EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MessageParameters()); auto task = @@ -495,14 +486,12 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; MockNamespaceTask* task_ptr = nullptr; EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MessageParameters()); auto task = @@ -538,7 +527,6 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo", "bar", "baz"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; // The namespace tree already has a subscriber for a prefix of "foo". @@ -553,15 +541,13 @@ MoqtSubscribeNamespace message = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); MoqtRequestOk ok(kRequestId); EXPECT_CALL(mock_stream_, Writev(SerializedControlMessage(ok), _)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MessageParameters()); return std::make_unique<MockNamespaceTask>( @@ -574,7 +560,6 @@ MoqtSubscribeNamespace message2 = { kRequestId + 2, TrackNamespace({"bar"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; ReceiveControlMessage(message2); @@ -585,15 +570,13 @@ MoqtSubscribeNamespace message1 = { kRequestId, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; EXPECT_CALL(add_callback_, Call).WillOnce(Return(true)); MoqtRequestOk ok1(kRequestId); EXPECT_CALL(mock_stream_, Writev(SerializedControlMessage(ok1), _)); EXPECT_CALL(mock_application_, Call) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MessageParameters()); return std::make_unique<MockNamespaceTask>( @@ -613,7 +596,6 @@ MoqtSubscribeNamespace message2 = { kRequestId + 2, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; EXPECT_CALL(add_callback_, Call).WillOnce(Return(false)); @@ -626,7 +608,6 @@ MoqtSubscribeNamespace message3 = { kRequestId + 4, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kNamespace, MessageParameters(), }; stream2.CheckStatus(stream2.OnControlMessage(message3));
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc index 5a297a6..3eec6fe 100644 --- a/quiche/quic/moqt/moqt_parser.cc +++ b/quiche/quic/moqt/moqt_parser.cc
@@ -814,26 +814,32 @@ absl::string_view data) const { quic::QuicDataReader reader(data); MoqtSubscribeNamespace subscribe_namespace; - uint64_t raw_option; if (!reader.ReadMoqVarInt(&subscribe_namespace.request_id)) { return absl::InvalidArgumentError("Request ID missing"); } QUICHE_RETURN_IF_ERROR( ReadTrackNamespace(reader, subscribe_namespace.track_namespace_prefix)); - if (!reader.ReadMoqVarInt(&raw_option)) { - return absl::InvalidArgumentError("SUBSCRIBE_NAMESPACE option missing"); - } - if (raw_option > kMaxSubscribeOption) { - return absl::InvalidArgumentError("Invalid SUBSCRIBE_NAMESPACE option"); - } - subscribe_namespace.subscribe_options = - static_cast<SubscribeNamespaceOption>(raw_option); QUICHE_RETURN_IF_ERROR( FillAndValidateMessageParameters(reader, subscribe_namespace.parameters)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return subscribe_namespace; } +absl::StatusOr<MoqtSubscribeTracks> +MoqtControlMessageParser::ProcessSubscribeTracks(absl::string_view data) const { + quic::QuicDataReader reader(data); + MoqtSubscribeTracks subscribe_tracks; + if (!reader.ReadMoqVarInt(&subscribe_tracks.request_id)) { + return absl::InvalidArgumentError("Request ID missing"); + } + QUICHE_RETURN_IF_ERROR( + ReadTrackNamespace(reader, subscribe_tracks.track_namespace_prefix)); + QUICHE_RETURN_IF_ERROR( + FillAndValidateMessageParameters(reader, subscribe_tracks.parameters)); + QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); + return subscribe_tracks; +} + absl::StatusOr<MoqtMaxRequestId> MoqtControlMessageParser::ProcessMaxRequestId( absl::string_view data) const { quic::QuicDataReader reader(data);
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h index eca79f4..f8f685b 100644 --- a/quiche/quic/moqt/moqt_parser.h +++ b/quiche/quic/moqt/moqt_parser.h
@@ -145,6 +145,8 @@ absl::StatusOr<MoqtGoAway> ProcessGoAway(absl::string_view data) const; absl::StatusOr<MoqtSubscribeNamespace> ProcessSubscribeNamespace( absl::string_view data) const; + absl::StatusOr<MoqtSubscribeTracks> ProcessSubscribeTracks( + absl::string_view data) const; absl::StatusOr<MoqtMaxRequestId> ProcessMaxRequestId( absl::string_view data) const; absl::StatusOr<MoqtFetch> ProcessFetch(absl::string_view data) const; @@ -202,6 +204,8 @@ return parse(&MoqtControlMessageParser::ProcessGoAway); case MoqtMessageType::kSubscribeNamespace: return parse(&MoqtControlMessageParser::ProcessSubscribeNamespace); + case MoqtMessageType::kSubscribeTracks: + return parse(&MoqtControlMessageParser::ProcessSubscribeTracks); case MoqtMessageType::kMaxRequestId: return parse(&MoqtControlMessageParser::ProcessMaxRequestId); case MoqtMessageType::kFetch:
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc index 74fcd62..d598c3d 100644 --- a/quiche/quic/moqt/moqt_parser_test.cc +++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -61,6 +61,7 @@ MoqtMessageType::kPublishNamespaceCancel, MoqtMessageType::kGoAway, MoqtMessageType::kSubscribeNamespace, + MoqtMessageType::kSubscribeTracks, MoqtMessageType::kMaxRequestId, MoqtMessageType::kFetch, MoqtMessageType::kFetchCancel,
diff --git a/quiche/quic/moqt/moqt_relay_publisher.h b/quiche/quic/moqt/moqt_relay_publisher.h index ee1fb71..1e1f89f 100644 --- a/quiche/quic/moqt/moqt_relay_publisher.h +++ b/quiche/quic/moqt/moqt_relay_publisher.h
@@ -37,9 +37,16 @@ const FullTrackName& track_name) override; std::unique_ptr<MoqtNamespaceTask> AddNamespaceSubscriber( - const TrackNamespace& track_namespace, - MoqtSessionInterface* absl_nullable session) { - return namespace_publishers_.AddSubscriber(track_namespace, session); + const TrackNamespace& track_namespace) { + return namespace_publishers_.AddNamespaceSubscriber(track_namespace); + } + bool AddTrackSubscriber(const TrackNamespace& track_namespace, + MoqtSessionInterface* absl_nonnull session) { + return namespace_publishers_.AddTrackSubscriber(track_namespace, session); + } + void RemoveTrackSubscriber(const TrackNamespace& track_namespace, + MoqtSessionInterface* absl_nonnull session) { + namespace_publishers_.RemoveTrackSubscriber(track_namespace, session); } // There is a new default upstream session. When there is no other namespace
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc index d03dc55..c9b54f5 100644 --- a/quiche/quic/moqt/moqt_session.cc +++ b/quiche/quic/moqt/moqt_session.cc
@@ -273,8 +273,7 @@ } std::unique_ptr<MoqtNamespaceTask> MoqtSession::SubscribeNamespace( - TrackNamespace& prefix, SubscribeNamespaceOption option, - const MessageParameters& parameters, + TrackNamespace& prefix, const MessageParameters& parameters, MoqtResponseCallback response_callback) { if (received_goaway_ || sent_goaway_) { QUIC_DLOG(INFO) << ENDPOINT @@ -295,18 +294,6 @@ << peer_max_request_id_; return nullptr; } - // Sanitize the option. - switch (option) { - case SubscribeNamespaceOption::kNamespace: - break; - case SubscribeNamespaceOption::kPublish: - // TODO(martinduke): Support PUBLISH. - return nullptr; - case SubscribeNamespaceOption::kBoth: - option = SubscribeNamespaceOption::kNamespace; - break; - } - QUICHE_DCHECK(option == SubscribeNamespaceOption::kNamespace); if (!outgoing_subscribe_namespace_.SubscribeNamespace(prefix)) { std::move(response_callback)(MoqtRequestErrorInfo{ RequestErrorCode::kInternalError, std::nullopt, @@ -339,10 +326,8 @@ pending_bidi_streams_.push_back(std::move(state)); } MoqtSubscribeNamespace message; - message.request_id = next_request_id_; - next_request_id_ += 2; + message.request_id = NextRequestId(); message.track_namespace_prefix = prefix; - message.subscribe_options = SubscribeNamespaceOption::kNamespace; message.parameters = parameters; state_ptr->SendOrBufferMessageOrFatal( framer_.SerializeSubscribeNamespace(message)); @@ -351,6 +336,16 @@ return state_ptr->CreateTask(prefix); } +bool MoqtSession::SubscribeTracks(TrackNamespace& prefix, + const MessageParameters& parameters, + MoqtResponseCallback response_callback) { + return false; +} + +void MoqtSession::UnsubscribeTracks(TrackNamespace& prefix) { + // Do nothing. +} + bool MoqtSession::PublishNamespace( const TrackNamespace& track_namespace, const MessageParameters& parameters, MoqtResponseCallback response_callback, @@ -963,6 +958,21 @@ temp_stream->OnCanRead(); break; } + case MoqtMessageType::kSubscribeTracks: { + // TODO(martinduke): Implement this. + MoqtControlMessageQueue queue(stream_); + if (!queue + .SendOrBufferMessage( + session_->framer_.SerializeRequestError(MoqtRequestError{ + /*request_id=*/0, RequestErrorCode::kNotSupported, + std::nullopt, "SUBSCRIBE_TRACKS is not supported"}), + /*fin=*/true) + .ok()) { + session_->Error(MoqtError::kInternalError, "Internal write error"); + return; + } + break; + } case MoqtMessageType::kPublish: { auto publish_stream = std::make_unique<MoqtPublishResponseStream>( &session_->framer_, session_->ControlMessageParser(),
diff --git a/quiche/quic/moqt/moqt_session.h b/quiche/quic/moqt/moqt_session.h index 66660be..b031dc2 100644 --- a/quiche/quic/moqt/moqt_session.h +++ b/quiche/quic/moqt/moqt_session.h
@@ -132,9 +132,12 @@ // MoqtNamespaceTask::SetObjectsAvailableCallback() to actually retrieve // namespaces. std::unique_ptr<MoqtNamespaceTask> SubscribeNamespace( - TrackNamespace& prefix, SubscribeNamespaceOption option, - const MessageParameters& parameters, + TrackNamespace& prefix, const MessageParameters& parameters, MoqtResponseCallback response_callback) override; + bool SubscribeTracks(TrackNamespace& prefix, + const MessageParameters& parameters, + MoqtResponseCallback response_callback) override; + void UnsubscribeTracks(TrackNamespace& prefix) override; quiche::QuicheWeakPtr<MoqtSessionInterface> GetWeakPtr() override { return weak_ptr_factory_.Create(); }
diff --git a/quiche/quic/moqt/moqt_session_callbacks.h b/quiche/quic/moqt/moqt_session_callbacks.h index 5b2070b..224ffac 100644 --- a/quiche/quic/moqt/moqt_session_callbacks.h +++ b/quiche/quic/moqt/moqt_session_callbacks.h
@@ -104,9 +104,11 @@ // tracks and namespaces, as appropriate, that are already present. using MoqtIncomingSubscribeNamespaceCallback = quiche::MultiUseCallback<std::unique_ptr<MoqtNamespaceTask>( - const TrackNamespace& prefix, SubscribeNamespaceOption option, - const MessageParameters& parameters, + const TrackNamespace& prefix, const MessageParameters& parameters, MoqtResponseCallback response_callback)>; +using MoqtIncomingSubscribeTracksCallback = quiche::MultiUseCallback<void( + const TrackNamespace& prefix, const MessageParameters& parameters, + MoqtResponseCallback response_callback)>; inline void DefaultIncomingPublishNamespaceCallback( const TrackNamespace&, const std::optional<MessageParameters>&, @@ -121,7 +123,7 @@ inline std::unique_ptr<MoqtNamespaceTask> DefaultIncomingSubscribeNamespaceCallback( - const TrackNamespace&, SubscribeNamespaceOption, const MessageParameters&, + const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)( MoqtRequestErrorInfo{RequestErrorCode::kNotSupported, std::nullopt, @@ -129,6 +131,15 @@ return nullptr; } +// If |response_callback| is nullptr, it's removing a subscription. +inline void DefaultIncomingSubscribeTracksCallback( + const TrackNamespace&, const MessageParameters&, + MoqtResponseCallback response_callback) { + std::move(response_callback)( + MoqtRequestErrorInfo{RequestErrorCode::kNotSupported, std::nullopt, + "This endpoint cannot publish."}); +} + inline SubscribeVisitor* DefaultIncomingPublishCallback( const FullTrackName&, const MessageParameters&, const TrackExtensions&, MoqtResponseCallback) { @@ -148,6 +159,8 @@ DefaultIncomingPublishNamespaceCallback; MoqtIncomingSubscribeNamespaceCallback incoming_subscribe_namespace_callback = DefaultIncomingSubscribeNamespaceCallback; + MoqtIncomingSubscribeTracksCallback incoming_subscribe_tracks_callback = + DefaultIncomingSubscribeTracksCallback; MoqtIncomingPublishCallback incoming_publish_callback = DefaultIncomingPublishCallback; const quic::QuicClock* clock = quic::QuicDefaultClock::Get();
diff --git a/quiche/quic/moqt/moqt_session_interface.h b/quiche/quic/moqt/moqt_session_interface.h index 72c5164..c08cf50 100644 --- a/quiche/quic/moqt/moqt_session_interface.h +++ b/quiche/quic/moqt/moqt_session_interface.h
@@ -177,9 +177,12 @@ // Returns nullptr if the message cannot be sent. // To unsubscribe, simply destroy the returned MoqtNamespaceTask. virtual std::unique_ptr<MoqtNamespaceTask> SubscribeNamespace( - TrackNamespace& prefix, SubscribeNamespaceOption option, - const MessageParameters& parameters, + TrackNamespace& prefix, const MessageParameters& parameters, MoqtResponseCallback response_callback) = 0; + virtual bool SubscribeTracks(TrackNamespace& prefix, + const MessageParameters& parameters, + MoqtResponseCallback response_callback) = 0; + virtual void UnsubscribeTracks(TrackNamespace& prefix) = 0; // TODO(martinduke): Add an API for absolute joining fetch.
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc index d4a6b97..0581f28 100644 --- a/quiche/quic/moqt/moqt_session_test.cc +++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -168,7 +168,7 @@ // |wt_stream| is the underlying mock WebTransport stream; if nullptr, use // mock_bidi_stream_. static constexpr absl::string_view kSubscribeByte = "\x03"; - static constexpr absl::string_view kSubscribeNamespaceByte = "\x11"; + static constexpr absl::string_view kSubscribeNamespaceByte = "\x50"; static constexpr absl::string_view kPublishByte = "\x1d"; std::unique_ptr<MoqtBidiStreamBase> ResponseStream( absl::string_view first_byte, @@ -1132,7 +1132,7 @@ mock_bidi_stream_, Writev(ControlMessageOfType(MoqtMessageType::kSubscribeNamespace), _)); std::unique_ptr<MoqtNamespaceTask> task = session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kNamespace, MessageParameters(), + prefix, MessageParameters(), [&](std::variant<MessageParameters, MoqtRequestErrorInfo> response) { got_callback = true; EXPECT_TRUE(std::holds_alternative<MessageParameters>(response)); @@ -1151,7 +1151,7 @@ mock_bidi_stream_, Writev(ControlMessageOfType(MoqtMessageType::kSubscribeNamespace), _)); std::unique_ptr<MoqtNamespaceTask> task = session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kNamespace, MessageParameters(), + prefix, MessageParameters(), [&](std::variant<MessageParameters, MoqtRequestErrorInfo> response) { got_callback = true; EXPECT_TRUE(std::holds_alternative<MoqtRequestErrorInfo>(response)); @@ -1167,26 +1167,6 @@ EXPECT_TRUE(got_callback); } -TEST_F(MoqtSessionTest, SubscribeNamespacePublishOnly) { - TrackNamespace prefix({"foo"}); - // kPublish is not allowed. - EXPECT_EQ(session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kPublish, MessageParameters(), - [&](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), - nullptr); - // kBoth is treated as kNamespace. - PrepareRequestStream(bidi_wrapper_); - EXPECT_CALL(mock_bidi_stream_, - Writev(SerializedControlMessage(MoqtSubscribeNamespace{ - 0, prefix, SubscribeNamespaceOption::kNamespace, - MessageParameters()}), - _)); - EXPECT_NE(session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kBoth, MessageParameters(), - [&](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), - nullptr); -} - TEST_F(MoqtSessionTest, SubscribeOkWithBadTrackAlias) { PrepareRequestStream(bidi_wrapper_); session_.Subscribe(FullTrackName("foo", "bar"), &remote_track_visitor_, @@ -1962,15 +1942,14 @@ parameters.authorization_tokens.emplace_back(AuthTokenType::kOutOfBand, "foo"); - MoqtSubscribeNamespace subscribe_namespace = { - /*request_id=*/1, prefix, SubscribeNamespaceOption::kBoth, parameters}; + MoqtSubscribeNamespace subscribe_namespace = {/*request_id=*/1, prefix, + parameters}; quiche::QuicheWeakPtr<MockNamespaceTask> task; MoqtRequestOk expected_ok(/*request_id=*/1); expected_ok.parameters.expires = quic::QuicTimeDelta::FromSeconds(60); EXPECT_CALL(session_callbacks_.incoming_subscribe_namespace_callback, - Call(prefix, SubscribeNamespaceOption::kBoth, parameters, _)) - .WillOnce([&](const TrackNamespace& prefix, SubscribeNamespaceOption, - const MessageParameters&, + Call(prefix, parameters, _)) + .WillOnce([&](const TrackNamespace& prefix, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(expected_ok.parameters); auto task_ptr = std::make_unique<MockNamespaceTask>(prefix); @@ -2013,12 +1992,11 @@ "foo"); bidi_wrapper_ = std::make_unique<MoqtBidiStreamTestWrapper>( ResponseStream(kSubscribeNamespaceByte)); - MoqtSubscribeNamespace subscribe_namespace = { - /*request_id=*/1, prefix, SubscribeNamespaceOption::kBoth, parameters}; + MoqtSubscribeNamespace subscribe_namespace = {/*request_id=*/1, prefix, + parameters}; EXPECT_CALL(session_callbacks_.incoming_subscribe_namespace_callback, - Call(prefix, SubscribeNamespaceOption::kBoth, parameters, _)) - .WillOnce([&](const TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, + Call(prefix, parameters, _)) + .WillOnce([&](const TrackNamespace&, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MoqtRequestErrorInfo{ RequestErrorCode::kUnauthorized, std::nullopt, "foo"}); @@ -2041,12 +2019,11 @@ "foo"); bidi_wrapper_ = std::make_unique<MoqtBidiStreamTestWrapper>( ResponseStream(kSubscribeNamespaceByte)); - MoqtSubscribeNamespace subscribe_namespace = { - /*request_id=*/1, foo, SubscribeNamespaceOption::kBoth, parameters}; + MoqtSubscribeNamespace subscribe_namespace = {/*request_id=*/1, foo, + parameters}; EXPECT_CALL(session_callbacks_.incoming_subscribe_namespace_callback, - Call(foo, SubscribeNamespaceOption::kBoth, parameters, _)) - .WillOnce([&](const TrackNamespace& prefix, SubscribeNamespaceOption, - const MessageParameters&, + Call(foo, parameters, _)) + .WillOnce([&](const TrackNamespace& prefix, const MessageParameters&, MoqtResponseCallback response_callback) { std::move(response_callback)(MessageParameters()); auto task_ptr = std::make_unique<MockNamespaceTask>(prefix); @@ -2328,11 +2305,10 @@ EXPECT_FALSE(session_.Subscribe(FullTrackName("foo", "bar"), &remote_track_visitor_, parameters)); TrackNamespace prefix({"foo"}); - EXPECT_EQ( - session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kNamespace, MessageParameters(), - +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), - nullptr); + EXPECT_EQ(session_.SubscribeNamespace( + prefix, MessageParameters(), + +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), + nullptr); session_.PublishNamespace( TrackNamespace{"foo"}, MessageParameters(), +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}, @@ -2403,11 +2379,10 @@ EXPECT_FALSE(session_.Subscribe(FullTrackName({"foo"}, "bar"), &remote_track_visitor_, parameters)); TrackNamespace prefix({"foo"}); - EXPECT_EQ( - session_.SubscribeNamespace( - prefix, SubscribeNamespaceOption::kNamespace, MessageParameters(), - +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), - nullptr); + EXPECT_EQ(session_.SubscribeNamespace( + prefix, MessageParameters(), + +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}), + nullptr); session_.PublishNamespace( TrackNamespace{"foo"}, MessageParameters(), +[](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}, @@ -2909,9 +2884,9 @@ uint64_t type; uint16_t length; uint64_t request_id; - bool type_read = reader.ReadVarInt62(&type); + bool type_read = reader.ReadMoqVarInt(&type); bool length_read = reader.ReadUInt16(&length); - bool req_id_read = reader.ReadVarInt62(&request_id); + bool req_id_read = reader.ReadMoqVarInt(&request_id); EXPECT_TRUE(type_read) << "Failed to read type, written_data.size()=" << stream.write_buffer().length(); EXPECT_TRUE(length_read); @@ -2928,7 +2903,7 @@ .WillOnce(Return(&sub_ns_stream)); TrackNamespace namespace1({"namespace1"}); std::unique_ptr<MoqtNamespaceTask> task1 = session_.SubscribeNamespace( - namespace1, SubscribeNamespaceOption::kNamespace, MessageParameters(), + namespace1, MessageParameters(), [](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}); ASSERT_NE(task1, nullptr); EXPECT_EQ(get_request_id(sub_ns_stream), next_request_id); @@ -2996,7 +2971,7 @@ .WillOnce(Return(&sub_ns_stream_2)); TrackNamespace namespace_dup({"namespace_dup"}); std::unique_ptr<MoqtNamespaceTask> task_dup = session_.SubscribeNamespace( - namespace_dup, SubscribeNamespaceOption::kNamespace, MessageParameters(), + namespace_dup, MessageParameters(), [](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}); ASSERT_NE(task_dup, nullptr); EXPECT_EQ(next_request_id, get_request_id(sub_ns_stream_2));
diff --git a/quiche/quic/moqt/moqt_types.h b/quiche/quic/moqt/moqt_types.h index 32f38d6..873dc4f 100644 --- a/quiche/quic/moqt/moqt_types.h +++ b/quiche/quic/moqt/moqt_types.h
@@ -79,13 +79,6 @@ } }; -enum class QUICHE_EXPORT SubscribeNamespaceOption : uint64_t { - kPublish = 0x00, - kNamespace = 0x01, - kBoth = 0x02, -}; -static constexpr uint64_t kMaxSubscribeOption = 0x02; - } // namespace moqt #endif // QUICHE_QUIC_MOQT_MOQT_TYPES_H_
diff --git a/quiche/quic/moqt/relay_namespace_tree.cc b/quiche/quic/moqt/relay_namespace_tree.cc index 8524f45..a21a344 100644 --- a/quiche/quic/moqt/relay_namespace_tree.cc +++ b/quiche/quic/moqt/relay_namespace_tree.cc
@@ -27,7 +27,7 @@ namespace moqt { RelayNamespaceTree::RelayNamespaceListener::~RelayNamespaceListener() { - tree_.RemoveSubscriber(prefix_, this); + tree_.RemoveNamespaceSubscriber(prefix_, this); } void RelayNamespaceTree::RelayNamespaceListener::SetObjectsAvailableCallback( @@ -85,15 +85,6 @@ callback_(); } -void RelayNamespaceTree::RelayNamespaceListener::Publish(TrackNamespace, - absl::string_view) { - if (session_ == nullptr) { - return; // Not interested in tracks. - } - // TODO(martinduke): Build a full track name from prefix_, suffix, and name, - // then call session_->Publish(). -} - void RelayNamespaceTree::RelayNamespaceListener::DeclareEof() { if (eof_ || error_.has_value()) { return; @@ -126,17 +117,16 @@ } } -std::unique_ptr<MoqtNamespaceTask> RelayNamespaceTree::AddSubscriber( - const TrackNamespace& prefix, - MoqtSessionInterface* absl_nullable track_listener) { +std::unique_ptr<MoqtNamespaceTask> RelayNamespaceTree::AddNamespaceSubscriber( + const TrackNamespace& prefix) { Node* node = FindOrCreateNode(prefix); - auto task = - std::make_unique<RelayNamespaceListener>(*this, prefix, track_listener); + auto task = std::make_unique<RelayNamespaceListener>(*this, prefix); node->listeners[task.get()] = task->GetWeakPtr(); return std::move(task); } -void RelayNamespaceTree::RemoveSubscriber( +// private +void RelayNamespaceTree::RemoveNamespaceSubscriber( TrackNamespace prefix, MoqtNamespaceTask* absl_nonnull listener) { Node* node = FindNode(prefix); if (node == nullptr) { @@ -204,9 +194,6 @@ if (!node->publishers.empty()) { listener->AddPendingSuffix(suffix, TransactionType::kAdd); } - for (const std::string& track : node->published_tracks) { - listener->Publish(suffix, track); - } for (auto child = node->children.begin(); child != node->children.end(); ++child) { if (std::optional<absl::string_view> element = (*child)->element) {
diff --git a/quiche/quic/moqt/relay_namespace_tree.h b/quiche/quic/moqt/relay_namespace_tree.h index 2b58715..469a284 100644 --- a/quiche/quic/moqt/relay_namespace_tree.h +++ b/quiche/quic/moqt/relay_namespace_tree.h
@@ -39,12 +39,8 @@ // If |tracks| is nullptr, the listener is not interested in PUBLISH // messages. RelayNamespaceListener(RelayNamespaceTree& tree, - const TrackNamespace& prefix, - MoqtSessionInterface* absl_nullable tracks) - : prefix_(prefix), - tree_(tree), - session_(tracks), - weak_ptr_factory_(this) {} + const TrackNamespace& prefix) + : prefix_(prefix), tree_(tree), weak_ptr_factory_(this) {} ~RelayNamespaceListener() override; // MoqtNamespaceTask methods. void SetObjectsAvailableCallback(ObjectsAvailableCallback @@ -61,8 +57,6 @@ // Queues a suffix corresponding to a NAMESPACE (if |type| is kAdd) or a // NAMESPACE_DONE (if |type| is kDelete). void AddPendingSuffix(TrackNamespace suffix, TransactionType type); - // Publishes a track in this namespace. - void Publish(TrackNamespace suffix, absl::string_view name); void DeclareEof(); quiche::QuicheWeakPtr<RelayNamespaceListener> GetWeakPtr() { return weak_ptr_factory_.Create(); @@ -80,7 +74,6 @@ RelayNamespaceTree& tree_; std::optional<webtransport::StreamErrorCode> error_; quiche::QuicheCircularDeque<PendingSuffix> pending_suffixes_; - MoqtSessionInterface* absl_nullable session_; ObjectsAvailableCallback absl_nullable callback_ = nullptr; bool eof_ = false; bool got_first_pull_ = false; @@ -89,22 +82,35 @@ }; public: - // Adds a publisher to the namespace tree. The caller is responsible to call - // RemovePublisher if it goes away. |session| is stored as a WeakPtr. + // Adds a namespace publisher to the namespace tree. The caller is responsible + // to call RemovePublisher if it goes away. |session| is stored as a WeakPtr. void AddPublisher(TrackNamespace prefix, MoqtSessionInterface* absl_nonnull session); - void RemovePublisher(const TrackNamespace& prefix, MoqtSessionInterface* absl_nonnull session); - // Called on incoming SUBSCRIBE_NAMESPACE messages. If track_subscriber is - // nullptr, it is not interested in PUBLISH messages. If callback is nullptr, - // it is not interested in NAMESPACE messages. If not interested in, - // namespaces, will return nullptr. Otherwise, will return a task to flow - // control published namespaces. - std::unique_ptr<MoqtNamespaceTask> AddSubscriber( - const TrackNamespace& prefix, - MoqtSessionInterface* absl_nullable track_listener); + // Called on incoming SUBSCRIBE_NAMESPACE messages. Returns nullptr if the + // relay is unable to support the request. The subscription can be removed by + // freeing the returned task. + std::unique_ptr<MoqtNamespaceTask> AddNamespaceSubscriber( + const TrackNamespace& prefix); + // Called on incoming SUBSCRIBE_TRACKS messages. Returns false if the relay is + // unable to support the request. + // TODO(martinduke): Implement this. + bool AddTrackSubscriber( + const TrackNamespace& /*prefix*/, + MoqtSessionInterface* absl_nonnull /*track_listener*/) { + return false; + } + void RemoveTrackSubscriber( + const TrackNamespace& /*prefix*/, + MoqtSessionInterface* absl_nonnull /*track_listener*/) {} + + // As the relay learns about tracks, Add and Remove so that they can be + // PUBLISHEed on SUBSCRIBE_TRACKS. MoqtKnownTrackPublisher manages the + // existence of these tracks, so just store the track name. + void AddTrack(const FullTrackName& /*track_name*/) {} + void RemoveTrack(const FullTrackName& /*track_name*/) {} // Returns a raw pointer to the session that publishes the smallest namespace // that contains |track_namespace|. If a WeakPtr is found to be invalid, @@ -135,6 +141,9 @@ // go away. absl::flat_hash_map<void*, quiche::QuicheWeakPtr<RelayNamespaceListener>> listeners; + // Sessions listening for PUBLISH. + absl::flat_hash_map<void*, quiche::QuicheWeakPtr<MoqtSessionInterface>> + publish_subscribers; // Just store the track name. Additional information will be in the // TrackPublisher. absl::flat_hash_set<std::string> published_tracks; @@ -145,10 +154,15 @@ Node* FindOrCreateNode(TrackNamespace track_namespace); - // Recursive function to notify |listener| of all published namespaces and - // tracks in and below |node|. + // Recursive function to notify |listener| of all published namespaces in and + // below |node|. void NotifyOfAllChildren(Node* node, TrackNamespace& suffix, RelayNamespaceListener* absl_nonnull listener); + // Recursive function to send PUBLISH to |session| for all tracks in or below + // |node|. + // TODO(martinduke): Implement this. + void NotifyOfAllChildren(Node* /*node*/, TrackNamespace& /*suffix*/, + MoqtSessionInterface* absl_nonnull /*session*/) {} // If |adding| is true, sends NAMESPACE to all subscribers to a // parent namespace. If |adding| is false, sends NAMESPACE_DONE. @@ -158,8 +172,9 @@ // if the same applies to its parent. void MaybePrune(Node* node, TrackNamespace track_namespace); - void RemoveSubscriber(TrackNamespace prefix, - MoqtNamespaceTask* absl_nonnull namespace_listener); + void RemoveNamespaceSubscriber( + TrackNamespace prefix, + MoqtNamespaceTask* absl_nonnull namespace_listener); // A map that allows quick access to any namespace without traversing the // tree. Use unique_ptr so that it's pointer stable.
diff --git a/quiche/quic/moqt/relay_namespace_tree_test.cc b/quiche/quic/moqt/relay_namespace_tree_test.cc index 324e003..0ffc145 100644 --- a/quiche/quic/moqt/relay_namespace_tree_test.cc +++ b/quiche/quic/moqt/relay_namespace_tree_test.cc
@@ -59,8 +59,7 @@ TEST_F(RelayNamespaceTreeTest, AddGetRemoveListener) { // Add a listener to a namespace that has no publishers. EXPECT_EQ(tree_.NumNamespaces(), 0u); - std::unique_ptr<MoqtNamespaceTask> task = - tree_.AddSubscriber(ab_, session_.get()); + std::unique_ptr<MoqtNamespaceTask> task = tree_.AddNamespaceSubscriber(ab_); task->SetObjectsAvailableCallback(std::move(callback_)); EXPECT_EQ(tree_.NumNamespaces(), 3u); tree_.AddPublisher(a_, session_.get()); @@ -90,8 +89,7 @@ } TEST_F(RelayNamespaceTreeTest, SessionDestroyed) { - std::unique_ptr<MoqtNamespaceTask> task = - tree_.AddSubscriber(ab_, session_.get()); + std::unique_ptr<MoqtNamespaceTask> task = tree_.AddNamespaceSubscriber(ab_); task->SetObjectsAvailableCallback(std::move(callback_)); tree_.AddPublisher(ab_, session_.get()); EXPECT_EQ(objects_available_, 1); @@ -107,8 +105,7 @@ tree_.AddPublisher(a_, session_.get()); tree_.AddPublisher(ab_, session_.get()); tree_.AddPublisher(abc_, session_.get()); - std::unique_ptr<MoqtNamespaceTask> task = - tree_.AddSubscriber(ab_, session_.get()); + std::unique_ptr<MoqtNamespaceTask> task = tree_.AddNamespaceSubscriber(ab_); task->SetObjectsAvailableCallback(std::move(callback_)); EXPECT_EQ(objects_available_, 2); CheckNextSuffix(task.get(), ab_); @@ -116,8 +113,7 @@ } TEST_F(RelayNamespaceTreeTest, MaxSizeNamespace) { - std::unique_ptr<MoqtNamespaceTask> task = - tree_.AddSubscriber(a_, session_.get()); + std::unique_ptr<MoqtNamespaceTask> task = tree_.AddNamespaceSubscriber(a_); task->SetObjectsAvailableCallback(std::move(callback_)); TrackNamespace big_namespace{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
diff --git a/quiche/quic/moqt/test_tools/mock_moqt_session.h b/quiche/quic/moqt/test_tools/mock_moqt_session.h index 2cd1df2..135fe0b 100644 --- a/quiche/quic/moqt/test_tools/mock_moqt_session.h +++ b/quiche/quic/moqt/test_tools/mock_moqt_session.h
@@ -119,9 +119,12 @@ RequestErrorCode error_code, absl::string_view error_reason), (override)); MOCK_METHOD(std::unique_ptr<MoqtNamespaceTask>, SubscribeNamespace, - (TrackNamespace&, SubscribeNamespaceOption, - const MessageParameters&, MoqtResponseCallback), + (TrackNamespace&, const MessageParameters&, MoqtResponseCallback), (override)); + MOCK_METHOD(bool, SubscribeTracks, + (TrackNamespace&, const MessageParameters&, MoqtResponseCallback), + (override)); + MOCK_METHOD(void, UnsubscribeTracks, (TrackNamespace&), (override)); quiche::QuicheWeakPtr<MoqtSessionInterface> GetWeakPtr() override { return weak_factory_.Create();
diff --git a/quiche/quic/moqt/test_tools/moqt_framer_utils.cc b/quiche/quic/moqt/test_tools/moqt_framer_utils.cc index a0e6d42..f35d15b 100644 --- a/quiche/quic/moqt/test_tools/moqt_framer_utils.cc +++ b/quiche/quic/moqt/test_tools/moqt_framer_utils.cc
@@ -61,6 +61,9 @@ quiche::QuicheBuffer operator()(const MoqtSubscribeNamespace& message) { return framer.SerializeSubscribeNamespace(message); } + quiche::QuicheBuffer operator()(const MoqtSubscribeTracks& message) { + return framer.SerializeSubscribeTracks(message); + } quiche::QuicheBuffer operator()(const MoqtMaxRequestId& message) { return framer.SerializeMaxRequestId(message); }
diff --git a/quiche/quic/moqt/test_tools/moqt_framer_utils.h b/quiche/quic/moqt/test_tools/moqt_framer_utils.h index c5a5d4a..43a95f7 100644 --- a/quiche/quic/moqt/test_tools/moqt_framer_utils.h +++ b/quiche/quic/moqt/test_tools/moqt_framer_utils.h
@@ -24,9 +24,9 @@ MoqtSubscribeOk, MoqtPublishDone, MoqtRequestUpdate, MoqtPublishNamespace, MoqtPublishNamespaceDone, MoqtPublishNamespaceCancel, MoqtTrackStatus, MoqtGoAway, - MoqtSubscribeNamespace, MoqtMaxRequestId, MoqtFetch, - MoqtFetchCancel, MoqtFetchOk, MoqtRequestsBlocked, MoqtPublish, - MoqtNamespace, MoqtNamespaceDone, MoqtObjectAck>; + MoqtSubscribeNamespace, MoqtSubscribeTracks, MoqtMaxRequestId, + MoqtFetch, MoqtFetchCancel, MoqtFetchOk, MoqtRequestsBlocked, + MoqtPublish, MoqtNamespace, MoqtNamespaceDone, MoqtObjectAck>; std::string SerializeGenericMessage(const AnyMoqtControlMessage& frame, bool use_webtrans = false);
diff --git a/quiche/quic/moqt/test_tools/moqt_mock_visitor.h b/quiche/quic/moqt/test_tools/moqt_mock_visitor.h index 5b46d41..69d8269 100644 --- a/quiche/quic/moqt/test_tools/moqt_mock_visitor.h +++ b/quiche/quic/moqt/test_tools/moqt_mock_visitor.h
@@ -47,9 +47,11 @@ MoqtResponseCallback)> incoming_publish_namespace_callback; testing::MockFunction<std::unique_ptr<MoqtNamespaceTask>( - const TrackNamespace&, SubscribeNamespaceOption, const MessageParameters&, - MoqtResponseCallback)> + const TrackNamespace&, const MessageParameters&, MoqtResponseCallback)> incoming_subscribe_namespace_callback; + testing::MockFunction<bool(const TrackNamespace&, const MessageParameters&, + MoqtResponseCallback)> + incoming_subscribe_tracks_callback; MockSessionCallbacks() { ON_CALL(incoming_publish_namespace_callback, Call)
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h index 4e1d394..76b5c67 100644 --- a/quiche/quic/moqt/test_tools/moqt_test_message.h +++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -116,8 +116,8 @@ MoqtSetup, MoqtObject, MoqtRequestOk, MoqtRequestError, MoqtSubscribe, MoqtSubscribeOk, MoqtPublishDone, MoqtRequestUpdate, MoqtPublishNamespace, MoqtPublishNamespaceDone, MoqtPublishNamespaceCancel, MoqtTrackStatus, - MoqtGoAway, MoqtSubscribeNamespace, MoqtMaxRequestId, MoqtFetch, - MoqtFetchCancel, MoqtFetchOk, MoqtRequestsBlocked, MoqtPublish, + MoqtGoAway, MoqtSubscribeNamespace, MoqtSubscribeTracks, MoqtMaxRequestId, + MoqtFetch, MoqtFetchCancel, MoqtFetchOk, MoqtRequestsBlocked, MoqtPublish, MoqtNamespace, MoqtNamespaceDone, MoqtObjectAck>; // The total actual size of the message. @@ -1267,10 +1267,6 @@ QUIC_LOG(INFO) << "SUBSCRIBE_NAMESPACE track namespace mismatch"; return false; } - if (cast.subscribe_options != subscribe_namespace_.subscribe_options) { - QUIC_LOG(INFO) << "SUBSCRIBE_NAMESPACE subscribe options mismatch"; - return false; - } if (cast.parameters != subscribe_namespace_.parameters) { QUIC_LOG(INFO) << "SUBSCRIBE_NAMESPACE parameters mismatch"; return false; @@ -1278,17 +1274,16 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----"); } + void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----vv"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(subscribe_namespace_); } private: - uint8_t raw_packet_[20] = { - 0x11, 0x00, 0x11, 0x01, // request_id = 1 + uint8_t raw_packet_[19] = { + 0x50, 0x00, 0x10, 0x01, // request_id = 1 0x01, 0x03, 0x66, 0x6f, 0x6f, // namespace = "foo" - 0x02, // subscribe_options = kBoth 0x02, // 2 parameters 0x03, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" 0x0d, 0x01, // forward = true @@ -1297,7 +1292,55 @@ MoqtSubscribeNamespace subscribe_namespace_ = { /*request_id=*/1, TrackNamespace({"foo"}), - SubscribeNamespaceOption::kBoth, + MessageParameters(), // set in constructor. + }; +}; + +class QUICHE_NO_EXPORT SubscribeTracksMessage : public TestMessageBase { + public: + SubscribeTracksMessage() : TestMessageBase() { + subscribe_tracks_.parameters.authorization_tokens.push_back( + AuthToken(AuthTokenType::kOutOfBand, "bar")); + subscribe_tracks_.parameters.set_forward(true); + SetWireImage(raw_packet_, sizeof(raw_packet_)); + } + + bool EqualFieldValues(const MessageStructuredData& values) const override { + auto cast = std::get<MoqtSubscribeTracks>(values); + if (cast.request_id != subscribe_tracks_.request_id) { + QUIC_LOG(INFO) << "SUBSCRIBE_TRACKS request_id mismatch"; + return false; + } + if (cast.track_namespace_prefix != + subscribe_tracks_.track_namespace_prefix) { + QUIC_LOG(INFO) << "SUBSCRIBE_TRACKS track namespace mismatch"; + return false; + } + if (cast.parameters != subscribe_tracks_.parameters) { + QUIC_LOG(INFO) << "SUBSCRIBE_TRACKS parameters mismatch"; + return false; + } + return true; + } + + void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----vv"); } + + MessageStructuredData structured_data() const override { + return TestMessageBase::MessageStructuredData(subscribe_tracks_); + } + + private: + uint8_t raw_packet_[19] = { + 0x51, 0x00, 0x10, 0x01, // request_id = 1 + 0x01, 0x03, 0x66, 0x6f, 0x6f, // namespace = "foo" + 0x02, // 2 parameters + 0x03, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" + 0x0d, 0x01, // forward = true + }; + + MoqtSubscribeTracks subscribe_tracks_ = { + /*request_id=*/1, + TrackNamespace({"foo"}), MessageParameters(), // set in constructor. }; }; @@ -1797,6 +1840,8 @@ return std::make_unique<GoAwayMessage>(); case MoqtMessageType::kSubscribeNamespace: return std::make_unique<SubscribeNamespaceMessage>(); + case MoqtMessageType::kSubscribeTracks: + return std::make_unique<SubscribeTracksMessage>(); case MoqtMessageType::kMaxRequestId: return std::make_unique<MaxRequestIdMessage>(); case MoqtMessageType::kFetch:
diff --git a/quiche/quic/moqt/tools/chat_client.cc b/quiche/quic/moqt/tools/chat_client.cc index 558470c..86b2750 100644 --- a/quiche/quic/moqt/tools/chat_client.cc +++ b/quiche/quic/moqt/tools/chat_client.cc
@@ -324,9 +324,8 @@ MessageParameters parameters; parameters.authorization_tokens.emplace_back( AuthTokenType::kOutOfBand, std::string(GetUsername(my_track_name_))); - namespace_task_ = - session_->SubscribeNamespace(prefix, SubscribeNamespaceOption::kNamespace, - parameters, std::move(response_callback)); + namespace_task_ = session_->SubscribeNamespace(prefix, parameters, + std::move(response_callback)); if (namespace_task_ != nullptr) { namespace_task_->SetObjectsAvailableCallback( [this]() {
diff --git a/quiche/quic/moqt/tools/moq_chat_end_to_end_test.cc b/quiche/quic/moqt/tools/moq_chat_end_to_end_test.cc index 75ffa99..7c2b46a 100644 --- a/quiche/quic/moqt/tools/moq_chat_end_to_end_test.cc +++ b/quiche/quic/moqt/tools/moq_chat_end_to_end_test.cc
@@ -151,7 +151,7 @@ TransactionType last_type; std::unique_ptr<MoqtNamespaceTask> namespace_probe = relay_.publisher()->AddNamespaceSubscriber( - TrackNamespace({moq_chat::kBasePath}), nullptr); + TrackNamespace({moq_chat::kBasePath})); namespace_probe->SetObjectsAvailableCallback([&]() { while (namespace_probe->GetNextSuffix(last_suffix, last_type) == kSuccess) { if (last_type == TransactionType::kAdd) {
diff --git a/quiche/quic/moqt/tools/moqt_relay.cc b/quiche/quic/moqt/tools/moqt_relay.cc index 14b77bd..48d2a80 100644 --- a/quiche/quic/moqt/tools/moqt_relay.cc +++ b/quiche/quic/moqt/tools/moqt_relay.cc
@@ -16,6 +16,7 @@ #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/io/quic_event_loop.h" #include "quiche/quic/core/quic_server_id.h" +#include "quiche/quic/moqt/moqt_error.h" #include "quiche/quic/moqt/moqt_fetch_task.h" #include "quiche/quic/moqt/moqt_key_value_pair.h" #include "quiche/quic/moqt/moqt_names.h" @@ -127,27 +128,36 @@ } }; session->callbacks().incoming_subscribe_namespace_callback = - [this, session](const TrackNamespace& prefix, - SubscribeNamespaceOption option, - const MessageParameters& parameters, - MoqtResponseCallback response_callback) + [this](const TrackNamespace& prefix, const MessageParameters& parameters, + MoqtResponseCallback response_callback) -> std::unique_ptr<MoqtNamespaceTask> { if (is_closing_) { return nullptr; } - std::unique_ptr<MoqtNamespaceTask> task; - switch (option) { - case SubscribeNamespaceOption::kNamespace: - task = publisher_.AddNamespaceSubscriber(prefix, nullptr); - break; - case SubscribeNamespaceOption::kBoth: - case SubscribeNamespaceOption::kPublish: - task = publisher_.AddNamespaceSubscriber(prefix, session); - break; + std::unique_ptr<MoqtNamespaceTask> task = + publisher_.AddNamespaceSubscriber(prefix); + if (task == nullptr) { + std::move(response_callback)(MoqtRequestErrorInfo{ + RequestErrorCode::kInternalError, std::nullopt, ""}); + return nullptr; } std::move(response_callback)(MessageParameters()); return task; }; + session->callbacks().incoming_subscribe_tracks_callback = + [this, session](const TrackNamespace& prefix, + const MessageParameters& parameters, + MoqtResponseCallback response_callback) { + if (is_closing_) { + return; + } + if (!publisher_.AddTrackSubscriber(prefix, session)) { + std::move(response_callback)(MoqtRequestErrorInfo{ + RequestErrorCode::kInternalError, std::nullopt, ""}); + } else { + std::move(response_callback)(MessageParameters()); + } + }; } absl::StatusOr<MoqtConfigureSessionCallback> MoqtRelay::IncomingSessionHandler(
diff --git a/quiche/quic/moqt/tools/moqt_relay_test.cc b/quiche/quic/moqt/tools/moqt_relay_test.cc index 7c8ae77..2ea008e 100644 --- a/quiche/quic/moqt/tools/moqt_relay_test.cc +++ b/quiche/quic/moqt/tools/moqt_relay_test.cc
@@ -23,7 +23,6 @@ #include "quiche/quic/moqt/moqt_relay_publisher.h" #include "quiche/quic/moqt/moqt_session.h" #include "quiche/quic/moqt/moqt_session_interface.h" -#include "quiche/quic/moqt/moqt_types.h" #include "quiche/quic/moqt/test_tools/moqt_mock_visitor.h" #include "quiche/quic/moqt/tools/moqt_client.h" #include "quiche/quic/moqt/tools/moqt_server.h" @@ -161,7 +160,7 @@ TrackNamespace suffix; TransactionType type; std::unique_ptr<MoqtNamespaceTask> relay_probe = - relay_.publisher()->AddNamespaceSubscriber(foo, nullptr); + relay_.publisher()->AddNamespaceSubscriber(foo); relay_probe->SetObjectsAvailableCallback([&]() { while (relay_probe->GetNextSuffix(suffix, type) == kSuccess) { if (type == TransactionType::kAdd) { @@ -174,7 +173,7 @@ } }); std::unique_ptr<MoqtNamespaceTask> upstream_probe = - upstream_.publisher()->AddNamespaceSubscriber(foo, nullptr); + upstream_.publisher()->AddNamespaceSubscriber(foo); upstream_probe->SetObjectsAvailableCallback([&]() { while (upstream_probe->GetNextSuffix(suffix, type) == kSuccess) { if (type == TransactionType::kAdd) { @@ -202,7 +201,7 @@ // Upstream subscribes. Now it's notified and forwards it to the probe. std::unique_ptr<MoqtNamespaceTask> task = upstream_session->SubscribeNamespace( - foo, SubscribeNamespaceOption::kNamespace, MessageParameters(), + foo, MessageParameters(), [](std::variant<MessageParameters, MoqtRequestErrorInfo>) {}); EXPECT_NE(task, nullptr); task->SetObjectsAvailableCallback([&]() {