From b773ccda18f2baa5c2ded004b460da19353d4d5f Mon Sep 17 00:00:00 2001 From: badaix Date: Thu, 30 Jan 2025 20:47:09 +0100 Subject: [PATCH] Add code documentation --- changelog.md | 4 +-- common/message/client_info.hpp | 21 ++++++++++----- common/message/codec_header.hpp | 19 ++++++++------ common/message/factory.hpp | 11 +++++--- common/message/hello.hpp | 28 ++++++++++++++------ common/message/json_message.hpp | 16 +++++++----- common/message/pcm_chunk.hpp | 42 +++++++++++++++++++++--------- common/message/server_settings.hpp | 25 +++++++++++------- common/message/time.hpp | 13 ++++----- 9 files changed, 116 insertions(+), 63 deletions(-) diff --git a/changelog.md b/changelog.md index e7de162d..3ce8364d 100644 --- a/changelog.md +++ b/changelog.md @@ -4,8 +4,8 @@ ### Features -- Client: Add support for (secure-) websockets (Issue #1325) -- Server: Add client authentication (Issue #1334) +- Client: Add support for (secure-) websockets (Issue #1325, PR #1340) +- Server: Add client authentication (Issue #1334, PR #1340) ### Bugfixes diff --git a/common/message/client_info.hpp b/common/message/client_info.hpp index 06331841..2e388743 100644 --- a/common/message/client_info.hpp +++ b/common/message/client_info.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,7 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_CLIENT_INFO_HPP -#define MESSAGE_CLIENT_INFO_HPP +#pragma once // local headers #include "json_message.hpp" @@ -27,39 +26,47 @@ namespace msg { /// Client information sent from client to server -/// Might also be used for sync stats and latency estimations +/// Might also be used for other things in future, like +/// - sync stats +/// - latency estimations +/// - Battery status +/// - ... class ClientInfo : public JsonMessage { public: + /// c'tor ClientInfo() : JsonMessage(message_type::kClientInfo) { setVolume(100); setMuted(false); } + /// d'tor ~ClientInfo() override = default; + /// @return the volume in percent uint16_t getVolume() { return get("volume", static_cast(100)); } + /// @return if muted or not bool isMuted() { return get("muted", false); } + /// Set the volume to @p volume percent void setVolume(uint16_t volume) { msg["volume"] = volume; } + /// Set muted to @p muted void setMuted(bool muted) { msg["muted"] = muted; } }; + } // namespace msg - - -#endif diff --git a/common/message/codec_header.hpp b/common/message/codec_header.hpp index 3f91c971..743760c0 100644 --- a/common/message/codec_header.hpp +++ b/common/message/codec_header.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,8 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_CODEC_HEADER_HPP -#define MESSAGE_CODEC_HEADER_HPP +#pragma once + // local headers #include "message.hpp" @@ -31,13 +31,15 @@ namespace msg class CodecHeader : public BaseMessage { public: - explicit CodecHeader(const std::string& codecName = "", uint32_t size = 0) - : BaseMessage(message_type::kCodecHeader), payloadSize(size), payload(nullptr), codec(codecName) + /// c'tor taking the @p codec_name and @p site of the payload + explicit CodecHeader(const std::string& codec_name = "", uint32_t size = 0) + : BaseMessage(message_type::kCodecHeader), payloadSize(size), payload(nullptr), codec(codec_name) { if (size > 0) payload = static_cast(malloc(size * sizeof(char))); } + /// d'tor ~CodecHeader() override { free(payload); @@ -54,8 +56,11 @@ public: return static_cast(sizeof(uint32_t) + codec.size() + sizeof(uint32_t) + payloadSize); } + /// payload size uint32_t payloadSize; + /// the payload char* payload; + /// name of the codec std::string codec; protected: @@ -65,7 +70,5 @@ protected: writeVal(stream, payload, payloadSize); } }; + } // namespace msg - - -#endif diff --git a/common/message/factory.hpp b/common/message/factory.hpp index ac236ed6..767fe945 100644 --- a/common/message/factory.hpp +++ b/common/message/factory.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 @@ -30,15 +30,16 @@ namespace msg { +/// Cast a BaseMessage @message to type "ToType" +/// @return castest message or nullptr, if the cast failed template static std::unique_ptr message_cast(std::unique_ptr message) { - ToType* tmp = dynamic_cast(message.get()); - std::unique_ptr result; + auto* tmp = dynamic_cast(message.get()); if (tmp != nullptr) { message.release(); - result.reset(tmp); + std::unique_ptr result(tmp); return result; } return nullptr; @@ -47,6 +48,7 @@ static std::unique_ptr message_cast(std::unique_ptr me namespace factory { +/// Create a message of type T from @p base_message beaser and payload @p buffer template static std::unique_ptr createMessage(const BaseMessage& base_message, char* buffer) { @@ -57,6 +59,7 @@ static std::unique_ptr createMessage(const BaseMessage& base_message, char* b return result; } +/// Create a BaseMessage from @p base_message header and payload @p buffer static std::unique_ptr createMessage(const BaseMessage& base_message, char* buffer) { std::unique_ptr result; diff --git a/common/message/hello.hpp b/common/message/hello.hpp index 3deb161e..5e768319 100644 --- a/common/message/hello.hpp +++ b/common/message/hello.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,7 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_HELLO_HPP -#define MESSAGE_HELLO_HPP +#pragma once // local headers #include "common/str_compat.hpp" @@ -31,16 +30,20 @@ namespace msg { +/// Hello message +/// Initial message, sent from client to server class Hello : public JsonMessage { public: + /// c'tor Hello() : JsonMessage(message_type::kHello) { } - Hello(const std::string& macAddress, const std::string& id, size_t instance) : JsonMessage(message_type::kHello) + /// c'tor taking @p macAddress, @p id and @p instance + Hello(const std::string& mac_address, const std::string& id, size_t instance) : JsonMessage(message_type::kHello) { - msg["MAC"] = macAddress; + msg["MAC"] = mac_address; msg["HostName"] = ::getHostName(); msg["Version"] = VERSION; msg["ClientName"] = "Snapclient"; @@ -51,53 +54,64 @@ public: msg["SnapStreamProtocolVersion"] = 2; } + /// d'tor ~Hello() override = default; + /// @return the MAC address std::string getMacAddress() const { return msg["MAC"]; } + /// @return the host name std::string getHostName() const { return msg["HostName"]; } + /// @return the client version std::string getVersion() const { return msg["Version"]; } + /// @return the client name (e.g. "Snapclient") std::string getClientName() const { return msg["ClientName"]; } + /// @return the OS name std::string getOS() const { return msg["OS"]; } + /// @return the CPU architecture std::string getArch() const { return msg["Arch"]; } + /// @return the instance id int getInstance() const { return get("Instance", 1); } + /// @return the protocol version int getProtocolVersion() const { return get("SnapStreamProtocolVersion", 1); } + /// @return a unqiue machine ID std::string getId() const { return get("ID", getMacAddress()); } + /// @return a unqiue client ID std::string getUniqueId() const { std::string id = getId(); @@ -109,7 +123,5 @@ public: return id; } }; + } // namespace msg - - -#endif diff --git a/common/message/json_message.hpp b/common/message/json_message.hpp index 127a1d33..a9829ff2 100644 --- a/common/message/json_message.hpp +++ b/common/message/json_message.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,8 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_JSON_HPP -#define MESSAGE_JSON_HPP +#pragma once + // local headers #include "common/json.hpp" @@ -30,13 +30,16 @@ using json = nlohmann::json; namespace msg { +/// Base class of a message with json payload class JsonMessage : public BaseMessage { public: - JsonMessage(message_type msgType) : BaseMessage(msgType) + /// c'tor taking the @p msg_type + explicit JsonMessage(message_type msg_type) : BaseMessage(msg_type) { } + /// d'tor ~JsonMessage() override = default; void read(std::istream& stream) override @@ -60,6 +63,7 @@ protected: writeVal(stream, msg.dump()); } + /// @return value for key @p what or @p def, if not found template T get(const std::string& what, const T& def) const { @@ -75,7 +79,5 @@ protected: } } }; + } // namespace msg - - -#endif diff --git a/common/message/pcm_chunk.hpp b/common/message/pcm_chunk.hpp index df4e66aa..5454d5c2 100644 --- a/common/message/pcm_chunk.hpp +++ b/common/message/pcm_chunk.hpp @@ -38,15 +38,17 @@ namespace msg class PcmChunk : public WireChunk { public: - PcmChunk(const SampleFormat& sampleFormat, uint32_t ms) - : WireChunk((sampleFormat.rate() * ms / 1000) * sampleFormat.frameSize()), format(sampleFormat), idx_(0) + /// c'tor, construct from @p sample_format with duration @p ms + PcmChunk(const SampleFormat& sample_format, uint32_t ms) : WireChunk((sample_format.rate() * ms / 1000) * sample_format.frameSize()), format(sample_format) { } - PcmChunk() : WireChunk(), idx_(0) + /// c'tor + PcmChunk() : WireChunk() { } + /// d'tor ~PcmChunk() override = default; #if 0 @@ -73,16 +75,18 @@ public: // return result; // } - int readFrames(void* outputBuffer, uint32_t frameCount) + /// Read the next @p frame_count frames into @p output_buffer, update the internal read position + /// @return number of frames copied to @p output_buffer + int readFrames(void* output_buffer, uint32_t frame_count) { // logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize()) << ", idx: " << idx;// << "\n"; - int result = frameCount; - if (idx_ + frameCount > (payloadSize / format.frameSize())) + int result = frame_count; + if (idx_ + frame_count > (payloadSize / format.frameSize())) result = (payloadSize / format.frameSize()) - idx_; // logd << ", from: " << format.frameSize()*idx << ", to: " << format.frameSize()*idx + format.frameSize()*result; - if (outputBuffer != nullptr) - memcpy(static_cast(outputBuffer), static_cast(payload) + format.frameSize() * idx_, format.frameSize() * result); + if (output_buffer != nullptr) + memcpy(static_cast(output_buffer), static_cast(payload) + format.frameSize() * idx_, format.frameSize() * result); idx_ += result; // logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize(): " << @@ -90,6 +94,8 @@ public: return result; } + /// seek @p frames forward or backward + /// @return the new read position int seek(int frames) { if ((frames < 0) && (-frames > static_cast(idx_))) @@ -102,17 +108,20 @@ public: return idx_; } + /// @return start time of the current frame chronos::time_point_clk start() const override { return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec) + chronos::usec(static_cast(1000000. * ((double)idx_ / (double)format.rate())))); } + /// @return time of the last frame inline chronos::time_point_clk end() const { return start() + durationLeft(); } + /// @return duration of this chunk template inline T duration() const { @@ -127,42 +136,51 @@ public: // payloadSize = newSize; // } - void setFrameCount(int frameCount) + /// Set the @p frame_count, reserve memory + void setFrameCount(int frame_count) { - auto newSize = format.frameSize() * frameCount; - payload = static_cast(realloc(payload, newSize)); - payloadSize = newSize; + auto new_size = format.frameSize() * frame_count; + payload = static_cast(realloc(payload, new_size)); + payloadSize = new_size; } + /// @return duration of this chunk in [ms] double durationMs() const { return static_cast(getFrameCount()) / format.msRate(); } + /// @return time left, starting from the read pointer template inline T durationLeft() const { return std::chrono::duration_cast(chronos::nsec(static_cast(1000000 * (getFrameCount() - idx_) / format.msRate()))); } + /// @return true if the read pointer is at the end inline bool isEndOfChunk() const { return idx_ >= getFrameCount(); } + /// @return number of frames inline uint32_t getFrameCount() const { return (payloadSize / format.frameSize()); } + /// @return number of samples inline uint32_t getSampleCount() const { return (payloadSize / format.sampleSize()); } + /// Sample format of this chunk SampleFormat format; private: + /// current read position (frame idx) uint32_t idx_ = 0; }; + } // namespace msg diff --git a/common/message/server_settings.hpp b/common/message/server_settings.hpp index 0979118a..cff96977 100644 --- a/common/message/server_settings.hpp +++ b/common/message/server_settings.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,7 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_SERVER_SETTINGS_HPP -#define MESSAGE_SERVER_SETTINGS_HPP +#pragma once // local headers #include "json_message.hpp" @@ -26,9 +25,11 @@ namespace msg { +/// Dynamic settings that affect the client class ServerSettings : public JsonMessage { public: + /// c'tor ServerSettings() : JsonMessage(message_type::kServerSettings) { setBufferMs(0); @@ -37,51 +38,57 @@ public: setMuted(false); } + /// d'tor ~ServerSettings() override = default; + /// @return the end to end delay in [ms] int32_t getBufferMs() { return get("bufferMs", 0); } + /// @return client specific additional latency in [ms] int32_t getLatency() { return get("latency", 0); } + /// @return the volume in [%] uint16_t getVolume() { return get("volume", static_cast(100)); } + /// @return if muted bool isMuted() { return get("muted", false); } - - void setBufferMs(int32_t bufferMs) + /// Set the end to end delay to @p buffer_ms [ms] + void setBufferMs(int32_t buffer_ms) { - msg["bufferMs"] = bufferMs; + msg["bufferMs"] = buffer_ms; } + /// Set the additional client specific @p latency [ms] void setLatency(int32_t latency) { msg["latency"] = latency; } + /// Set the @p volume [%] void setVolume(uint16_t volume) { msg["volume"] = volume; } + /// Set client to @p muted void setMuted(bool muted) { msg["muted"] = muted; } }; + } // namespace msg - - -#endif diff --git a/common/message/time.hpp b/common/message/time.hpp index 3abc18bb..52cf8c7b 100644 --- a/common/message/time.hpp +++ b/common/message/time.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2022 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 @@ -16,8 +16,7 @@ along with this program. If not, see . ***/ -#ifndef MESSAGE_TIME_HPP -#define MESSAGE_TIME_HPP +#pragma once // local headers #include "message.hpp" @@ -25,13 +24,16 @@ namespace msg { +/// Time sync message, send from client to server and back class Time : public BaseMessage { public: + /// c'tor Time() : BaseMessage(message_type::kTime) { } + /// d'tor ~Time() override = default; void read(std::istream& stream) override @@ -45,6 +47,7 @@ public: return sizeof(tv); } + /// The latency after round trip "client => server => client" tv latency; protected: @@ -54,7 +57,5 @@ protected: writeVal(stream, latency.usec); } }; + } // namespace msg - - -#endif