From 1b270f7c281e80e0a6a693f48a8df09a86adcd4c Mon Sep 17 00:00:00 2001 From: badaix Date: Sun, 16 Feb 2025 22:07:09 +0100 Subject: [PATCH] Add documentation --- client/client_connection.hpp | 1 + client/controller.cpp | 2 +- client/double_buffer.hpp | 18 +++++++++++++++--- client/stream.hpp | 5 +++++ common/queue.hpp | 22 ++++++++++++++++++---- common/resampler.cpp | 6 ++++-- common/sample_format.hpp | 17 +++++++++++++++-- server/authinfo.hpp | 4 ++-- server/control_server.cpp | 2 +- server/encoder/flac_encoder.hpp | 1 + server/server_settings.hpp | 1 + server/stream_session.hpp | 3 ++- server/streamreader/pcm_stream.hpp | 2 +- server/streamreader/stream_control.hpp | 16 ++++++++++++++++ server/streamreader/watchdog.hpp | 2 +- 15 files changed, 84 insertions(+), 18 deletions(-) diff --git a/client/client_connection.hpp b/client/client_connection.hpp index b84d3a76..4bca2820 100644 --- a/client/client_connection.hpp +++ b/client/client_connection.hpp @@ -153,6 +153,7 @@ class ClientConnection virtual void getNextMessage(const MessageHandler& handler) = 0; protected: + /// Send @p buffer, return result in @p write_handler virtual void write(boost::asio::streambuf& buffer, WriteHandler&& write_handler) = 0; /// Connect to @p endpoint diff --git a/client/controller.cpp b/client/controller.cpp index c888ff1c..dc0d7ba0 100644 --- a/client/controller.cpp +++ b/client/controller.cpp @@ -129,7 +129,7 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings #endif // HAS_OPENSSL } - +/// Helper to create a player instance template std::unique_ptr Controller::createPlayer(ClientSettings::Player& settings, const std::string& player_name) { diff --git a/client/double_buffer.hpp b/client/double_buffer.hpp index 9163bb21..7b24b12b 100644 --- a/client/double_buffer.hpp +++ b/client/double_buffer.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2024 Johannes Pohl + Copyright (C) 2014-2025 Johannes Pohl This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,10 +36,12 @@ template class DoubleBuffer { public: - DoubleBuffer(size_t size = 10) : bufferSize(size) + /// c'tor + explicit DoubleBuffer(size_t size = 10) : bufferSize(size) { } + /// Add @p element, pop last, if buffer is full inline void add(const T& element) { buffer.push_back(element); @@ -47,6 +49,7 @@ class DoubleBuffer buffer.pop_front(); } + /// Add @p element, pop last, if buffer is full inline void add(T&& element) { buffer.push_back(std::move(element)); @@ -54,7 +57,7 @@ class DoubleBuffer buffer.pop_front(); } - /// Median as mean over N values around the median + /// @return median as mean over N values around the median T median(uint16_t mean = 1) const { if (buffer.empty()) @@ -78,6 +81,7 @@ class DoubleBuffer } } + /// @return mean value double mean() const { if (buffer.empty()) @@ -88,6 +92,7 @@ class DoubleBuffer return mean; } + /// @return @p percentile percentile T percentile(unsigned int percentile) const { if (buffer.empty()) @@ -97,6 +102,7 @@ class DoubleBuffer return tmpBuffer[(size_t)((tmpBuffer.size() - 1) * ((float)percentile / (float)100))]; } + /// @return array of different percentiles template std::array percentiles(std::array percentiles) const { @@ -112,31 +118,37 @@ class DoubleBuffer return result; } + /// @return if the buffer is full inline bool full() const { return (buffer.size() == bufferSize); } + /// Clear the buffer inline void clear() { buffer.clear(); } + /// @return current size of the buffer inline size_t size() const { return buffer.size(); } + /// @return if the buffer is empty inline bool empty() const { return buffer.empty(); } + /// Set size of the buffer void setSize(size_t size) { bufferSize = size; } + /// @return the raw buffer const std::deque& getBuffer() const { return buffer; diff --git a/client/stream.hpp b/client/stream.hpp index 89f91afd..878d1bb7 100644 --- a/client/stream.hpp +++ b/client/stream.hpp @@ -45,11 +45,14 @@ class Stream { public: + /// c'tor Stream(const SampleFormat& in_format, const SampleFormat& out_format); + /// d'tor virtual ~Stream() = default; /// Adds PCM data to the queue void addChunk(std::unique_ptr chunk); + /// Remove all chunks from the queue void clearChunks(); /// Get PCM data, which will be played out in "outputBufferDacTime" time @@ -68,11 +71,13 @@ class Stream /// "Server buffer": playout latency, e.g. 1000ms void setBufferLen(size_t bufferLenMs); + /// @return sampleformat const SampleFormat& getFormat() const { return format_; } + /// @return if chunk was avabilable within @p timeout bool waitForChunk(const std::chrono::milliseconds& timeout) const; private: diff --git a/common/queue.hpp b/common/queue.hpp index 15df986d..f021bc98 100644 --- a/common/queue.hpp +++ b/common/queue.hpp @@ -30,6 +30,12 @@ template class Queue { public: + /// c'tor + Queue() = default; + Queue(const Queue&) = delete; ///< disable copying + Queue& operator=(const Queue&) = delete; ///< disable assignment + + /// @return next element, delete from queue T pop() { std::unique_lock mlock(mutex_); @@ -41,6 +47,7 @@ class Queue return val; } + /// abort wait void abort_wait() { { @@ -50,6 +57,7 @@ class Queue cond_.notify_one(); } + /// wait for @timeout for new element, @return if an element has been added bool wait_for(const std::chrono::microseconds& timeout) const { std::unique_lock mlock(mutex_); @@ -60,6 +68,7 @@ class Queue return !queue_.empty() && !abort_; } + /// @return if an element has been returned in @p item within @p timeout bool try_pop(T& item, const std::chrono::microseconds& timeout = std::chrono::microseconds(0)) { std::unique_lock mlock(mutex_); @@ -79,6 +88,7 @@ class Queue return true; } + /// return next element in @p item, wait for an element if queue is empty void pop(T& item) { std::unique_lock mlock(mutex_); @@ -89,6 +99,7 @@ class Queue queue_.pop_front(); } + /// Add @p item to the queue void push_front(const T& item) { { @@ -98,6 +109,7 @@ class Queue cond_.notify_one(); } + /// return a copy of the next element in @p copy, @return false if the queue is empty bool back_copy(T& copy) { std::lock_guard mlock(mutex_); @@ -107,6 +119,7 @@ class Queue return true; } + /// return a copy of the last element in @p copy, @return false if the queue is empty bool front_copy(T& copy) { std::lock_guard mlock(mutex_); @@ -116,6 +129,7 @@ class Queue return true; } + /// Add element @p item at the front of the queue void push_front(T&& item) { { @@ -125,6 +139,7 @@ class Queue cond_.notify_one(); } + /// Add element @p item at the end of the queue void push(const T& item) { { @@ -134,6 +149,7 @@ class Queue cond_.notify_one(); } + /// Add element @p item at the end of the queue void push(T&& item) { { @@ -143,21 +159,19 @@ class Queue cond_.notify_one(); } + /// @return number of elements in the queue size_t size() const { std::lock_guard mlock(mutex_); return queue_.size(); } + /// @return if the queue is empty bool empty() const { return (size() == 0); } - Queue() = default; - Queue(const Queue&) = delete; // disable copying - Queue& operator=(const Queue&) = delete; // disable assignment - private: std::deque queue_; mutable std::atomic abort_; diff --git a/common/resampler.cpp b/common/resampler.cpp index 9548c957..cf99a5b2 100644 --- a/common/resampler.cpp +++ b/common/resampler.cpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2024 Johannes Pohl + Copyright (C) 2014-2025 Johannes Pohl This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,9 @@ // local headers #include "common/aixlog.hpp" +#ifndef HAS_SOXR #include "common/snap_exception.hpp" +#endif // standard headers #include @@ -195,7 +197,7 @@ std::shared_ptr Resampler::resample(const msg::PcmChunk& chunk) } -shared_ptr Resampler::resample(shared_ptr chunk) +std::shared_ptr Resampler::resample(std::shared_ptr chunk) { #ifndef HAS_SOXR return chunk; diff --git a/common/sample_format.hpp b/common/sample_format.hpp index cb252533..96b5e2cc 100644 --- a/common/sample_format.hpp +++ b/common/sample_format.hpp @@ -38,57 +38,70 @@ class SampleFormat { public: + /// c'tor SampleFormat(); + /// c'tor SampleFormat(const std::string& format); + /// c'tor SampleFormat(uint32_t rate, uint16_t bits, uint16_t channels); + /// @return sampleformat as string rate:bits::channels std::string toString() const; + /// Set @p format (rate:bits::channels) void setFormat(const std::string& format); + /// Set format void setFormat(uint32_t rate, uint16_t bits, uint16_t channels); + /// @return if has format bool isInitialized() const { return ((rate_ != 0) || (bits_ != 0) || (channels_ != 0)); } + /// @return rate uint32_t rate() const { return rate_; } + /// @return bits uint16_t bits() const { return bits_; } + /// @return channels uint16_t channels() const { return channels_; } - // size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits) + /// @return size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits) uint16_t sampleSize() const { return sample_size_; } - // size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit) + /// @return size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit) uint16_t frameSize() const { return frame_size_; } + /// @return rate per ms (= rate / 1000) inline double msRate() const { return static_cast(rate_) / 1000.; } + /// @return rate per micro seconds (= rate / 1000000) inline double usRate() const { return static_cast(rate_) / 1000000.; } + /// @return rate per nano seconds (= rate / 1000000000) inline double nsRate() const { return static_cast(rate_) / 1000000000.; diff --git a/server/authinfo.hpp b/server/authinfo.hpp index 276e45ee..f7898cea 100644 --- a/server/authinfo.hpp +++ b/server/authinfo.hpp @@ -79,7 +79,7 @@ class AuthInfo /// Authenticate with basic scheme ErrorCode authenticateBasic(const std::string& credentials); - /// Authenticate with : + /// Authenticate with user:password ErrorCode authenticatePlain(const std::string& user_password); /// Authenticate with bearer scheme // ErrorCode authenticateBearer(const std::string& token); @@ -88,7 +88,7 @@ class AuthInfo /// Authenticate with scheme ("basic" or "bearer") and auth param ErrorCode authenticate(const std::string& scheme, const std::string& param); - /// @return JWS token for @p username and @p password + // @return JWS token for @p username and @p password // ErrorOr getToken(const std::string& username, const std::string& password) const; /// @return if the authenticated user has permission to access @p ressource bool hasPermission(const std::string& resource) const; diff --git a/server/control_server.cpp b/server/control_server.cpp index 6e2dfb44..23c5303f 100644 --- a/server/control_server.cpp +++ b/server/control_server.cpp @@ -146,7 +146,7 @@ void ControlServer::onMessageReceived(std::shared_ptr session, c } -void ControlServer::onNewSession(shared_ptr session) +void ControlServer::onNewSession(std::shared_ptr session) { std::lock_guard mlock(session_mutex_); session->start(); diff --git a/server/encoder/flac_encoder.hpp b/server/encoder/flac_encoder.hpp index a0cd6036..e514e7e7 100644 --- a/server/encoder/flac_encoder.hpp +++ b/server/encoder/flac_encoder.hpp @@ -25,6 +25,7 @@ #include "FLAC/stream_encoder.h" // standard headers +#include #include #include #include diff --git a/server/server_settings.hpp b/server/server_settings.hpp index 13f9d77a..e5a6548b 100644 --- a/server/server_settings.hpp +++ b/server/server_settings.hpp @@ -76,6 +76,7 @@ struct ServerSettings /// c'tor Authorization() = default; + /// Init with @p conf_roles and @p conf_users void init(const std::vector& conf_roles, const std::vector& conf_users) { roles.clear(); diff --git a/server/stream_session.hpp b/server/stream_session.hpp index 5a54b987..51c9165c 100644 --- a/server/stream_session.hpp +++ b/server/stream_session.hpp @@ -87,8 +87,9 @@ class shared_const_buffer buffer_ = boost::asio::buffer(message_->data); } - // Implement the ConstBufferSequence requirements. + /// const buffer. using value_type = boost::asio::const_buffer; + /// const buffer iterator using const_iterator = const boost::asio::const_buffer*; /// begin iterator diff --git a/server/streamreader/pcm_stream.hpp b/server/streamreader/pcm_stream.hpp index f2446ce0..464e5aa5 100644 --- a/server/streamreader/pcm_stream.hpp +++ b/server/streamreader/pcm_stream.hpp @@ -114,7 +114,7 @@ class PcmStream : public std::enable_shared_from_this virtual void onStateChanged(const PcmStream* pcmStream, ReaderState state) = 0; /// Chunk @p chunk of @p pcmStream has read virtual void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) = 0; - /// Chunk @p chunk with duration @p duration of stream @pcmStream has been encoded + /// Chunk @p chunk with duration @p duration of stream @p pcmStream has been encoded virtual void onChunkEncoded(const PcmStream* pcmStream, std::shared_ptr chunk, double duration) = 0; /// Stream @p pcmStream muissed to read audio with duration @p ms virtual void onResync(const PcmStream* pcmStream, double ms) = 0; diff --git a/server/streamreader/stream_control.hpp b/server/streamreader/stream_control.hpp index 32578fee..26591579 100644 --- a/server/streamreader/stream_control.hpp +++ b/server/streamreader/stream_control.hpp @@ -42,6 +42,9 @@ namespace streamreader { +/// Stream control base class +/// Controls a stream via "command" (play, pause, next, ...) +/// Provides status information (playback status, position, metadata, ...) class StreamControl { public: @@ -50,21 +53,30 @@ class StreamControl using OnResponse = std::function; using OnLog = std::function; + /// c'tor explicit StreamControl(const boost::asio::any_io_executor& executor); + /// d'tor virtual ~StreamControl() = default; + /// Start the stream control, calls abstract "doStart" void start(const std::string& stream_id, const ServerSettings& server_setttings, const OnNotification& notification_handler, const OnRequest& request_handler, const OnLog& log_handler); + /// Issue a command to the stream, calls abstract "doCommand" void command(const jsonrpcpp::Request& request, const OnResponse& response_handler); protected: + /// abstract "command" interface: send a json request to the plugin virtual void doCommand(const jsonrpcpp::Request& request) = 0; + /// abstract "start" interface: starts and initializes the plugin virtual void doStart(const std::string& stream_id, const ServerSettings& server_setttings) = 0; + /// a @p json message has been received from the plugin void onReceive(const std::string& json); + /// a @p message log request has been received from the plugin void onLog(std::string message); + /// asio executor boost::asio::any_io_executor executor_; private: @@ -76,10 +88,14 @@ class StreamControl }; +/// Script based stream control +/// Executes a script (e.g. Python) and communicates via stdout/stdin with the script class ScriptStreamControl : public StreamControl { public: + /// c'tor ScriptStreamControl(const boost::asio::any_io_executor& executor, const std::filesystem::path& plugin_dir, std::string script, std::string params); + /// d'tor virtual ~ScriptStreamControl() = default; private: diff --git a/server/streamreader/watchdog.hpp b/server/streamreader/watchdog.hpp index 0f6139bb..4f793f45 100644 --- a/server/streamreader/watchdog.hpp +++ b/server/streamreader/watchdog.hpp @@ -39,7 +39,7 @@ class Watchdog using TimeoutHandler = std::function; /// c'tor - explicit Watchdog(const boost::asio::any_io_executor& executor);//, WatchdogListener* listener = nullptr); + explicit Watchdog(const boost::asio::any_io_executor& executor); /// d'tor virtual ~Watchdog();