blob: b5cb7477d05396515d6357fa23d63b11e8f04894 [file] [log] [blame]
// 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