Add code documentation

This commit is contained in:
badaix 2025-01-30 20:47:09 +01:00
parent c2bebb4bae
commit b773ccda18
9 changed files with 116 additions and 63 deletions

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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<uint16_t>(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

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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<char*>(malloc(size * sizeof(char)));
}
/// d'tor
~CodecHeader() override
{
free(payload);
@ -54,8 +56,11 @@ public:
return static_cast<uint32_t>(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

View file

@ -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 <typename ToType>
static std::unique_ptr<ToType> message_cast(std::unique_ptr<msg::BaseMessage> message)
{
ToType* tmp = dynamic_cast<ToType*>(message.get());
std::unique_ptr<ToType> result;
auto* tmp = dynamic_cast<ToType*>(message.get());
if (tmp != nullptr)
{
message.release();
result.reset(tmp);
std::unique_ptr<ToType> result(tmp);
return result;
}
return nullptr;
@ -47,6 +48,7 @@ static std::unique_ptr<ToType> message_cast(std::unique_ptr<msg::BaseMessage> me
namespace factory
{
/// Create a message of type T from @p base_message beaser and payload @p buffer
template <typename T>
static std::unique_ptr<T> createMessage(const BaseMessage& base_message, char* buffer)
{
@ -57,6 +59,7 @@ static std::unique_ptr<T> 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<BaseMessage> createMessage(const BaseMessage& base_message, char* buffer)
{
std::unique_ptr<BaseMessage> result;

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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 <typename T>
T get(const std::string& what, const T& def) const
{
@ -75,7 +79,5 @@ protected:
}
}
};
} // namespace msg
#endif

View file

@ -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<char*>(outputBuffer), static_cast<char*>(payload) + format.frameSize() * idx_, format.frameSize() * result);
if (output_buffer != nullptr)
memcpy(static_cast<char*>(output_buffer), static_cast<char*>(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<int>(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<chronos::usec::rep>(1000000. * ((double)idx_ / (double)format.rate()))));
}
/// @return time of the last frame
inline chronos::time_point_clk end() const
{
return start() + durationLeft<chronos::usec>();
}
/// @return duration of this chunk
template <typename T>
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<char*>(realloc(payload, newSize));
payloadSize = newSize;
auto new_size = format.frameSize() * frame_count;
payload = static_cast<char*>(realloc(payload, new_size));
payloadSize = new_size;
}
/// @return duration of this chunk in [ms]
double durationMs() const
{
return static_cast<double>(getFrameCount()) / format.msRate();
}
/// @return time left, starting from the read pointer
template <typename T>
inline T durationLeft() const
{
return std::chrono::duration_cast<T>(chronos::nsec(static_cast<chronos::nsec::rep>(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

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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<uint16_t>(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

View file

@ -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 <http://www.gnu.org/licenses/>.
***/
#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