| // Copyright (c) 2024 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_QUIC_MOQT_TOOLS_CHAT_CLIENT_H | |
| #define QUICHE_QUIC_MOQT_TOOLS_CHAT_CLIENT_H | |
| #include <cstdint> | |
| #include <fstream> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include "absl/container/flat_hash_map.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/quic/core/io/quic_event_loop.h" | |
| #include "quiche/quic/core/quic_server_id.h" | |
| #include "quiche/quic/core/quic_time.h" | |
| #include "quiche/quic/moqt/moqt_known_track_publisher.h" | |
| #include "quiche/quic/moqt/moqt_messages.h" | |
| #include "quiche/quic/moqt/moqt_outgoing_queue.h" | |
| #include "quiche/quic/moqt/moqt_priority.h" | |
| #include "quiche/quic/moqt/moqt_session.h" | |
| #include "quiche/quic/moqt/moqt_track.h" | |
| #include "quiche/quic/moqt/tools/moq_chat.h" | |
| #include "quiche/quic/moqt/tools/moqt_client.h" | |
| #include "quiche/quic/tools/interactive_cli.h" | |
| #include "quiche/common/platform/api/quiche_export.h" | |
| namespace moqt { | |
| class ChatClient { | |
| public: | |
| ChatClient(const quic::QuicServerId& server_id, absl::string_view path, | |
| absl::string_view username, absl::string_view chat_id, | |
| bool ignore_certificate, absl::string_view output_file); | |
| void OnTerminalLineInput(absl::string_view input_message); | |
| bool session_is_open() const { return session_is_open_; } | |
| // Returns true if the client is still doing initial sync: retrieving the | |
| // catalog, subscribing to all the users in it, and waiting for the server | |
| // to subscribe to the local track. | |
| bool is_syncing() const { | |
| return !catalog_group_.has_value() || subscribes_to_make_ > 0 || | |
| (queue_ == nullptr || !queue_->HasSubscribers()); | |
| } | |
| void RunEventLoop() { | |
| event_loop_->RunEventLoopOnce(quic::QuicTime::Delta::FromMilliseconds(500)); | |
| } | |
| bool has_output_file() { return !output_filename_.empty(); } | |
| void WriteToFile(absl::string_view user, absl::string_view message) { | |
| output_file_ << user << ": " << message << "\n\n"; | |
| output_file_.flush(); | |
| } | |
| class QUICHE_EXPORT RemoteTrackVisitor : public moqt::RemoteTrack::Visitor { | |
| public: | |
| RemoteTrackVisitor(ChatClient* client) : client_(client) { | |
| cli_ = client->cli_.get(); | |
| } | |
| void OnReply(const moqt::FullTrackName& full_track_name, | |
| std::optional<absl::string_view> reason_phrase) override; | |
| void OnObjectFragment(const moqt::FullTrackName& full_track_name, | |
| uint64_t group_sequence, uint64_t object_sequence, | |
| moqt::MoqtPriority publisher_priority, | |
| moqt::MoqtObjectStatus status, | |
| moqt::MoqtForwardingPreference forwarding_preference, | |
| absl::string_view object, | |
| bool end_of_message) override; | |
| void set_cli(quic::InteractiveCli* cli) { cli_ = cli; } | |
| private: | |
| ChatClient* client_; | |
| quic::InteractiveCli* cli_; | |
| }; | |
| // Returns false on error. | |
| bool AnnounceAndSubscribe(); | |
| private: | |
| // Objects from the same catalog group arrive on the same stream, and in | |
| // object sequence order. | |
| void ProcessCatalog(absl::string_view object, | |
| moqt::RemoteTrack::Visitor* visitor, | |
| uint64_t group_sequence, uint64_t object_sequence); | |
| struct ChatUser { | |
| moqt::FullTrackName full_track_name; | |
| uint64_t from_group; | |
| ChatUser(const moqt::FullTrackName& ftn, uint64_t group) | |
| : full_track_name(ftn), from_group(group) {} | |
| }; | |
| // Basic session information | |
| const std::string username_; | |
| moqt::MoqChatStrings chat_strings_; | |
| // General state variables | |
| std::unique_ptr<quic::QuicEventLoop> event_loop_; | |
| bool session_is_open_ = false; | |
| moqt::MoqtSession* session_ = nullptr; | |
| moqt::MoqtKnownTrackPublisher publisher_; | |
| std::unique_ptr<moqt::MoqtClient> client_; | |
| moqt::MoqtSessionCallbacks session_callbacks_; | |
| // Related to syncing. | |
| std::optional<uint64_t> catalog_group_; | |
| absl::flat_hash_map<std::string, ChatUser> other_users_; | |
| int subscribes_to_make_ = 1; | |
| // Related to subscriptions/announces | |
| // TODO: One for each subscribe | |
| std::unique_ptr<RemoteTrackVisitor> remote_track_visitor_; | |
| // Handling outgoing messages | |
| std::shared_ptr<moqt::MoqtOutgoingQueue> queue_; | |
| // Used when chat output goes to file. | |
| std::ofstream output_file_; | |
| std::string output_filename_; | |
| // Used when there is no output file, and both input and output are in the | |
| // terminal. | |
| std::unique_ptr<quic::InteractiveCli> cli_; | |
| }; | |
| } // namespace moqt | |
| #endif // QUICHE_QUIC_MOQT_TOOLS_CHAT_CLIENT_H |