mirror of
https://github.com/badaix/snapcast.git
synced 2025-06-18 02:31:44 +02:00
Add documentation
This commit is contained in:
parent
f0985cbce4
commit
76e35e3e18
34 changed files with 199 additions and 78 deletions
|
@ -153,6 +153,7 @@ public:
|
|||
virtual void getNextMessage(const MessageHandler<msg::BaseMessage>& 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
|
||||
|
|
|
@ -129,7 +129,7 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings
|
|||
#endif // HAS_OPENSSL
|
||||
}
|
||||
|
||||
|
||||
/// Helper to create a player instance
|
||||
template <typename PlayerType>
|
||||
std::unique_ptr<Player> Controller::createPlayer(ClientSettings::Player& settings, const std::string& player_name)
|
||||
{
|
||||
|
|
|
@ -46,8 +46,8 @@ struct riff_wave_header
|
|||
/// See https://en.wikipedia.org/wiki/WAV
|
||||
struct chunk_header
|
||||
{
|
||||
uint32_t id;
|
||||
uint32_t sz;
|
||||
uint32_t id; ///< id
|
||||
uint32_t sz; ///< size
|
||||
};
|
||||
|
||||
|
||||
|
@ -55,12 +55,12 @@ struct chunk_header
|
|||
/// See https://en.wikipedia.org/wiki/WAV
|
||||
struct chunk_fmt
|
||||
{
|
||||
uint16_t audio_format; ///<
|
||||
uint16_t num_channels;
|
||||
uint32_t sample_rate;
|
||||
uint32_t byte_rate;
|
||||
uint16_t block_align;
|
||||
uint16_t bits_per_sample;
|
||||
uint16_t audio_format; ///< format
|
||||
uint16_t num_channels; ///< channels
|
||||
uint32_t sample_rate; ///< sample rate
|
||||
uint32_t byte_rate; ///< byte rate
|
||||
uint16_t block_align; ///< block align
|
||||
uint16_t bits_per_sample; ///< bps
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 T>
|
|||
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 @@ public:
|
|||
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 @@ public:
|
|||
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 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/// @return mean value
|
||||
double mean() const
|
||||
{
|
||||
if (buffer.empty())
|
||||
|
@ -88,6 +92,7 @@ public:
|
|||
return mean;
|
||||
}
|
||||
|
||||
/// @return @p percentile percentile
|
||||
T percentile(unsigned int percentile) const
|
||||
{
|
||||
if (buffer.empty())
|
||||
|
@ -97,6 +102,7 @@ public:
|
|||
return tmpBuffer[(size_t)((tmpBuffer.size() - 1) * ((float)percentile / (float)100))];
|
||||
}
|
||||
|
||||
/// @return array of different percentiles
|
||||
template <std::size_t Size>
|
||||
std::array<T, Size> percentiles(std::array<uint8_t, Size> percentiles) const
|
||||
{
|
||||
|
@ -112,31 +118,37 @@ public:
|
|||
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<T>& getBuffer() const
|
||||
{
|
||||
return buffer;
|
||||
|
|
|
@ -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
|
||||
|
@ -44,7 +44,9 @@ static constexpr auto ALSA = "alsa";
|
|||
class AlsaPlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
/// d'tor
|
||||
~AlsaPlayer() override;
|
||||
|
||||
void start() override;
|
||||
|
|
|
@ -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 CORE_AUDIO_PLAYER_HPP
|
||||
#define CORE_AUDIO_PLAYER_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
// local headers
|
||||
#include "player.hpp"
|
||||
|
@ -45,13 +45,17 @@ static constexpr auto COREAUDIO = "coreaudio";
|
|||
class CoreAudioPlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
CoreAudioPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
/// d'tor
|
||||
virtual ~CoreAudioPlayer();
|
||||
|
||||
/// Callback funtion for audio data to be played
|
||||
void playerCallback(AudioQueueRef queue, AudioQueueBufferRef bufferRef);
|
||||
/// @return list of available pcm devices
|
||||
static std::vector<PcmDevice> pcm_list();
|
||||
|
||||
protected:
|
||||
private:
|
||||
void worker() override;
|
||||
bool needsThread() const override;
|
||||
|
||||
|
@ -67,5 +71,3 @@ protected:
|
|||
};
|
||||
|
||||
} // namespace player
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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,9 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef FILE_PLAYER_HPP
|
||||
#define FILE_PLAYER_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
// local headers
|
||||
#include "player.hpp"
|
||||
|
@ -40,8 +41,10 @@ static constexpr auto FILE = "file";
|
|||
class FilePlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
FilePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
virtual ~FilePlayer();
|
||||
/// d'tor
|
||||
~FilePlayer() override;
|
||||
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
@ -49,7 +52,7 @@ public:
|
|||
/// List the dummy file PCM device
|
||||
static std::vector<PcmDevice> pcm_list(const std::string& parameter);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void requestAudio();
|
||||
void loop();
|
||||
bool needsThread() const override;
|
||||
|
@ -60,5 +63,3 @@ protected:
|
|||
};
|
||||
|
||||
} // namespace player
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 OPEN_SL_PLAYER_HPP
|
||||
#define OPEN_SL_PLAYER_HPP
|
||||
#pragma once
|
||||
|
||||
|
||||
// local headers
|
||||
#include "player.hpp"
|
||||
|
@ -35,7 +35,9 @@ namespace player
|
|||
|
||||
static constexpr auto OPENSL = "opensl";
|
||||
|
||||
typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
|
||||
|
||||
// typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
|
||||
using AndroidAudioCallback = int (*)(short*, int);
|
||||
|
||||
|
||||
/// OpenSL Audio Player
|
||||
|
@ -45,15 +47,18 @@ typedef int (*AndroidAudioCallback)(short* buffer, int num_samples);
|
|||
class OpenslPlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
OpenslPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
virtual ~OpenslPlayer();
|
||||
/// d'tor
|
||||
~OpenslPlayer() override;
|
||||
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
/// Callback to feed data to the player API
|
||||
void playerCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void initOpensl();
|
||||
void uninitOpensl();
|
||||
|
||||
|
@ -83,5 +88,3 @@ protected:
|
|||
};
|
||||
|
||||
} // namespace player
|
||||
|
||||
#endif
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
bool mute{false}; ///< muted?
|
||||
};
|
||||
|
||||
/// Callback for volume changes on the local client, to notify the server
|
||||
using volume_callback = std::function<void(const Volume& volume)>;
|
||||
|
||||
/// c'tor
|
||||
|
@ -89,7 +90,6 @@ protected:
|
|||
|
||||
/// set the hardware mixer volume
|
||||
/// @param volume the volume on range [0..1], muted or not
|
||||
/// @param muted muted or not
|
||||
virtual void setHardwareVolume(const Volume& volume);
|
||||
|
||||
/// set volume polynomial: volume^exp
|
||||
|
@ -102,7 +102,6 @@ protected:
|
|||
|
||||
/// Notify the server about hardware volume changes
|
||||
/// @param volume the volume in range [0..1]
|
||||
/// @param muted if muted or not
|
||||
void notifyVolumeChange(const Volume& volume) const
|
||||
{
|
||||
if (onVolumeChanged_)
|
||||
|
|
|
@ -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
|
||||
|
@ -42,7 +42,9 @@ static constexpr auto PULSE = "pulse";
|
|||
class PulsePlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
PulsePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
/// d'tor
|
||||
virtual ~PulsePlayer();
|
||||
|
||||
void start() override;
|
||||
|
@ -51,7 +53,7 @@ public:
|
|||
/// List the system's audio output devices
|
||||
static std::vector<PcmDevice> pcm_list(const std::string& parameter);
|
||||
|
||||
protected:
|
||||
private:
|
||||
bool needsThread() const override;
|
||||
void worker() override;
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ static constexpr auto LOG_TAG = "WASAPI";
|
|||
template <typename T>
|
||||
struct COMMemDeleter
|
||||
{
|
||||
/// Operator()
|
||||
void operator()(T* obj)
|
||||
{
|
||||
if (obj != NULL)
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef WASAPI_PLAYER_HPP
|
||||
#define WASAPI_PLAYER_HPP
|
||||
#pragma once
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
|
@ -42,31 +41,37 @@ class AudioSessionEventListener : public IAudioSessionEvents
|
|||
bool muted_ = false;
|
||||
|
||||
public:
|
||||
/// c'tor
|
||||
AudioSessionEventListener() : _cRef(1)
|
||||
{
|
||||
}
|
||||
|
||||
/// @return volume
|
||||
float getVolume()
|
||||
{
|
||||
return volume_;
|
||||
}
|
||||
|
||||
/// @return if muted
|
||||
bool getMuted()
|
||||
{
|
||||
return muted_;
|
||||
}
|
||||
|
||||
/// d'tor
|
||||
~AudioSessionEventListener()
|
||||
{
|
||||
}
|
||||
|
||||
// IUnknown methods -- AddRef, Release, and QueryInterface
|
||||
|
||||
/// documentation missing
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&_cRef);
|
||||
}
|
||||
|
||||
/// documentation missing
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
ULONG ulRef = InterlockedDecrement(&_cRef);
|
||||
|
@ -77,34 +82,42 @@ public:
|
|||
return ulRef;
|
||||
}
|
||||
|
||||
/// documentation missing
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface);
|
||||
|
||||
// Notification methods for audio session events
|
||||
|
||||
/// OnDisplayNameChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// OnIconPathChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// OnSimpleVolumeChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext);
|
||||
|
||||
/// OnChannelVolumeChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// OnGroupingParamChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// OnStateChanged callback
|
||||
HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState);
|
||||
|
||||
/// OnSessionDisconnected callback
|
||||
HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason);
|
||||
};
|
||||
|
||||
|
@ -117,19 +130,23 @@ class AudioEndpointVolumeCallback : public IAudioEndpointVolumeCallback
|
|||
bool muted_ = false;
|
||||
|
||||
public:
|
||||
/// c'tor
|
||||
AudioEndpointVolumeCallback() : _cRef(1)
|
||||
{
|
||||
}
|
||||
|
||||
/// d'tor
|
||||
~AudioEndpointVolumeCallback()
|
||||
{
|
||||
}
|
||||
|
||||
/// @return volume
|
||||
float getVolume()
|
||||
{
|
||||
return volume_;
|
||||
}
|
||||
|
||||
/// @return if muted
|
||||
bool getMuted()
|
||||
{
|
||||
return muted_;
|
||||
|
@ -137,11 +154,13 @@ public:
|
|||
|
||||
// IUnknown methods -- AddRef, Release, and QueryInterface
|
||||
|
||||
/// documentation missing
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&_cRef);
|
||||
}
|
||||
|
||||
/// documentation missing
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
ULONG ulRef = InterlockedDecrement(&_cRef);
|
||||
|
@ -152,6 +171,7 @@ public:
|
|||
return ulRef;
|
||||
}
|
||||
|
||||
/// documentation missing
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
|
||||
{
|
||||
if (IID_IUnknown == riid)
|
||||
|
@ -175,6 +195,7 @@ public:
|
|||
|
||||
// Callback method for endpoint-volume-change notifications.
|
||||
|
||||
/// documentation missing
|
||||
HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify);
|
||||
};
|
||||
|
||||
|
@ -184,14 +205,17 @@ static constexpr auto WASAPI = "wasapi";
|
|||
class WASAPIPlayer : public Player
|
||||
{
|
||||
public:
|
||||
/// c'tor
|
||||
WASAPIPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
/// d'tor
|
||||
virtual ~WASAPIPlayer();
|
||||
|
||||
/// @return list of available PCM devices
|
||||
static std::vector<PcmDevice> pcm_list();
|
||||
|
||||
protected:
|
||||
virtual void worker();
|
||||
virtual bool needsThread() const override
|
||||
void worker() override;
|
||||
bool needsThread() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -206,5 +230,3 @@ private:
|
|||
#pragma warning(pop)
|
||||
|
||||
} // namespace player
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<msg::PcmChunk> chunk);
|
||||
/// Remove all chunks from the queue
|
||||
void clearChunks();
|
||||
|
||||
/// Get PCM data, which will be played out in "outputBufferDacTime" time
|
||||
|
@ -68,11 +71,13 @@ public:
|
|||
/// "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:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/***
|
||||
This file is part of snapcast
|
||||
Copyright (C) 2014-2023 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
|
||||
|
@ -40,15 +40,19 @@
|
|||
class TimeProvider
|
||||
{
|
||||
public:
|
||||
/// @return singleton
|
||||
static TimeProvider& getInstance()
|
||||
{
|
||||
static TimeProvider instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Set diff to server
|
||||
void setDiffToServer(double ms);
|
||||
/// Set diff from round-trip-times client-to-server and server-to-client
|
||||
void setDiff(const tv& c2s, const tv& s2c);
|
||||
|
||||
/// @return time diff to server
|
||||
template <typename T>
|
||||
inline T getDiffToServer() const
|
||||
{
|
||||
|
@ -60,22 +64,26 @@ public:
|
|||
long getDiffToServerMs();
|
||||
*/
|
||||
|
||||
/// @return time since epoch
|
||||
template <typename T>
|
||||
static T sinceEpoche(const chronos::time_point_clk& point)
|
||||
{
|
||||
return std::chrono::duration_cast<T>(point.time_since_epoch());
|
||||
}
|
||||
|
||||
/// @return time_point from @p timeval
|
||||
static chronos::time_point_clk toTimePoint(const tv& timeval)
|
||||
{
|
||||
return chronos::time_point_clk(chronos::usec(timeval.usec) + chronos::sec(timeval.sec));
|
||||
}
|
||||
|
||||
/// @return local time
|
||||
inline static chronos::time_point_clk now()
|
||||
{
|
||||
return chronos::clk::now();
|
||||
}
|
||||
|
||||
/// @return server time
|
||||
inline static chronos::time_point_clk serverNow()
|
||||
{
|
||||
return chronos::clk::now() + TimeProvider::getInstance().getDiffToServer<chronos::usec>();
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
return static_cast<uint32_t>(sizeof(uint32_t) + msg.dump().size());
|
||||
}
|
||||
|
||||
/// the json message payload
|
||||
json msg;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -39,18 +39,21 @@ namespace msg
|
|||
class WireChunk : public BaseMessage
|
||||
{
|
||||
public:
|
||||
explicit WireChunk(uint32_t size = 0) : BaseMessage(message_type::kWireChunk), payloadSize(size), payload(nullptr)
|
||||
/// c'tor
|
||||
explicit WireChunk(uint32_t size = 0) : BaseMessage(message_type::kWireChunk), payloadSize(size)
|
||||
{
|
||||
if (size > 0)
|
||||
payload = static_cast<char*>(malloc(size * sizeof(char)));
|
||||
}
|
||||
|
||||
/// c'tor
|
||||
WireChunk(const WireChunk& wireChunk) : BaseMessage(message_type::kWireChunk), timestamp(wireChunk.timestamp), payloadSize(wireChunk.payloadSize)
|
||||
{
|
||||
payload = static_cast<char*>(malloc(payloadSize));
|
||||
memcpy(payload, wireChunk.payload, payloadSize);
|
||||
}
|
||||
|
||||
/// d'tor
|
||||
~WireChunk() override
|
||||
{
|
||||
free(payload);
|
||||
|
@ -68,15 +71,17 @@ public:
|
|||
return sizeof(tv) + sizeof(int32_t) + payloadSize;
|
||||
}
|
||||
|
||||
/// @return playout time of the chunk (server time)
|
||||
virtual chronos::time_point_clk start() const
|
||||
{
|
||||
return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec));
|
||||
}
|
||||
|
||||
tv timestamp;
|
||||
uint32_t payloadSize;
|
||||
char* payload;
|
||||
tv timestamp; ///< playout timestamp (server time)
|
||||
uint32_t payloadSize; ///< size of the payload
|
||||
char* payload{nullptr}; ///< raw chunk payload
|
||||
|
||||
/// @return tuple of payload and payload size
|
||||
template <typename T>
|
||||
std::pair<T*, size_t> getPayload() const
|
||||
{
|
||||
|
|
|
@ -30,6 +30,12 @@ template <typename T>
|
|||
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<std::mutex> mlock(mutex_);
|
||||
|
@ -41,6 +47,7 @@ public:
|
|||
return val;
|
||||
}
|
||||
|
||||
/// abort wait
|
||||
void abort_wait()
|
||||
{
|
||||
{
|
||||
|
@ -50,6 +57,7 @@ public:
|
|||
cond_.notify_one();
|
||||
}
|
||||
|
||||
/// wait for @p timeout for new element, @return if an element has been added
|
||||
bool wait_for(const std::chrono::microseconds& timeout) const
|
||||
{
|
||||
std::unique_lock<std::mutex> mlock(mutex_);
|
||||
|
@ -60,6 +68,7 @@ public:
|
|||
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<std::mutex> mlock(mutex_);
|
||||
|
@ -79,6 +88,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
/// return next element in @p item, wait for an element if queue is empty
|
||||
void pop(T& item)
|
||||
{
|
||||
std::unique_lock<std::mutex> mlock(mutex_);
|
||||
|
@ -89,6 +99,7 @@ public:
|
|||
queue_.pop_front();
|
||||
}
|
||||
|
||||
/// Add @p item to the queue
|
||||
void push_front(const T& item)
|
||||
{
|
||||
{
|
||||
|
@ -98,6 +109,7 @@ public:
|
|||
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<std::mutex> mlock(mutex_);
|
||||
|
@ -107,6 +119,7 @@ public:
|
|||
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<std::mutex> mlock(mutex_);
|
||||
|
@ -116,6 +129,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Add element @p item at the front of the queue
|
||||
void push_front(T&& item)
|
||||
{
|
||||
{
|
||||
|
@ -125,6 +139,7 @@ public:
|
|||
cond_.notify_one();
|
||||
}
|
||||
|
||||
/// Add element @p item at the end of the queue
|
||||
void push(const T& item)
|
||||
{
|
||||
{
|
||||
|
@ -134,6 +149,7 @@ public:
|
|||
cond_.notify_one();
|
||||
}
|
||||
|
||||
/// Add element @p item at the end of the queue
|
||||
void push(T&& item)
|
||||
{
|
||||
{
|
||||
|
@ -143,21 +159,19 @@ public:
|
|||
cond_.notify_one();
|
||||
}
|
||||
|
||||
/// @return number of elements in the queue
|
||||
size_t size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<T> queue_;
|
||||
mutable std::atomic<bool> abort_;
|
||||
|
|
|
@ -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 <cmath>
|
||||
|
@ -195,7 +197,7 @@ std::shared_ptr<msg::PcmChunk> Resampler::resample(const msg::PcmChunk& chunk)
|
|||
}
|
||||
|
||||
|
||||
shared_ptr<msg::PcmChunk> Resampler::resample(shared_ptr<msg::PcmChunk> chunk)
|
||||
std::shared_ptr<msg::PcmChunk> Resampler::resample(std::shared_ptr<msg::PcmChunk> chunk)
|
||||
{
|
||||
#ifndef HAS_SOXR
|
||||
return chunk;
|
||||
|
|
|
@ -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 = num-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<double>(rate_) / 1000.;
|
||||
}
|
||||
|
||||
/// @return rate per micro seconds (= rate / 1000000)
|
||||
inline double usRate() const
|
||||
{
|
||||
return static_cast<double>(rate_) / 1000000.;
|
||||
}
|
||||
|
||||
/// @return rate per nano seconds (= rate / 1000000000)
|
||||
inline double nsRate() const
|
||||
{
|
||||
return static_cast<double>(rate_) / 1000000000.;
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
/// Authenticate with basic scheme
|
||||
ErrorCode authenticateBasic(const std::string& credentials);
|
||||
/// Authenticate with <user>:<password>
|
||||
/// 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 @@ public:
|
|||
/// 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<std::string> 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;
|
||||
|
|
|
@ -146,7 +146,7 @@ void ControlServer::onMessageReceived(std::shared_ptr<ControlSession> session, c
|
|||
}
|
||||
|
||||
|
||||
void ControlServer::onNewSession(shared_ptr<ControlSession> session)
|
||||
void ControlServer::onNewSession(std::shared_ptr<ControlSession> session)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> mlock(session_mutex_);
|
||||
session->start();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "FLAC/stream_encoder.h"
|
||||
|
||||
// standard headers
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
|
|
@ -76,6 +76,7 @@ struct ServerSettings
|
|||
/// c'tor
|
||||
Authorization() = default;
|
||||
|
||||
/// Init with @p conf_roles and @p conf_users
|
||||
void init(const std::vector<std::string>& conf_roles, const std::vector<std::string>& conf_users)
|
||||
{
|
||||
roles.clear();
|
||||
|
|
|
@ -87,8 +87,9 @@ public:
|
|||
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
|
||||
|
|
|
@ -35,11 +35,11 @@ namespace streamreader
|
|||
/// Tage entry??
|
||||
struct TageEntry
|
||||
{
|
||||
std::string code;
|
||||
std::string type;
|
||||
std::string data;
|
||||
bool isBase64{false};
|
||||
int length{0};
|
||||
std::string code; ///< code
|
||||
std::string type; ///< type
|
||||
std::string data; ///< data
|
||||
bool isBase64{false}; ///< is base64?
|
||||
int length{0}; ///< length
|
||||
};
|
||||
|
||||
/// Starts shairport-sync and reads PCM data from stdout
|
||||
|
|
|
@ -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
|
||||
|
@ -47,7 +47,7 @@ public:
|
|||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
void do_read();
|
||||
void initAlsa();
|
||||
void uninitAlsa();
|
||||
|
|
|
@ -45,7 +45,9 @@ public:
|
|||
/// c'tor. Encoded PCM data is passed to the PipeListener
|
||||
AsioStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
|
||||
|
||||
/// Start the stream reader, init the encoder and optionally the stream control
|
||||
void start() override;
|
||||
/// Stop the stream reader
|
||||
void stop() override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -30,7 +30,9 @@ namespace detail
|
|||
struct category : public std::error_category
|
||||
{
|
||||
public:
|
||||
/// @return category name
|
||||
const char* name() const noexcept override;
|
||||
/// @return error message for @p value
|
||||
std::string message(int value) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/***
|
||||
This file is part of snapcast
|
||||
Copyright (C) 2014-2024 Johannes Pohl
|
||||
Copyright (C) 2014-2025 Johannes Pohl
|
||||
Copyright (C) 2024 Marcus Weseloh <marcus@weseloh.cc>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
bool openJackConnection();
|
||||
void closeJackConnection();
|
||||
bool createJackPorts();
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
/// ctor. Encoded PCM data is passed to the PipeListener
|
||||
LibrespotStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
|
||||
|
||||
protected:
|
||||
private:
|
||||
bool killall_;
|
||||
|
||||
void onStderrMsg(const std::string& line) override;
|
||||
|
|
|
@ -114,13 +114,13 @@ public:
|
|||
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<msg::PcmChunk> chunk, double duration) = 0;
|
||||
/// Stream @p pcmStream muissed to read audio with duration @p ms
|
||||
virtual void onResync(const PcmStream* pcmStream, double ms) = 0;
|
||||
};
|
||||
|
||||
|
||||
/// Handler function for command results
|
||||
using ResultHandler = std::function<void(const snapcast::ErrorCode& ec)>;
|
||||
|
||||
/// c'tor. Encoded PCM data is passed to the PcmStream::Listener
|
||||
|
|
|
@ -42,29 +42,45 @@ namespace streamreader
|
|||
{
|
||||
|
||||
|
||||
/// Stream control base class
|
||||
/// Controls a stream via "command" (play, pause, next, ...)
|
||||
/// Provides status information (playback status, position, metadata, ...)
|
||||
class StreamControl
|
||||
{
|
||||
public:
|
||||
/// Request handler coming from the plugin
|
||||
using OnRequest = std::function<void(const jsonrpcpp::Request& response)>;
|
||||
/// Notification handler coming from the plugin
|
||||
using OnNotification = std::function<void(const jsonrpcpp::Notification& response)>;
|
||||
/// Response handler coming from the plugin
|
||||
using OnResponse = std::function<void(const jsonrpcpp::Response& response)>;
|
||||
/// Log handler coming from the plugin
|
||||
using OnLog = std::function<void(std::string message)>;
|
||||
|
||||
/// 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 +92,14 @@ private:
|
|||
};
|
||||
|
||||
|
||||
/// 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:
|
||||
|
|
|
@ -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
|
||||
|
@ -39,10 +39,10 @@ namespace streamreader
|
|||
class TcpStream : public AsioStream<tcp::socket>
|
||||
{
|
||||
public:
|
||||
/// ctor. Encoded PCM data is passed to the PipeListener
|
||||
/// c'tor. Encoded PCM data is passed to the PipeListener
|
||||
TcpStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void connect() override;
|
||||
void disconnect() override;
|
||||
std::unique_ptr<tcp::acceptor> acceptor_;
|
||||
|
|
|
@ -36,10 +36,11 @@ class Watchdog;
|
|||
class Watchdog
|
||||
{
|
||||
public:
|
||||
/// Watchdog timeout handler
|
||||
using TimeoutHandler = std::function<void(std::chrono::milliseconds ms)>;
|
||||
|
||||
/// 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();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue