Reformat code

This commit is contained in:
badaix 2025-01-09 15:06:35 +01:00
parent 58e82ef9e8
commit d5677b6f78
31 changed files with 236 additions and 330 deletions

View file

@ -198,17 +198,16 @@ bool BrowseBonjour::browse(const string& serviceName, mDNSResult& result, int /*
deque<mDNSReply> replyCollection; deque<mDNSReply> replyCollection;
{ {
DNSServiceHandle service(new DNSServiceRef(NULL)); DNSServiceHandle service(new DNSServiceRef(NULL));
CHECKED(DNSServiceBrowse( CHECKED(DNSServiceBrowse(service.get(), 0, 0, serviceName.c_str(), "local.",
service.get(), 0, 0, serviceName.c_str(), "local.", [](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t /*interfaceIndex*/, DNSServiceErrorType errorCode,
[](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t /*interfaceIndex*/, DNSServiceErrorType errorCode, const char* serviceName, const char* serviceName, const char* regtype, const char* replyDomain, void* context)
const char* regtype, const char* replyDomain, void* context) {
{
auto replyCollection = static_cast<deque<mDNSReply>*>(context); auto replyCollection = static_cast<deque<mDNSReply>*>(context);
CHECKED(errorCode); CHECKED(errorCode);
replyCollection->push_back(mDNSReply{string(serviceName), string(regtype), string(replyDomain)}); replyCollection->push_back(mDNSReply{string(serviceName), string(regtype), string(replyDomain)});
}, },
&replyCollection)); &replyCollection));
runService(service); runService(service);
} }
@ -218,17 +217,17 @@ bool BrowseBonjour::browse(const string& serviceName, mDNSResult& result, int /*
{ {
DNSServiceHandle service(new DNSServiceRef(NULL)); DNSServiceHandle service(new DNSServiceRef(NULL));
for (auto& reply : replyCollection) for (auto& reply : replyCollection)
CHECKED(DNSServiceResolve( CHECKED(DNSServiceResolve(service.get(), 0, 0, reply.name.c_str(), reply.regtype.c_str(), reply.domain.c_str(),
service.get(), 0, 0, reply.name.c_str(), reply.regtype.c_str(), reply.domain.c_str(), [](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t /*interfaceIndex*/, DNSServiceErrorType errorCode,
[](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t /*interfaceIndex*/, DNSServiceErrorType errorCode, const char* /*fullName*/, const char* /*fullName*/, const char* hosttarget, uint16_t port, uint16_t /*txtLen*/,
const char* hosttarget, uint16_t port, uint16_t /*txtLen*/, const unsigned char* /*txtRecord*/, void* context) const unsigned char* /*txtRecord*/, void* context)
{ {
auto resultCollection = static_cast<deque<mDNSResolve>*>(context); auto resultCollection = static_cast<deque<mDNSResolve>*>(context);
CHECKED(errorCode); CHECKED(errorCode);
resultCollection->push_back(mDNSResolve{string(hosttarget), ntohs(port)}); resultCollection->push_back(mDNSResolve{string(hosttarget), ntohs(port)});
}, },
&resolveCollection)); &resolveCollection));
runService(service); runService(service);
} }
@ -241,11 +240,10 @@ bool BrowseBonjour::browse(const string& serviceName, mDNSResult& result, int /*
for (auto& resolve : resolveCollection) for (auto& resolve : resolveCollection)
{ {
resultCollection[i].port = resolve.port; resultCollection[i].port = resolve.port;
CHECKED(DNSServiceGetAddrInfo( CHECKED(DNSServiceGetAddrInfo(service.get(), kDNSServiceFlagsLongLivedQuery, 0, kDNSServiceProtocol_IPv4, resolve.fullName.c_str(),
service.get(), kDNSServiceFlagsLongLivedQuery, 0, kDNSServiceProtocol_IPv4, resolve.fullName.c_str(), [](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t interfaceIndex, DNSServiceErrorType /*errorCode*/,
[](DNSServiceRef /*service*/, DNSServiceFlags /*flags*/, uint32_t interfaceIndex, DNSServiceErrorType /*errorCode*/, const char* hostname, const char* hostname, const sockaddr* address, uint32_t /*ttl*/, void* context)
const sockaddr* address, uint32_t /*ttl*/, void* context) {
{
auto result = static_cast<mDNSResult*>(context); auto result = static_cast<mDNSResult*>(context);
result->host = string(hostname); result->host = string(hostname);
@ -259,8 +257,8 @@ bool BrowseBonjour::browse(const string& serviceName, mDNSResult& result, int /*
else else
return; return;
result->valid = true; result->valid = true;
}, },
&resultCollection[i++])); &resultCollection[i++]));
} }
runService(service); runService(service);
} }

View file

@ -49,9 +49,8 @@ PendingRequest::~PendingRequest()
void PendingRequest::setValue(std::unique_ptr<msg::BaseMessage> value) void PendingRequest::setValue(std::unique_ptr<msg::BaseMessage> value)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, self = shared_from_this(), val = std::move(value)]() mutable
[this, self = shared_from_this(), val = std::move(value)]() mutable {
{
timer_.cancel(); timer_.cancel();
if (handler_) if (handler_)
handler_({}, std::move(val)); handler_({}, std::move(val));
@ -66,9 +65,8 @@ uint16_t PendingRequest::id() const
void PendingRequest::startTimer(const chronos::usec& timeout) void PendingRequest::startTimer(const chronos::usec& timeout)
{ {
timer_.expires_after(timeout); timer_.expires_after(timeout);
timer_.async_wait( timer_.async_wait([this, self = shared_from_this()](boost::system::error_code ec)
[this, self = shared_from_this()](boost::system::error_code ec) {
{
if (!handler_) if (!handler_)
return; return;
if (!ec) if (!ec)
@ -212,9 +210,8 @@ void ClientConnection::sendNext()
message.msg->serialize(stream); message.msg->serialize(stream);
auto handler = message.handler; auto handler = message.handler;
boost::asio::async_write(socket_, streambuf, boost::asio::async_write(socket_, streambuf, [this, handler](boost::system::error_code ec, std::size_t length)
[this, handler](boost::system::error_code ec, std::size_t length) {
{
if (ec) if (ec)
LOG(ERROR, LOG_TAG) << "Failed to send message, error: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Failed to send message, error: " << ec.message() << "\n";
else else
@ -232,9 +229,8 @@ void ClientConnection::sendNext()
void ClientConnection::send(const msg::message_ptr& message, const ResultHandler& handler) void ClientConnection::send(const msg::message_ptr& message, const ResultHandler& handler)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, message, handler]()
[this, message, handler]() {
{
messages_.emplace_back(message, handler); messages_.emplace_back(message, handler);
if (messages_.size() > 1) if (messages_.size() > 1)
{ {
@ -248,9 +244,8 @@ void ClientConnection::send(const msg::message_ptr& message, const ResultHandler
void ClientConnection::sendRequest(const msg::message_ptr& message, const chronos::usec& timeout, const MessageHandler<msg::BaseMessage>& handler) void ClientConnection::sendRequest(const msg::message_ptr& message, const chronos::usec& timeout, const MessageHandler<msg::BaseMessage>& handler)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, message, timeout, handler]()
[this, message, timeout, handler]() {
{
pendingRequests_.erase( pendingRequests_.erase(
std::remove_if(pendingRequests_.begin(), pendingRequests_.end(), [](std::weak_ptr<PendingRequest> request) { return request.expired(); }), std::remove_if(pendingRequests_.begin(), pendingRequests_.end(), [](std::weak_ptr<PendingRequest> request) { return request.expired(); }),
pendingRequests_.end()); pendingRequests_.end());
@ -261,9 +256,8 @@ void ClientConnection::sendRequest(const msg::message_ptr& message, const chrono
auto request = make_shared<PendingRequest>(strand_, reqId_, handler); auto request = make_shared<PendingRequest>(strand_, reqId_, handler);
pendingRequests_.push_back(request); pendingRequests_.push_back(request);
request->startTimer(timeout); request->startTimer(timeout);
send(message, send(message, [handler](const boost::system::error_code& ec)
[handler](const boost::system::error_code& ec) {
{
if (ec) if (ec)
handler(ec, nullptr); handler(ec, nullptr);
}); });
@ -273,9 +267,8 @@ void ClientConnection::sendRequest(const msg::message_ptr& message, const chrono
void ClientConnection::getNextMessage(const MessageHandler<msg::BaseMessage>& handler) void ClientConnection::getNextMessage(const MessageHandler<msg::BaseMessage>& handler)
{ {
boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_msg_size_), boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_msg_size_), [this, handler](boost::system::error_code ec, std::size_t length) mutable
[this, handler](boost::system::error_code ec, std::size_t length) mutable {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error reading message header of length " << length << ": " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error reading message header of length " << length << ": " << ec.message() << "\n";
@ -309,7 +302,7 @@ void ClientConnection::getNextMessage(const MessageHandler<msg::BaseMessage>& ha
boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_message_.size), boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_message_.size),
[this, handler](boost::system::error_code ec, std::size_t length) mutable [this, handler](boost::system::error_code ec, std::size_t length) mutable
{ {
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error reading message body of length " << length << ": " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error reading message body of length " << length << ": " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -111,9 +111,8 @@ public:
template <typename Message> template <typename Message>
void sendRequest(const msg::message_ptr& message, const chronos::usec& timeout, const MessageHandler<Message>& handler) void sendRequest(const msg::message_ptr& message, const chronos::usec& timeout, const MessageHandler<Message>& handler)
{ {
sendRequest(message, timeout, sendRequest(message, timeout, [handler](const boost::system::error_code& ec, std::unique_ptr<msg::BaseMessage> response)
[handler](const boost::system::error_code& ec, std::unique_ptr<msg::BaseMessage> response) {
{
if (ec) if (ec)
handler(ec, nullptr); handler(ec, nullptr);
else if (auto casted_response = msg::message_cast<Message>(std::move(response))) else if (auto casted_response = msg::message_cast<Message>(std::move(response)))

View file

@ -122,9 +122,8 @@ std::vector<std::string> Controller::getSupportedPlayerNames()
void Controller::getNextMessage() void Controller::getNextMessage()
{ {
clientConnection_->getNextMessage( clientConnection_->getNextMessage([this](const boost::system::error_code& ec, std::unique_ptr<msg::BaseMessage> response)
[this](const boost::system::error_code& ec, std::unique_ptr<msg::BaseMessage> response) {
{
if (ec) if (ec)
{ {
reconnect(); reconnect();
@ -227,9 +226,8 @@ void Controller::getNextMessage()
if (!player_) if (!player_)
throw SnapException("No audio player support" + (settings_.player.player_name.empty() ? "" : " for: " + settings_.player.player_name)); throw SnapException("No audio player support" + (settings_.player.player_name.empty() ? "" : " for: " + settings_.player.player_name));
player_->setVolumeCallback( player_->setVolumeCallback([this](const Player::Volume& volume)
[this](const Player::Volume& volume) {
{
// Cache the last volume and check if it really changed in the player's volume callback // Cache the last volume and check if it really changed in the player's volume callback
static Player::Volume last_volume{-1, true}; static Player::Volume last_volume{-1, true};
if (volume != last_volume) if (volume != last_volume)
@ -238,9 +236,8 @@ void Controller::getNextMessage()
auto info = std::make_shared<msg::ClientInfo>(); auto info = std::make_shared<msg::ClientInfo>();
info->setVolume(static_cast<uint16_t>(volume.volume * 100.)); info->setVolume(static_cast<uint16_t>(volume.volume * 100.));
info->setMuted(volume.mute); info->setMuted(volume.mute);
clientConnection_->send(info, clientConnection_->send(info, [this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Failed to send client info, error: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Failed to send client info, error: " << ec.message() << "\n";
@ -272,7 +269,7 @@ void Controller::sendTimeSyncMessage(int quick_syncs)
auto timeReq = std::make_shared<msg::Time>(); auto timeReq = std::make_shared<msg::Time>();
clientConnection_->sendRequest<msg::Time>(timeReq, 2s, clientConnection_->sendRequest<msg::Time>(timeReq, 2s,
[this, quick_syncs](const boost::system::error_code& ec, const std::unique_ptr<msg::Time>& response) mutable [this, quick_syncs](const boost::system::error_code& ec, const std::unique_ptr<msg::Time>& response) mutable
{ {
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Time sync request failed: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Time sync request failed: " << ec.message() << "\n";
@ -293,9 +290,8 @@ void Controller::sendTimeSyncMessage(int quick_syncs)
next = 100us; next = 100us;
} }
timer_.expires_after(next); timer_.expires_after(next);
timer_.async_wait( timer_.async_wait([this, quick_syncs](const boost::system::error_code& ec)
[this, quick_syncs](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
sendTimeSyncMessage(quick_syncs); sendTimeSyncMessage(quick_syncs);
@ -327,9 +323,8 @@ void Controller::browseMdns(const MdnsHandler& handler)
} }
timer_.expires_after(500ms); timer_.expires_after(500ms);
timer_.async_wait( timer_.async_wait([this, handler](const boost::system::error_code& ec)
[this, handler](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
browseMdns(handler); browseMdns(handler);
@ -348,9 +343,8 @@ void Controller::start()
{ {
if (settings_.server.host.empty()) if (settings_.server.host.empty())
{ {
browseMdns( browseMdns([this](const boost::system::error_code& ec, const std::string& host, uint16_t port)
[this](const boost::system::error_code& ec, const std::string& host, uint16_t port) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Failed to browse MDNS, error: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Failed to browse MDNS, error: " << ec.message() << "\n";
@ -387,9 +381,8 @@ void Controller::reconnect()
stream_.reset(); stream_.reset();
decoder_.reset(); decoder_.reset();
timer_.expires_after(1s); timer_.expires_after(1s);
timer_.async_wait( timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
worker(); worker();
@ -399,9 +392,8 @@ void Controller::reconnect()
void Controller::worker() void Controller::worker()
{ {
clientConnection_->connect( clientConnection_->connect([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
// LOG(INFO, LOG_TAG) << "Connected!\n"; // LOG(INFO, LOG_TAG) << "Connected!\n";
@ -412,9 +404,8 @@ void Controller::worker()
// Say hello to the server // Say hello to the server
auto hello = std::make_shared<msg::Hello>(macAddress, settings_.host_id, settings_.instance); auto hello = std::make_shared<msg::Hello>(macAddress, settings_.host_id, settings_.instance);
clientConnection_->sendRequest<msg::ServerSettings>( clientConnection_->sendRequest<msg::ServerSettings>(
hello, 2s, hello, 2s, [this](const boost::system::error_code& ec, std::unique_ptr<msg::ServerSettings> response) mutable
[this](const boost::system::error_code& ec, std::unique_ptr<msg::ServerSettings> response) mutable {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Failed to send hello request, error: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Failed to send hello request, error: " << ec.message() << "\n";
@ -427,7 +418,7 @@ void Controller::worker()
LOG(INFO, LOG_TAG) << "ServerSettings - buffer: " << serverSettings_->getBufferMs() << ", latency: " << serverSettings_->getLatency() LOG(INFO, LOG_TAG) << "ServerSettings - buffer: " << serverSettings_->getBufferMs() << ", latency: " << serverSettings_->getLatency()
<< ", volume: " << serverSettings_->getVolume() << ", muted: " << serverSettings_->isMuted() << "\n"; << ", volume: " << serverSettings_->getVolume() << ", muted: " << serverSettings_->isMuted() << "\n";
} }
}); });
// Do initial time sync with the server // Do initial time sync with the server
sendTimeSyncMessage(50); sendTimeSyncMessage(50);

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -42,7 +42,7 @@ static constexpr std::chrono::milliseconds BUFFER_TIME = 80ms;
static constexpr int PERIODS = 4; static constexpr int PERIODS = 4;
static constexpr int MIN_PERIODS = 3; static constexpr int MIN_PERIODS = 3;
#define exp10(x) (exp((x)*log(10))) #define exp10(x) (exp((x) * log(10)))
static constexpr auto LOG_TAG = "Alsa"; static constexpr auto LOG_TAG = "Alsa";
@ -191,9 +191,8 @@ bool AlsaPlayer::getHardwareVolume(Volume& volume)
void AlsaPlayer::waitForEvent() void AlsaPlayer::waitForEvent()
{ {
sd_.async_wait(boost::asio::posix::stream_descriptor::wait_read, sd_.async_wait(boost::asio::posix::stream_descriptor::wait_read, [this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
// TODO: fd is "Bad" after unplugging/plugging USB DAC, i.e. after init/uninit/init cycle // TODO: fd is "Bad" after unplugging/plugging USB DAC, i.e. after init/uninit/init cycle
@ -226,9 +225,8 @@ void AlsaPlayer::waitForEvent()
// As workaround we defer getting the volume by 20ms. // As workaround we defer getting the volume by 20ms.
timer_.cancel(); timer_.cancel();
timer_.expires_after(20ms); timer_.expires_after(20ms);
timer_.async_wait( timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
if (getHardwareVolume(volume_)) if (getHardwareVolume(volume_))
@ -256,9 +254,8 @@ void AlsaPlayer::initMixer()
throw SnapException("Can't open control for " + mixer_device_ + ", error: " + snd_strerror(err)); throw SnapException("Can't open control for " + mixer_device_ + ", error: " + snd_strerror(err));
if ((err = snd_ctl_subscribe_events(ctl_, 1)) < 0) if ((err = snd_ctl_subscribe_events(ctl_, 1)) < 0)
throw SnapException("Can't subscribe for events for " + mixer_device_ + ", error: " + snd_strerror(err)); throw SnapException("Can't subscribe for events for " + mixer_device_ + ", error: " + snd_strerror(err));
fd_ = std::unique_ptr<pollfd, std::function<void(pollfd*)>>(new pollfd(), fd_ = std::unique_ptr<pollfd, std::function<void(pollfd*)>>(new pollfd(), [](pollfd* p)
[](pollfd* p) {
{
close(p->fd); close(p->fd);
delete p; delete p;
}); });

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -53,7 +53,7 @@ std::vector<PcmDevice> FilePlayer::pcm_list(const std::string& parameter)
FilePlayer::FilePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream) FilePlayer::FilePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream)
: Player(io_context, settings, stream), timer_(io_context), file_(nullptr) : Player(io_context, settings, std::move(stream)), timer_(io_context), file_(nullptr)
{ {
auto params = utils::string::split_pairs(settings.parameter, ',', '='); auto params = utils::string::split_pairs(settings.parameter, ',', '=');
string filename; string filename;
@ -130,9 +130,8 @@ void FilePlayer::loop()
next_request_ = now + 1ms; next_request_ = now + 1ms;
timer_.expires_at(next_request_); timer_.expires_at(next_request_);
timer_.async_wait( timer_.async_wait([this](boost::system::error_code ec)
[this](boost::system::error_code ec) {
{
if (ec) if (ec)
return; return;
requestAudio(); requestAudio();

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -245,7 +245,7 @@ void Player::setVolume(const Volume& volume)
bp::args = {"--volume", cpt::to_string(volume.volume), "--mute", volume.mute ? "true" : "false"}, bp::args = {"--volume", cpt::to_string(volume.volume), "--mute", volume.mute ? "true" : "false"},
bp::on_exit( bp::on_exit(
[&](int ret_val, std::error_code ec) [&](int ret_val, std::error_code ec)
{ {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
LOG(DEBUG, LOG_TAG) << "Error code: " << ec.message() << ", i: " << ret_val << "\n"; LOG(DEBUG, LOG_TAG) << "Error code: " << ec.message() << ", i: " << ret_val << "\n";
if (pending_volume_change.has_value()) if (pending_volume_change.has_value())
@ -255,7 +255,7 @@ void Player::setVolume(const Volume& volume)
lock.unlock(); lock.unlock();
setVolume(v); setVolume(v);
} }
}), }),
io_context_); io_context_);
} }
catch (const std::exception& e) catch (const std::exception& e)

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -53,9 +53,8 @@ vector<PcmDevice> PulsePlayer::pcm_list(const std::string& parameter)
{ {
auto pa_ml = std::shared_ptr<pa_mainloop>(pa_mainloop_new(), [](pa_mainloop* pa_ml) { pa_mainloop_free(pa_ml); }); auto pa_ml = std::shared_ptr<pa_mainloop>(pa_mainloop_new(), [](pa_mainloop* pa_ml) { pa_mainloop_free(pa_ml); });
pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml.get()); pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml.get());
auto pa_ctx = std::shared_ptr<pa_context>(pa_context_new(pa_mlapi, "Snapcast"), auto pa_ctx = std::shared_ptr<pa_context>(pa_context_new(pa_mlapi, "Snapcast"), [](pa_context* pa_ctx)
[](pa_context* pa_ctx) {
{
pa_context_disconnect(pa_ctx); pa_context_disconnect(pa_ctx);
pa_context_unref(pa_ctx); pa_context_unref(pa_ctx);
}); });
@ -69,10 +68,8 @@ vector<PcmDevice> PulsePlayer::pcm_list(const std::string& parameter)
throw SnapException("Failed to connect to PulseAudio context, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get())))); throw SnapException("Failed to connect to PulseAudio context, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get()))));
static int pa_ready = 0; static int pa_ready = 0;
pa_context_set_state_callback( pa_context_set_state_callback(pa_ctx.get(), [](pa_context* c, void* userdata)
pa_ctx.get(), {
[](pa_context* c, void* userdata)
{
std::ignore = userdata; std::ignore = userdata;
pa_context_state_t state = pa_context_get_state(c); pa_context_state_t state = pa_context_get_state(c);
switch (state) switch (state)
@ -87,8 +84,7 @@ vector<PcmDevice> PulsePlayer::pcm_list(const std::string& parameter)
default: default:
break; break;
} }
}, }, nullptr);
nullptr);
// We can't do anything until PA is ready, so just iterate the mainloop // We can't do anything until PA is ready, so just iterate the mainloop
// and continue // and continue
@ -107,18 +103,15 @@ vector<PcmDevice> PulsePlayer::pcm_list(const std::string& parameter)
throw SnapException("PulseAudio context failed, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get())))); throw SnapException("PulseAudio context failed, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get()))));
static std::vector<PcmDevice> devices; static std::vector<PcmDevice> devices;
auto* op = pa_context_get_sink_info_list( auto* op = pa_context_get_sink_info_list(pa_ctx.get(), [](pa_context* ctx, const pa_sink_info* i, int eol, void* userdata) mutable
pa_ctx.get(), {
[](pa_context* ctx, const pa_sink_info* i, int eol, void* userdata) mutable
{
std::ignore = ctx; std::ignore = ctx;
std::ignore = userdata; std::ignore = userdata;
// auto self = static_cast<PulsePlayer*>(userdata); // auto self = static_cast<PulsePlayer*>(userdata);
// If eol is set to a positive number, you're at the end of the list // If eol is set to a positive number, you're at the end of the list
if (eol <= 0) if (eol <= 0)
devices.emplace_back(i->index, i->name, i->description); devices.emplace_back(i->index, i->name, i->description);
}, }, nullptr);
nullptr);
if (op == nullptr) if (op == nullptr)
throw SnapException("PulseAudio get sink info list failed, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get())))); throw SnapException("PulseAudio get sink info list failed, error: " + std::string(pa_strerror(pa_context_errno(pa_ctx.get()))));
@ -246,10 +239,8 @@ bool PulsePlayer::getHardwareVolume(Volume& volume)
void PulsePlayer::triggerVolumeUpdate() void PulsePlayer::triggerVolumeUpdate()
{ {
pa_context_get_sink_input_info( pa_context_get_sink_input_info(pa_ctx_, pa_stream_get_index(playstream_), [](pa_context* ctx, const pa_sink_input_info* info, int eol, void* userdata)
pa_ctx_, pa_stream_get_index(playstream_), {
[](pa_context* ctx, const pa_sink_input_info* info, int eol, void* userdata)
{
std::ignore = ctx; std::ignore = ctx;
LOG(DEBUG, LOG_TAG) << "pa_context_get_sink_info_by_index info: " << (info != nullptr) << ", eol: " << eol << "\n"; LOG(DEBUG, LOG_TAG) << "pa_context_get_sink_info_by_index info: " << (info != nullptr) << ", eol: " << eol << "\n";
if (info != nullptr) if (info != nullptr)
@ -269,8 +260,7 @@ void PulsePlayer::triggerVolumeUpdate()
} }
self->notifyVolumeChange(self->volume_); self->notifyVolumeChange(self->volume_);
} }
}, }, this);
this);
} }
@ -433,14 +423,11 @@ void PulsePlayer::connect()
// modify the variable to 1 so we know when we have a connection and it's // modify the variable to 1 so we know when we have a connection and it's
// ready. // ready.
// If there's an error, the callback will set pa_ready to 2 // If there's an error, the callback will set pa_ready to 2
pa_context_set_state_callback( pa_context_set_state_callback(pa_ctx_, [](pa_context* c, void* userdata)
pa_ctx_, {
[](pa_context* c, void* userdata)
{
auto* self = static_cast<PulsePlayer*>(userdata); auto* self = static_cast<PulsePlayer*>(userdata);
self->stateCallback(c); self->stateCallback(c);
}, }, this);
this);
// We can't do anything until PA is ready, so just iterate the mainloop // We can't do anything until PA is ready, so just iterate the mainloop
// and continue // and continue
@ -464,47 +451,35 @@ void PulsePlayer::connect()
if (settings_.mixer.mode == ClientSettings::Mixer::Mode::hardware) if (settings_.mixer.mode == ClientSettings::Mixer::Mode::hardware)
{ {
pa_context_set_subscribe_callback( pa_context_set_subscribe_callback(pa_ctx_, [](pa_context* ctx, pa_subscription_event_type_t event_type, uint32_t idx, void* userdata)
pa_ctx_, {
[](pa_context* ctx, pa_subscription_event_type_t event_type, uint32_t idx, void* userdata)
{
auto* self = static_cast<PulsePlayer*>(userdata); auto* self = static_cast<PulsePlayer*>(userdata);
self->subscribeCallback(ctx, event_type, idx); self->subscribeCallback(ctx, event_type, idx);
}, }, this);
this);
const auto mask = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK_INPUT); const auto mask = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK_INPUT);
pa_context_subscribe( pa_context_subscribe(pa_ctx_, mask, [](pa_context* ctx, int success, void* userdata)
pa_ctx_, mask, {
[](pa_context* ctx, int success, void* userdata)
{
std::ignore = ctx; std::ignore = ctx;
if (success != 0) if (success != 0)
{ {
auto* self = static_cast<PulsePlayer*>(userdata); auto* self = static_cast<PulsePlayer*>(userdata);
self->triggerVolumeUpdate(); self->triggerVolumeUpdate();
} }
}, }, this);
this);
} }
pa_stream_set_write_callback( pa_stream_set_write_callback(playstream_, [](pa_stream* stream, size_t length, void* userdata)
playstream_, {
[](pa_stream* stream, size_t length, void* userdata)
{
auto* self = static_cast<PulsePlayer*>(userdata); auto* self = static_cast<PulsePlayer*>(userdata);
self->writeCallback(stream, length); self->writeCallback(stream, length);
}, }, this);
this);
pa_stream_set_underflow_callback( pa_stream_set_underflow_callback(playstream_, [](pa_stream* stream, void* userdata)
playstream_, {
[](pa_stream* stream, void* userdata)
{
auto* self = static_cast<PulsePlayer*>(userdata); auto* self = static_cast<PulsePlayer*>(userdata);
self->underflowCallback(stream); self->underflowCallback(stream);
}, }, this);
this);
bufattr_.fragsize = pa_usec_to_bytes(latency_.count(), &pa_ss_); bufattr_.fragsize = pa_usec_to_bytes(latency_.count(), &pa_ss_);
bufattr_.maxlength = pa_usec_to_bytes(latency_.count(), &pa_ss_); bufattr_.maxlength = pa_usec_to_bytes(latency_.count(), &pa_ss_);

View file

@ -62,8 +62,7 @@ static constexpr auto LOG_TAG = "Snapclient";
PcmDevice getPcmDevice(const std::string& player, const std::string& parameter, const std::string& soundcard) PcmDevice getPcmDevice(const std::string& player, const std::string& parameter, const std::string& soundcard)
{ {
LOG(DEBUG, LOG_TAG) << "Trying to get PCM device for player: " << player << ", parameter: " LOG(DEBUG, LOG_TAG) << "Trying to get PCM device for player: " << player << ", parameter: " << ", card: " << soundcard << "\n";
<< ", card: " << soundcard << "\n";
#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI) #if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI)
vector<PcmDevice> pcm_devices; vector<PcmDevice> pcm_devices;
#if defined(HAS_ALSA) #if defined(HAS_ALSA)
@ -430,9 +429,8 @@ int main(int argc, char** argv)
boost::asio::io_context io_context; boost::asio::io_context io_context;
// Construct a signal set registered for process termination. // Construct a signal set registered for process termination.
boost::asio::signal_set signals(io_context, SIGHUP, SIGINT, SIGTERM); boost::asio::signal_set signals(io_context, SIGHUP, SIGINT, SIGTERM);
signals.async_wait( signals.async_wait([&](const boost::system::error_code& ec, int signal)
[&](const boost::system::error_code& ec, int signal) {
{
if (!ec) if (!ec)
LOG(INFO, LOG_TAG) << "Received signal " << signal << ": " << strsignal(signal) << "\n"; LOG(INFO, LOG_TAG) << "Received signal " << signal << ": " << strsignal(signal) << "\n";
else else

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -66,9 +66,8 @@ namespace strutils = utils::string;
#ifndef WINDOWS #ifndef WINDOWS
static std::string execGetOutput(const std::string& cmd) static std::string execGetOutput(const std::string& cmd)
{ {
std::shared_ptr<::FILE> pipe(popen((cmd + " 2> /dev/null").c_str(), "r"), std::shared_ptr<::FILE> pipe(popen((cmd + " 2> /dev/null").c_str(), "r"), [](::FILE* stream)
[](::FILE* stream) {
{
if (stream != nullptr) if (stream != nullptr)
pclose(stream); pclose(stream);
}); });
@ -441,15 +440,15 @@ static std::string getHostId(const std::string& defaultId = "")
result = getProp("ro.serialno"); result = getProp("ro.serialno");
#endif #endif
//#else // #else
// // on embedded platforms it's // // on embedded platforms it's
// // - either not there // // - either not there
// // - or not unique, or changes during boot // // - or not unique, or changes during boot
// // - or changes during boot // // - or changes during boot
// std::ifstream infile("/var/lib/dbus/machine-id"); // std::ifstream infile("/var/lib/dbus/machine-id");
// if (infile.good()) // if (infile.good())
// std::getline(infile, result); // std::getline(infile, result);
//#endif // #endif
strutils::trim(result); strutils::trim(result);
if (!result.empty()) if (!result.empty())
return result; return result;

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -48,12 +48,11 @@ ControlServer::ControlServer(boost::asio::io_context& io_context, const ServerSe
boost::asio::ssl::context::single_dh_use); boost::asio::ssl::context::single_dh_use);
if (!ssl.key_password.empty()) if (!ssl.key_password.empty())
{ {
ssl_context_.set_password_callback( ssl_context_.set_password_callback([pw = ssl.key_password](size_t max_length, boost::asio::ssl::context_base::password_purpose purpose) -> string
[pw = ssl.key_password](size_t max_length, boost::asio::ssl::context_base::password_purpose purpose) -> string {
{
LOG(DEBUG, LOG_TAG) << "getPassword, purpose: " << purpose << ", max length: " << max_length << "\n"; LOG(DEBUG, LOG_TAG) << "getPassword, purpose: " << purpose << ", max length: " << max_length << "\n";
return pw; return pw;
}); });
} }
if (!ssl.certificate.empty() && !ssl.certificate_key.empty()) if (!ssl.certificate.empty() && !ssl.certificate_key.empty())
{ {

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -174,9 +174,8 @@ void ControlSessionHttp::start()
LOG(DEBUG, LOG_TAG) << "start\n"; LOG(DEBUG, LOG_TAG) << "start\n";
if (is_ssl_) if (is_ssl_)
{ {
ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, [this, self = shared_from_this()](const boost::system::error_code& error)
[this, self = shared_from_this()](const boost::system::error_code& error) {
{
LOG(DEBUG, LOG_TAG) << "async_handshake\n"; LOG(DEBUG, LOG_TAG) << "async_handshake\n";
if (error) if (error)
{ {
@ -264,9 +263,8 @@ void ControlSessionHttp::handle_request(http::request<Body, http::basic_fields<A
return send(bad_request("Illegal request-target")); return send(bad_request("Illegal request-target"));
std::string request = req.body(); std::string request = req.body();
return message_receiver_->onMessageReceived(shared_from_this(), request, return message_receiver_->onMessageReceived(shared_from_this(), request, [req = std::move(req), send = std::move(send)](const std::string& response)
[req = std::move(req), send = std::move(send)](const std::string& response) {
{
http::response<http::string_body> res{http::status::ok, req.version()}; http::response<http::string_body> res{http::status::ok, req.version()};
res.set(http::field::server, HTTP_SERVER_NAME); res.set(http::field::server, HTTP_SERVER_NAME);
res.set(http::field::content_type, "application/json"); res.set(http::field::content_type, "application/json");
@ -363,9 +361,8 @@ void ControlSessionHttp::on_read(beast::error_code ec, std::size_t bytes_transfe
if ((ec == http::error::end_of_stream) || (ec == boost::asio::error::connection_reset)) if ((ec == http::error::end_of_stream) || (ec == boost::asio::error::connection_reset))
{ {
if (is_ssl_) if (is_ssl_)
ssl_socket_->async_shutdown( ssl_socket_->async_shutdown([](const boost::system::error_code& error)
[](const boost::system::error_code& error) {
{
if (error.failed()) if (error.failed())
LOG(ERROR, LOG_TAG) << "Failed to shudown ssl socket: " << error << "\n"; LOG(ERROR, LOG_TAG) << "Failed to shudown ssl socket: " << error << "\n";
}); });
@ -395,9 +392,8 @@ void ControlSessionHttp::on_read(beast::error_code ec, std::size_t bytes_transfe
// Create a WebSocket session by transferring the socket // Create a WebSocket session by transferring the socket
auto ws = std::make_shared<websocket::stream<ssl_socket>>(std::move(*ssl_socket_)); auto ws = std::make_shared<websocket::stream<ssl_socket>>(std::move(*ssl_socket_));
// Accept the websocket handshake // Accept the websocket handshake
ws->async_accept(req_, ws->async_accept(req_, [this, ws, self = shared_from_this()](beast::error_code ec) mutable
[this, ws, self = shared_from_this()](beast::error_code ec) mutable {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error during WebSocket accept (control): " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error during WebSocket accept (control): " << ec.message() << "\n";
@ -422,9 +418,8 @@ void ControlSessionHttp::on_read(beast::error_code ec, std::size_t bytes_transfe
// Create a WebSocket session by transferring the socket // Create a WebSocket session by transferring the socket
auto ws = std::make_shared<websocket::stream<tcp_socket>>(std::move(*tcp_socket_)); auto ws = std::make_shared<websocket::stream<tcp_socket>>(std::move(*tcp_socket_));
// Accept the websocket handshake // Accept the websocket handshake
ws->async_accept(req_, ws->async_accept(req_, [this, ws, self = shared_from_this()](beast::error_code ec) mutable
[this, ws, self = shared_from_this()](beast::error_code ec) mutable {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error during WebSocket accept (control): " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error during WebSocket accept (control): " << ec.message() << "\n";
@ -459,9 +454,8 @@ void ControlSessionHttp::on_read(beast::error_code ec, std::size_t bytes_transfe
} }
// Send the response // Send the response
handle_request(std::move(req_), handle_request(std::move(req_), [this](auto&& response)
[this](auto&& response) {
{
// The lifetime of the message has to extend // The lifetime of the message has to extend
// for the duration of the async operation so // for the duration of the async operation so
// we use a shared_ptr to manage it. // we use a shared_ptr to manage it.
@ -470,13 +464,11 @@ void ControlSessionHttp::on_read(beast::error_code ec, std::size_t bytes_transfe
// Write the response // Write the response
if (is_ssl_) if (is_ssl_)
http::async_write(this->ssl_socket_.value(), *sp, http::async_write(this->ssl_socket_.value(), *sp, [this, self = this->shared_from_this(), sp](beast::error_code ec, std::size_t bytes)
[this, self = this->shared_from_this(), sp](beast::error_code ec, std::size_t bytes) { this->on_write(ec, bytes, sp->need_eof()); });
{ this->on_write(ec, bytes, sp->need_eof()); });
else else
http::async_write(this->tcp_socket_.value(), *sp, http::async_write(this->tcp_socket_.value(), *sp, [this, self = this->shared_from_this(), sp](beast::error_code ec, std::size_t bytes)
[this, self = this->shared_from_this(), sp](beast::error_code ec, std::size_t bytes) { this->on_write(ec, bytes, sp->need_eof()); });
{ this->on_write(ec, bytes, sp->need_eof()); });
}); });
} }

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -54,7 +54,7 @@ void ControlSessionTcp::do_read()
const std::string delimiter = "\n"; const std::string delimiter = "\n";
boost::asio::async_read_until(socket_, streambuf_, delimiter, boost::asio::async_read_until(socket_, streambuf_, delimiter,
[this, self = shared_from_this(), delimiter](const std::error_code& ec, std::size_t bytes_transferred) [this, self = shared_from_this(), delimiter](const std::error_code& ec, std::size_t bytes_transferred)
{ {
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error while reading from control socket: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error while reading from control socket: " << ec.message() << "\n";
@ -70,9 +70,8 @@ void ControlSessionTcp::do_read()
// LOG(DEBUG, LOG_TAG) << "received: " << line << "\n"; // LOG(DEBUG, LOG_TAG) << "received: " << line << "\n";
if ((message_receiver_ != nullptr) && !line.empty()) if ((message_receiver_ != nullptr) && !line.empty())
{ {
message_receiver_->onMessageReceived(shared_from_this(), line, message_receiver_->onMessageReceived(shared_from_this(), line, [this](const std::string& response)
[this](const std::string& response) {
{
if (!response.empty()) if (!response.empty())
sendAsync(response); sendAsync(response);
}); });
@ -106,9 +105,8 @@ void ControlSessionTcp::stop()
void ControlSessionTcp::sendAsync(const std::string& message) void ControlSessionTcp::sendAsync(const std::string& message)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, self = shared_from_this(), message]()
[this, self = shared_from_this(), message]() {
{
messages_.emplace_back(message + "\r\n"); messages_.emplace_back(message + "\r\n");
if (messages_.size() > 1) if (messages_.size() > 1)
{ {
@ -121,9 +119,8 @@ void ControlSessionTcp::sendAsync(const std::string& message)
void ControlSessionTcp::send_next() void ControlSessionTcp::send_next()
{ {
boost::asio::async_write(socket_, boost::asio::buffer(messages_.front()), boost::asio::async_write(socket_, boost::asio::buffer(messages_.front()), [this, self = shared_from_this()](std::error_code ec, std::size_t length)
[this, self = shared_from_this()](std::error_code ec, std::size_t length) {
{
messages_.pop_front(); messages_.pop_front();
if (ec) if (ec)
{ {

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -73,9 +73,8 @@ void ControlSessionWebsocket::stop()
void ControlSessionWebsocket::sendAsync(const std::string& message) void ControlSessionWebsocket::sendAsync(const std::string& message)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, self = shared_from_this(), msg = message]() mutable
[this, self = shared_from_this(), msg = message]() mutable {
{
messages_.push_back(std::move(msg)); messages_.push_back(std::move(msg));
if (messages_.size() > 1) if (messages_.size() > 1)
{ {
@ -149,9 +148,8 @@ void ControlSessionWebsocket::on_read_ws(beast::error_code ec, std::size_t bytes
// LOG(DEBUG, LOG_TAG) << "received: " << line << "\n"; // LOG(DEBUG, LOG_TAG) << "received: " << line << "\n";
if (message_receiver_ != nullptr) if (message_receiver_ != nullptr)
{ {
message_receiver_->onMessageReceived(shared_from_this(), line, message_receiver_->onMessageReceived(shared_from_this(), line, [this](const std::string& response)
[this](const std::string& response) {
{
if (!response.empty()) if (!response.empty())
{ {
sendAsync(response); sendAsync(response);

View file

@ -1,7 +1,7 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2015 Hannes Ellinger Copyright (C) 2015 Hannes Ellinger
Copyright (C) 2016-2024 Johannes Pohl Copyright (C) 2016-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -204,8 +204,7 @@ void OpusEncoder::encode(const msg::PcmChunk& chunk)
if (remainder_->payloadSize < remainder_max_size_) if (remainder_->payloadSize < remainder_max_size_)
{ {
LOG(DEBUG, LOG_TAG) << "not enough data to encode (" << remainder_->payloadSize << " of " << remainder_max_size_ << " bytes)" LOG(DEBUG, LOG_TAG) << "not enough data to encode (" << remainder_->payloadSize << " of " << remainder_max_size_ << " bytes)" << "\n";
<< "\n";
return; return;
} }
encode(out->format, remainder_->payload, remainder_->payloadSize); encode(out->format, remainder_->payload, remainder_->payloadSize);

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -69,9 +69,8 @@ void PublishAvahi::publish(const std::vector<mDNSService>& services)
void PublishAvahi::poll() void PublishAvahi::poll()
{ {
timer_.expires_after(std::chrono::milliseconds(50)); timer_.expires_after(std::chrono::milliseconds(50));
timer_.async_wait( timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec && (avahi_simple_poll_iterate(simple_poll, 0) == 0)) if (!ec && (avahi_simple_poll_iterate(simple_poll, 0) == 0))
poll(); poll();
}); });

View file

@ -189,7 +189,7 @@ void Server::onMessageReceived(std::shared_ptr<ControlSession> controlSession, c
jsonrpcpp::request_ptr request = dynamic_pointer_cast<jsonrpcpp::Request>(entity); jsonrpcpp::request_ptr request = dynamic_pointer_cast<jsonrpcpp::Request>(entity);
processRequest(request, controlSession->authinfo, processRequest(request, controlSession->authinfo,
[this, controlSession, response_handler](jsonrpcpp::entity_ptr response, jsonrpcpp::notification_ptr notification) [this, controlSession, response_handler](jsonrpcpp::entity_ptr response, jsonrpcpp::notification_ptr notification)
{ {
// if (controlSession->authinfo.hasAuthInfo()) // if (controlSession->authinfo.hasAuthInfo())
// { // {
// LOG(INFO, LOG_TAG) << "Request auth info - username: " << controlSession->authinfo->username() // LOG(INFO, LOG_TAG) << "Request auth info - username: " << controlSession->authinfo->username()
@ -227,7 +227,7 @@ void Server::onMessageReceived(std::shared_ptr<ControlSession> controlSession, c
processRequest(request, controlSession->authinfo, processRequest(request, controlSession->authinfo,
[controlSession, response_handler, &responseBatch, &notificationBatch](jsonrpcpp::entity_ptr response, [controlSession, response_handler, &responseBatch, &notificationBatch](jsonrpcpp::entity_ptr response,
jsonrpcpp::notification_ptr notification) jsonrpcpp::notification_ptr notification)
{ {
if (response != nullptr) if (response != nullptr)
responseBatch.add_ptr(response); responseBatch.add_ptr(response);
if (notification != nullptr) if (notification != nullptr)
@ -381,9 +381,8 @@ void Server::saveConfig(const std::chrono::milliseconds& deferred)
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
config_timer_.cancel(); config_timer_.cancel();
config_timer_.expires_after(deferred); config_timer_.expires_after(deferred);
config_timer_.async_wait( config_timer_.async_wait([](const boost::system::error_code& ec)
[](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
LOG(DEBUG, LOG_TAG) << "Saving config\n"; LOG(DEBUG, LOG_TAG) << "Saving config\n";

View file

@ -83,11 +83,12 @@ int main(int argc, char* argv[])
// SSL settings // SSL settings
conf.add<Value<std::filesystem::path>>("", "ssl.certificate", "certificate file (PEM format)", settings.ssl.certificate, &settings.ssl.certificate); conf.add<Value<std::filesystem::path>>("", "ssl.certificate", "certificate file (PEM format)", settings.ssl.certificate, &settings.ssl.certificate);
conf.add<Value<std::filesystem::path>>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key, &settings.ssl.certificate_key); conf.add<Value<std::filesystem::path>>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key,
&settings.ssl.certificate_key);
conf.add<Value<string>>("", "ssl.key_password", "key password (for encrypted private key)", settings.ssl.key_password, &settings.ssl.key_password); conf.add<Value<string>>("", "ssl.key_password", "key password (for encrypted private key)", settings.ssl.key_password, &settings.ssl.key_password);
#if 0 // feature: users #if 0 // feature: users
// Users setting // Users setting
auto users_value = conf.add<Value<string>>("", "users.user", "<User nane>:<permissions>:<password>"); auto users_value = conf.add<Value<string>>("", "users.user", "<User nane>:<permissions>:<password>");
#endif #endif
@ -259,7 +260,8 @@ int main(int argc, char* argv[])
if (!settings.ssl.certificate.empty() && !settings.ssl.certificate_key.empty()) if (!settings.ssl.certificate.empty() && !settings.ssl.certificate_key.empty())
{ {
namespace fs = std::filesystem; namespace fs = std::filesystem;
auto make_absolute = [](const fs::path& filename) { auto make_absolute = [](const fs::path& filename)
{
const fs::path cert_path = "/etc/snapserver/certs/"; const fs::path cert_path = "/etc/snapserver/certs/";
if (filename.is_absolute()) if (filename.is_absolute())
return filename; return filename;
@ -288,7 +290,8 @@ int main(int argc, char* argv[])
LOG(INFO, LOG_TAG) << "Version " << version::code << (!version::rev().empty() ? (", revision " + version::rev(8)) : ("")) << "\n"; LOG(INFO, LOG_TAG) << "Version " << version::code << (!version::rev().empty() ? (", revision " + version::rev(8)) : ("")) << "\n";
if (settings.ssl.enabled()) if (settings.ssl.enabled())
LOG(INFO, LOG_TAG) << "SSL enabled - certificate file: '" << settings.ssl.certificate.native() << "', certificate key file: '" << settings.ssl.certificate_key.native() << "'\n"; LOG(INFO, LOG_TAG) << "SSL enabled - certificate file: '" << settings.ssl.certificate.native() << "', certificate key file: '"
<< settings.ssl.certificate_key.native() << "'\n";
if (!streamValue->is_set() && !sourceValue->is_set()) if (!streamValue->is_set() && !sourceValue->is_set())
settings.stream.sources.push_back(sourceValue->value()); settings.stream.sources.push_back(sourceValue->value());

View file

@ -140,10 +140,10 @@ void StreamServer::onDisconnect(StreamSession* streamSession)
LOG(DEBUG, LOG_TAG) << "sessions: " << sessions_.size() << "\n"; LOG(DEBUG, LOG_TAG) << "sessions: " << sessions_.size() << "\n";
sessions_.erase(std::remove_if(sessions_.begin(), sessions_.end(), sessions_.erase(std::remove_if(sessions_.begin(), sessions_.end(),
[streamSession](std::weak_ptr<StreamSession> session) [streamSession](std::weak_ptr<StreamSession> session)
{ {
auto s = session.lock(); auto s = session.lock();
return s.get() == streamSession; return s.get() == streamSession;
}), }),
sessions_.end()); sessions_.end());
LOG(DEBUG, LOG_TAG) << "sessions: " << sessions_.size() << "\n"; LOG(DEBUG, LOG_TAG) << "sessions: " << sessions_.size() << "\n";
if (messageReceiver_ != nullptr) if (messageReceiver_ != nullptr)

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ StreamSession::StreamSession(const boost::asio::any_io_executor& executor, Strea
void StreamSession::setPcmStream(PcmStreamPtr pcmStream) void StreamSession::setPcmStream(PcmStreamPtr pcmStream)
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
pcmStream_ = pcmStream; pcmStream_ = std::move(pcmStream);
} }
@ -60,12 +60,10 @@ void StreamSession::send_next()
{ {
auto& buffer = messages_.front(); auto& buffer = messages_.front();
buffer.on_air = true; buffer.on_air = true;
boost::asio::post(strand_, boost::asio::post(strand_, [this, self = shared_from_this(), buffer]()
[this, self = shared_from_this(), buffer]() {
{ sendAsync(buffer, [this](boost::system::error_code ec, std::size_t length)
sendAsync(buffer, {
[this](boost::system::error_code ec, std::size_t length)
{
messages_.pop_front(); messages_.pop_front();
if (ec) if (ec)
{ {
@ -82,19 +80,18 @@ void StreamSession::send_next()
void StreamSession::send(shared_const_buffer const_buf) void StreamSession::send(shared_const_buffer const_buf)
{ {
boost::asio::post(strand_, boost::asio::post(strand_, [this, self = shared_from_this(), const_buf]()
[this, self = shared_from_this(), const_buf]() {
{
// delete PCM chunks that are older than the overall buffer duration // delete PCM chunks that are older than the overall buffer duration
messages_.erase(std::remove_if(messages_.begin(), messages_.end(), messages_.erase(std::remove_if(messages_.begin(), messages_.end(),
[this](const shared_const_buffer& buffer) [this](const shared_const_buffer& buffer)
{ {
const auto& msg = buffer.message(); const auto& msg = buffer.message();
if (!msg.is_pcm_chunk || buffer.on_air) if (!msg.is_pcm_chunk || buffer.on_air)
return false; return false;
auto age = chronos::clk::now() - msg.rec_time; auto age = chronos::clk::now() - msg.rec_time;
return (age > std::chrono::milliseconds(bufferMs_) + 100ms); return (age > std::chrono::milliseconds(bufferMs_) + 100ms);
}), }),
messages_.end()); messages_.end());
messages_.push_back(const_buf); messages_.push_back(const_buf);

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -87,7 +87,7 @@ void StreamSessionTcp::read_next()
{ {
boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_msg_size_), boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_msg_size_),
[this, self = shared_from_this()](boost::system::error_code ec, std::size_t length) mutable [this, self = shared_from_this()](boost::system::error_code ec, std::size_t length) mutable
{ {
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error reading message header of length " << length << ": " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error reading message header of length " << length << ": " << ec.message() << "\n";
@ -114,9 +114,8 @@ void StreamSessionTcp::read_next()
if (baseMessage_.size > buffer_.size()) if (baseMessage_.size > buffer_.size())
buffer_.resize(baseMessage_.size); buffer_.resize(baseMessage_.size);
boost::asio::async_read(socket_, boost::asio::buffer(buffer_, baseMessage_.size), boost::asio::async_read(socket_, boost::asio::buffer(buffer_, baseMessage_.size), [this, self](boost::system::error_code ec, std::size_t length) mutable
[this, self](boost::system::error_code ec, std::size_t length) mutable {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error reading message body of length " << length << ": " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error reading message body of length " << length << ": " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -69,8 +69,7 @@ AirplayStream::AirplayStream(PcmStream::Listener* pcmListener, boost::asio::io_c
createParser(); createParser();
metadata_dirty_ = false; metadata_dirty_ = false;
#else #else
LOG(INFO, LOG_TAG) << "Metadata support not enabled (HAS_EXPAT not defined)" LOG(INFO, LOG_TAG) << "Metadata support not enabled (HAS_EXPAT not defined)" << "\n";
<< "\n";
#endif #endif
} }
@ -243,9 +242,8 @@ void AirplayStream::pipeReadLine()
} }
const std::string delimiter = "\n"; const std::string delimiter = "\n";
boost::asio::async_read_until(*pipe_fd_, streambuf_pipe_, delimiter, boost::asio::async_read_until(*pipe_fd_, streambuf_pipe_, delimiter, [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred)
[this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) {
{
if (ec) if (ec)
{ {
if ((ec.value() == boost::asio::error::eof) || (ec.value() == boost::asio::error::bad_descriptor)) if ((ec.value() == boost::asio::error::eof) || (ec.value() == boost::asio::error::bad_descriptor))

View file

@ -54,9 +54,8 @@ template <typename Rep, typename Period>
void wait(boost::asio::steady_timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler) void wait(boost::asio::steady_timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler)
{ {
timer.expires_after(duration); timer.expires_after(duration);
timer.async_wait( timer.async_wait([handler = std::move(handler)](const boost::system::error_code& ec)
[handler = std::move(handler)](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error during async wait: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error during async wait: " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -82,9 +82,8 @@ template <typename Timer, typename Rep, typename Period>
void AsioStream<ReadStream>::wait(Timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler) void AsioStream<ReadStream>::wait(Timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler)
{ {
timer.expires_after(duration); timer.expires_after(duration);
timer.async_wait( timer.async_wait([handler = std::move(handler)](const boost::system::error_code& ec)
[handler = std::move(handler)](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
LOG(ERROR, "AsioStream") << "Error during async wait: " << ec.message() << "\n"; LOG(ERROR, "AsioStream") << "Error during async wait: " << ec.message() << "\n";
@ -122,9 +121,8 @@ template <typename ReadStream>
void AsioStream<ReadStream>::check_state(const std::chrono::steady_clock::duration& duration) void AsioStream<ReadStream>::check_state(const std::chrono::steady_clock::duration& duration)
{ {
state_timer_.expires_after(duration); state_timer_.expires_after(duration);
state_timer_.async_wait( state_timer_.async_wait([this, duration](const boost::system::error_code& ec)
[this, duration](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
LOG(INFO, "AsioStream") << "No data since " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() LOG(INFO, "AsioStream") << "No data since " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
@ -177,7 +175,7 @@ void AsioStream<ReadStream>::do_read()
check_state(idle_threshold_ + std::chrono::milliseconds(chunk_ms_)); check_state(idle_threshold_ + std::chrono::milliseconds(chunk_ms_));
boost::asio::async_read(*stream_, boost::asio::buffer(chunk_->payload, chunk_->payloadSize), boost::asio::async_read(*stream_, boost::asio::buffer(chunk_->payload, chunk_->payloadSize),
[this](boost::system::error_code ec, std::size_t length) mutable [this](boost::system::error_code ec, std::size_t length) mutable
{ {
state_timer_.cancel(); state_timer_.cancel();
if (ec) if (ec)
@ -242,9 +240,8 @@ void AsioStream<ReadStream>::do_read()
if (nextTick_ >= currentTick) if (nextTick_ >= currentTick)
{ {
read_timer_.expires_after(nextTick_ - currentTick); read_timer_.expires_after(nextTick_ - currentTick);
read_timer_.async_wait( read_timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
LOG(ERROR, "AsioStream") << "Error during async wait: " << ec.message() << "\n"; LOG(ERROR, "AsioStream") << "Error during async wait: " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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> Copyright (C) 2024 Marcus Weseloh <marcus@weseloh.cc>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -111,9 +111,8 @@ template <typename Rep, typename Period>
void wait(boost::asio::steady_timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler) void wait(boost::asio::steady_timer& timer, const std::chrono::duration<Rep, Period>& duration, std::function<void()> handler)
{ {
timer.expires_after(duration); timer.expires_after(duration);
timer.async_wait( timer.async_wait([handler = std::move(handler)](const boost::system::error_code& ec)
[handler = std::move(handler)](const boost::system::error_code& ec) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error during async wait: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error during async wait: " << ec.message() << "\n";
@ -239,13 +238,10 @@ bool JackStream::openJackConnection()
cpt::to_string(jack_sample_rate) + "."); cpt::to_string(jack_sample_rate) + ".");
} }
jack_set_process_callback( jack_set_process_callback(client_, [](jack_nframes_t nframes, void* arg) { return static_cast<JackStream*>(arg)->readJackBuffers(nframes); }, this);
client_, [](jack_nframes_t nframes, void* arg) { return static_cast<JackStream*>(arg)->readJackBuffers(nframes); }, this); jack_on_shutdown(client_, [](void* arg) { static_cast<JackStream*>(arg)->onJackShutdown(); }, this);
jack_on_shutdown( jack_set_port_registration_callback(client_, [](jack_port_id_t port_id, int registered, void* arg)
client_, [](void* arg) { static_cast<JackStream*>(arg)->onJackShutdown(); }, this); { return static_cast<JackStream*>(arg)->onJackPortRegistration(port_id, registered); }, this);
jack_set_port_registration_callback(
client_, [](jack_port_id_t port_id, int registered, void* arg) { return static_cast<JackStream*>(arg)->onJackPortRegistration(port_id, registered); },
this);
int err = jack_activate(client_); int err = jack_activate(client_);
if (err) if (err)
@ -260,7 +256,7 @@ bool JackStream::openJackConnection()
bool JackStream::createJackPorts() bool JackStream::createJackPorts()
{ {
if (ports_.size() > 0) if (!ports_.empty())
{ {
throw SnapException("Jack ports already created!"); throw SnapException("Jack ports already created!");
} }

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -144,14 +144,12 @@ void PcmStream::onControlRequest(const jsonrpcpp::Request& request)
void PcmStream::pollProperties() void PcmStream::pollProperties()
{ {
property_timer_.expires_after(10s); property_timer_.expires_after(10s);
property_timer_.async_wait( property_timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
stream_ctrl_->command({++req_id_, "Plugin.Stream.Player.GetProperties"}, stream_ctrl_->command({++req_id_, "Plugin.Stream.Player.GetProperties"}, [this](const jsonrpcpp::Response& response)
[this](const jsonrpcpp::Response& response) {
{
LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetProperties: " << response.to_json() << "\n"; LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetProperties: " << response.to_json() << "\n";
if (response.error().code() == 0) if (response.error().code() == 0)
setProperties(response.result()); setProperties(response.result());
@ -175,9 +173,8 @@ void PcmStream::onControlNotification(const jsonrpcpp::Notification& notificatio
else if (notification.method() == "Plugin.Stream.Ready") else if (notification.method() == "Plugin.Stream.Ready")
{ {
LOG(DEBUG, LOG_TAG) << "Plugin is ready\n"; LOG(DEBUG, LOG_TAG) << "Plugin is ready\n";
stream_ctrl_->command({++req_id_, "Plugin.Stream.Player.GetProperties"}, stream_ctrl_->command({++req_id_, "Plugin.Stream.Player.GetProperties"}, [this](const jsonrpcpp::Response& response)
[this](const jsonrpcpp::Response& response) {
{
LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetProperties: " << response.to_json() << "\n"; LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetProperties: " << response.to_json() << "\n";
if (response.error().code() == 0) if (response.error().code() == 0)
setProperties(response.result()); setProperties(response.result());
@ -231,14 +228,14 @@ void PcmStream::start()
{ {
LOG(DEBUG, LOG_TAG) << "Start: " << name_ << ", type: " << uri_.scheme << ", sampleformat: " << sampleFormat_.toString() << ", codec: " << getCodec() LOG(DEBUG, LOG_TAG) << "Start: " << name_ << ", type: " << uri_.scheme << ", sampleformat: " << sampleFormat_.toString() << ", codec: " << getCodec()
<< "\n"; << "\n";
encoder_->init([this](const encoder::Encoder& encoder, std::shared_ptr<msg::PcmChunk> chunk, double duration) { chunkEncoded(encoder, chunk, duration); }, encoder_->init([this](const encoder::Encoder& encoder, std::shared_ptr<msg::PcmChunk> chunk, double duration)
sampleFormat_); { chunkEncoded(encoder, std::move(chunk), duration); }, sampleFormat_);
if (stream_ctrl_) if (stream_ctrl_)
{ {
stream_ctrl_->start( stream_ctrl_->start(getId(), server_settings_, [this](const jsonrpcpp::Notification& notification) {
getId(), server_settings_, [this](const jsonrpcpp::Notification& notification) { onControlNotification(notification); }, onControlNotification(notification);
[this](const jsonrpcpp::Request& request) { onControlRequest(request); }, [this](std::string message) { onControlLog(std::move(message)); }); }, [this](const jsonrpcpp::Request& request) { onControlRequest(request); }, [this](std::string message) { onControlLog(std::move(message)); });
} }
active_ = true; active_ = true;
@ -514,9 +511,8 @@ void PcmStream::sendRequest(const std::string& method, const jsonrpcpp::Paramete
return handler({ControlErrc::can_not_control}); return handler({ControlErrc::can_not_control});
jsonrpcpp::Request req(++req_id_, method, params); jsonrpcpp::Request req(++req_id_, method, params);
stream_ctrl_->command(req, stream_ctrl_->command(req, [handler](const jsonrpcpp::Response& response)
[handler](const jsonrpcpp::Response& response) {
{
if (response.error().code() != 0) if (response.error().code() != 0)
handler({static_cast<ControlErrc>(response.error().code()), response.error().data()}); handler({static_cast<ControlErrc>(response.error().code()), response.error().data()});
else else

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -145,9 +145,8 @@ void ProcessStream::onStderrMsg(const std::string& line)
void ProcessStream::stderrReadLine() void ProcessStream::stderrReadLine()
{ {
const std::string delimiter = "\n"; const std::string delimiter = "\n";
boost::asio::async_read_until(*stream_stderr_, streambuf_stderr_, delimiter, boost::asio::async_read_until(*stream_stderr_, streambuf_stderr_, delimiter, [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred)
[this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error while reading from stderr: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error while reading from stderr: " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -59,9 +59,8 @@ void StreamControl::start(const std::string& stream_id, const ServerSettings& se
void StreamControl::command(const jsonrpcpp::Request& request, const OnResponse& response_handler) void StreamControl::command(const jsonrpcpp::Request& request, const OnResponse& response_handler)
{ {
// use strand to serialize commands sent from different threads // use strand to serialize commands sent from different threads
boost::asio::post(executor_, boost::asio::post(executor_, [this, request, response_handler]()
[this, request, response_handler]() {
{
if (response_handler) if (response_handler)
request_callbacks_[request.id()] = response_handler; request_callbacks_[request.id()] = response_handler;
@ -161,16 +160,14 @@ void ScriptStreamControl::doStart(const std::string& stream_id, const ServerSett
LOG(DEBUG, LOG_TAG) << "Starting control script: '" << script_ << "', params: '" << params.str() << "'\n"; LOG(DEBUG, LOG_TAG) << "Starting control script: '" << script_ << "', params: '" << params.str() << "'\n";
try try
{ {
process_ = bp::child( process_ = bp::child(script_ + params.str(), bp::std_out > pipe_stdout_, bp::std_err > pipe_stderr_, bp::std_in < in_,
script_ + params.str(), bp::std_out > pipe_stdout_, bp::std_err > pipe_stderr_, bp::std_in < in_, bp::on_exit = [](int exit, const std::error_code& ec_in)
bp::on_exit = {
[](int exit, const std::error_code& ec_in)
{
auto severity = AixLog::Severity::debug; auto severity = AixLog::Severity::debug;
if (exit != 0) if (exit != 0)
severity = AixLog::Severity::error; severity = AixLog::Severity::error;
LOG(severity, LOG_TAG) << "Exit code: " << exit << ", message: " << ec_in.message() << "\n"; LOG(severity, LOG_TAG) << "Exit code: " << exit << ", message: " << ec_in.message() << "\n";
}); });
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -196,9 +193,8 @@ void ScriptStreamControl::doCommand(const jsonrpcpp::Request& request)
void ScriptStreamControl::stderrReadLine() void ScriptStreamControl::stderrReadLine()
{ {
const std::string delimiter = "\n"; const std::string delimiter = "\n";
boost::asio::async_read_until(*stream_stderr_, streambuf_stderr_, delimiter, boost::asio::async_read_until(*stream_stderr_, streambuf_stderr_, delimiter, [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred)
[this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error while reading from stderr: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error while reading from stderr: " << ec.message() << "\n";
@ -218,9 +214,8 @@ void ScriptStreamControl::stderrReadLine()
void ScriptStreamControl::stdoutReadLine() void ScriptStreamControl::stdoutReadLine()
{ {
const std::string delimiter = "\n"; const std::string delimiter = "\n";
boost::asio::async_read_until(*stream_stdout_, streambuf_stdout_, delimiter, boost::asio::async_read_until(*stream_stdout_, streambuf_stdout_, delimiter, [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred)
[this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) {
{
if (ec) if (ec)
{ {
LOG(ERROR, LOG_TAG) << "Error while reading from stdout: " << ec.message() << "\n"; LOG(ERROR, LOG_TAG) << "Error while reading from stdout: " << ec.message() << "\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -24,8 +24,8 @@
#include "server_settings.hpp" #include "server_settings.hpp"
// 3rd party headers // 3rd party headers
#include <boost/process.hpp>
#include <boost/asio/any_io_executor.hpp> #include <boost/asio/any_io_executor.hpp>
#include <boost/process.hpp>
// standard headers // standard headers
#include <map> #include <map>

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -24,12 +24,10 @@
#include "common/snap_exception.hpp" #include "common/snap_exception.hpp"
#include "common/str_compat.hpp" #include "common/str_compat.hpp"
#include "common/utils/string_utils.hpp" #include "common/utils/string_utils.hpp"
#include "encoder/encoder_factory.hpp"
// 3rd party headers // 3rd party headers
// standard headers // standard headers
#include <cerrno>
#include <memory> #include <memory>
@ -75,9 +73,8 @@ void TcpStream::connect()
if (is_server_) if (is_server_)
{ {
acceptor_->async_accept( acceptor_->async_accept([this](boost::system::error_code ec, tcp::socket socket)
[this](boost::system::error_code ec, tcp::socket socket) {
{
if (!ec) if (!ec)
{ {
LOG(DEBUG, LOG_TAG) << "New client connection\n"; LOG(DEBUG, LOG_TAG) << "New client connection\n";
@ -94,9 +91,8 @@ void TcpStream::connect()
{ {
stream_ = make_unique<tcp::socket>(strand_); stream_ = make_unique<tcp::socket>(strand_);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(host_), port_); boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(host_), port_);
stream_->async_connect(endpoint, stream_->async_connect(endpoint, [this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
LOG(DEBUG, LOG_TAG) << "Connected\n"; LOG(DEBUG, LOG_TAG) << "Connected\n";

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast 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 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 it under the terms of the GNU General Public License as published by
@ -65,9 +65,8 @@ void Watchdog::trigger()
{ {
timer_.cancel(); timer_.cancel();
timer_.expires_after(timeout_ms_); timer_.expires_after(timeout_ms_);
timer_.async_wait( timer_.async_wait([this](const boost::system::error_code& ec)
[this](const boost::system::error_code& ec) {
{
if (!ec) if (!ec)
{ {
LOG(INFO, LOG_TAG) << "Timed out: " << std::chrono::duration_cast<std::chrono::seconds>(timeout_ms_).count() << "s\n"; LOG(INFO, LOG_TAG) << "Timed out: " << std::chrono::duration_cast<std::chrono::seconds>(timeout_ms_).count() << "s\n";