diff --git a/sound/snapcast/Makefile b/sound/snapcast/Makefile new file mode 100644 index 00000000000000..354492e181612a --- /dev/null +++ b/sound/snapcast/Makefile @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +include $(TOPDIR)/rules.mk + +PKG_NAME:=snapcast +PKG_VERSION:=0.28.0 +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/badaix/snapcast.git +PKG_SOURCE_VERSION:=v$(PKG_VERSION) +PKG_MIRROR_HASH=288325fc8b134502adb522711c18210794734675684cf5d8eba8a0a82778f6c9 + +PKG_MAINTAINER:=Szabolcs Hubai , David Andreoletti +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE +PKG_CPE_ID:=cpe:/a:badaix:snapcast + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +CMAKE_OPTIONS += -DBUILD_TESTS=OFF + +define Package/snapcast/Default + SECTION:=sound + CATEGORY:=Sound + TITLE:=Synchronous multiroom audio player + DEPENDS:=+AUDIO_SUPPORT:alsa-lib +libavahi-client +libatomic +libogg +libflac +libopus +boost +libsoxr + URL:=https://github.com/badaix/snapcast +endef + +define Package/snapcast/Default/description + Synchronous audio player - Snapcast + + Snapcast is a multi-room client-server audio player, + where all clients are time synchronized with the server + to play perfectly synced audio. + It's not a standalone player, but an extension that turns + your existing audio player into a Sonos-like multiroom solution. + Audio is captured by the server and routed to the connected clients. + Several players can feed audio to the server in parallel + and clients can be grouped to play the same audio stream. + One of the most generic ways to use Snapcast is in conjunction + with the Music Player Daemon (MPD) or Mopidy. + +endef + +define Package/snapserver + $(call Package/snapcast/Default) + TITLE+= (server) + DEPENDS+=+libvorbis +endef + +define Package/snapclient + $(call Package/snapcast/Default) + TITLE+= (client) + DEPENDS+=+libvorbisidec +endef + +define Package/snapserver/description + $(call Package/snapcast/Default/description) + This package contains the Snapcast server. +endef + +define Package/snapclient/description + $(call Package/snapcast/Default/description) + This package contains the Snapcast client. +endef + +define Package/snapserver/conffiles +/etc/config/snapserver +/etc/snapserver.conf +endef + +define Package/snapclient/conffiles +/etc/config/snapclient +endef + +define Package/snapserver/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/snapserver $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/snapserver.init $(1)/etc/init.d/snapserver + $(INSTALL_CONF) $(PKG_BUILD_DIR)/server/etc/snapserver.conf $(1)/etc/snapserver.conf + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/snapserver.conf $(1)/etc/config/snapserver + +endef + +define Package/snapclient/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/snapclient $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/snapclient.init $(1)/etc/init.d/snapclient + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/snapclient.conf $(1)/etc/config/snapclient +endef + +$(eval $(call BuildPackage,snapserver)) +$(eval $(call BuildPackage,snapclient)) diff --git a/sound/snapcast/files/snapclient.conf b/sound/snapcast/files/snapclient.conf new file mode 100644 index 00000000000000..051890a0b8357d --- /dev/null +++ b/sound/snapcast/files/snapclient.conf @@ -0,0 +1,4 @@ +config snapclient 'config' + option enabled '0' + option log_sink 'system' + option opts '' diff --git a/sound/snapcast/files/snapclient.init b/sound/snapcast/files/snapclient.init new file mode 100755 index 00000000000000..49e95dc4150faa --- /dev/null +++ b/sound/snapcast/files/snapclient.init @@ -0,0 +1,33 @@ +#!/bin/sh /etc/rc.common +# Author: Johannes Pohl +# Co-Author: Szabolcs Hubai +START=90 + +USE_PROCD=1 + +NAME=snapclient +PROG=/usr/bin/$NAME +PID_FILE=/var/run/$NAME.pid + +start_service() +{ + config_load "$NAME" + + local enabled + config_get_bool enabled "config" enabled 0 + [ "$enabled" -ne "1" ] && logger -t $NAME "Disabled in /etc/config/$NAME" && return 1 + + local log_sink opts + config_get log_sink "config" log_sink system + config_get opts "config" opts "" + + procd_open_instance + procd_set_param command $PROG + procd_append_param command --logsink $log_sink + procd_append_param command $opts + procd_set_param pidfile $PID_FILE + procd_set_param respawn # use the defaults for respawing crashed process + procd_set_param stderr 1 + procd_set_param stdout 1 + procd_close_instance +} diff --git a/sound/snapcast/files/snapserver.conf b/sound/snapcast/files/snapserver.conf new file mode 100644 index 00000000000000..a341c480270611 --- /dev/null +++ b/sound/snapcast/files/snapserver.conf @@ -0,0 +1,4 @@ +config snapserver 'config' + option enabled '0' + option log_sink 'system' + option opts '' diff --git a/sound/snapcast/files/snapserver.init b/sound/snapcast/files/snapserver.init new file mode 100755 index 00000000000000..7aae17a200c6a5 --- /dev/null +++ b/sound/snapcast/files/snapserver.init @@ -0,0 +1,33 @@ +#!/bin/sh /etc/rc.common +# Author: Johannes Pohl +# Co-Author: Szabolcs Hubai +START=90 + +USE_PROCD=1 + +NAME=snapserver +PROG=/usr/bin/$NAME +PID_FILE=/var/run/$NAME.pid + +start_service() +{ + config_load "$NAME" + + local enabled + config_get_bool enabled "config" enabled 0 + [ "$enabled" -ne "1" ] && logger -t $NAME "Disabled in /etc/config/$NAME" && return 1 + + local log_sink opts + config_get log_sink "config" log_sink system + config_get opts "config" opts "" + + procd_open_instance + procd_set_param command $PROG + procd_append_param command --logging.sink $log_sink + procd_append_param command $opts + procd_set_param pidfile $PID_FILE + procd_set_param respawn # use the defaults for respawing crashed process + procd_set_param stderr 1 + procd_set_param stdout 1 + procd_close_instance +} diff --git a/sound/snapcast/patches/001-v0.29.0-fix-compilation-with-boost-v1.87.0.patch b/sound/snapcast/patches/001-v0.29.0-fix-compilation-with-boost-v1.87.0.patch new file mode 100644 index 00000000000000..743ff732a2b021 --- /dev/null +++ b/sound/snapcast/patches/001-v0.29.0-fix-compilation-with-boost-v1.87.0.patch @@ -0,0 +1,29 @@ +From 6ada939adf5c4f7e43d0fbe6ea45492b9564cb8d Mon Sep 17 00:00:00 2001 +From: badaix +Date: Thu, 19 Dec 2024 22:53:49 +0100 +Subject: [PATCH] Fix compilation with boost v1.87.0 + +--- + server/control_server.cpp | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/server/control_server.cpp ++++ b/server/control_server.cpp +@@ -161,7 +161,7 @@ void ControlServer::start() + { + LOG(INFO, LOG_TAG) << "Creating TCP acceptor for address: " << address << ", port: " << tcp_settings_.port << "\n"; + acceptor_tcp_.emplace_back(make_unique(boost::asio::make_strand(io_context_.get_executor()), +- tcp::endpoint(boost::asio::ip::address::from_string(address), tcp_settings_.port))); ++ tcp::endpoint(boost::asio::ip::make_address(address), settings_.tcp.port))); + } + catch (const boost::system::system_error& e) + { +@@ -177,7 +177,7 @@ void ControlServer::start() + { + LOG(INFO, LOG_TAG) << "Creating HTTP acceptor for address: " << address << ", port: " << http_settings_.port << "\n"; + acceptor_http_.emplace_back(make_unique(boost::asio::make_strand(io_context_.get_executor()), +- tcp::endpoint(boost::asio::ip::address::from_string(address), http_settings_.port))); ++ tcp::endpoint(boost::asio::ip::make_address(address), settings_.http.port))); + } + catch (const boost::system::system_error& e) + { diff --git a/sound/snapcast/patches/002-v0.30.0-use-make_address-from-boost-as-from_string-is-deprecated-1308.patch b/sound/snapcast/patches/002-v0.30.0-use-make_address-from-boost-as-from_string-is-deprecated-1308.patch new file mode 100644 index 00000000000000..65d09f4d5d4468 --- /dev/null +++ b/sound/snapcast/patches/002-v0.30.0-use-make_address-from-boost-as-from_string-is-deprecated-1308.patch @@ -0,0 +1,87 @@ +From b2fd2bf9ef835104a813c40c190d71e3644125d7 Mon Sep 17 00:00:00 2001 +From: Rudi Heitbaum +Date: Thu, 19 Dec 2024 02:23:11 +1100 +Subject: [PATCH] use make_address from boost as from_string is deprecated + (#1308) + +Since boost 1.87.0 boost::asio::ip::address::from_string is no longer available + +ref: https://github.com/boostorg/asio/commit/c0d1cfce7767599c4cf00df36f8017a1073339ae + +fixes: + ../server/control_server.cpp: In member function 'void ControlServer::start()': + ../server/control_server.cpp:164:111: error: 'from_string' is not a member of 'boost::asio::ip::address' + 164 | tcp::endpoint(boost::asio::ip::address::from_string(address), tcp_settings_.port))); + | ^~~~~~~~~~~ + ../server/control_server.cpp:180:112: error: 'from_string' is not a member of 'boost::asio::ip::address' + 180 | tcp::endpoint(boost::asio::ip::address::from_string(address), http_settings_.port))); + | ^~~~~~~~~~~ + ../server/streamreader/tcp_stream.cpp: In constructor 'streamreader::TcpStream::TcpStream(streamreader::PcmStream::Listener*, boost::asio::io_context&, const ServerSettings&, const streamreader::StreamUri&)': + ../server/streamreader/tcp_stream.cpp:67:97: error: 'from_string' is not a member of 'boost::asio::ip::address' + 67 | acceptor_ = make_unique(strand_, tcp::endpoint(boost::asio::ip::address::from_string(host_), port_)); + | ^~~~~~~~~~~ + ../server/streamreader/tcp_stream.cpp: In member function 'virtual void streamreader::TcpStream::connect()': + ../server/streamreader/tcp_stream.cpp:96:75: error: 'from_string' is not a member of 'boost::asio::ip::address' + 96 | boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(host_), port_); + | ^~~~~~~~~~~ + ../server/stream_server.cpp: In member function 'void StreamServer::start()': + ../server/stream_server.cpp:234:103: error: 'from_string' is not a member of 'boost::asio::ip::address' + 234 | tcp::endpoint(boost::asio::ip::address::from_string(address), settings_.stream.port))); + | +--- + server/control_server.cpp | 4 ++-- + server/stream_server.cpp | 2 +- + server/streamreader/tcp_stream.cpp | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +--- a/server/control_server.cpp ++++ b/server/control_server.cpp +@@ -161,7 +161,7 @@ void ControlServer::start() + { + LOG(INFO, LOG_TAG) << "Creating TCP acceptor for address: " << address << ", port: " << tcp_settings_.port << "\n"; + acceptor_tcp_.emplace_back(make_unique(boost::asio::make_strand(io_context_.get_executor()), +- tcp::endpoint(boost::asio::ip::make_address(address), settings_.tcp.port))); ++ tcp::endpoint(boost::asio::ip::make_address(address), tcp_settings_.port))); + } + catch (const boost::system::system_error& e) + { +@@ -177,7 +177,7 @@ void ControlServer::start() + { + LOG(INFO, LOG_TAG) << "Creating HTTP acceptor for address: " << address << ", port: " << http_settings_.port << "\n"; + acceptor_http_.emplace_back(make_unique(boost::asio::make_strand(io_context_.get_executor()), +- tcp::endpoint(boost::asio::ip::make_address(address), settings_.http.port))); ++ tcp::endpoint(boost::asio::ip::make_address(address), http_settings_.port))); + } + catch (const boost::system::system_error& e) + { +--- a/server/stream_server.cpp ++++ b/server/stream_server.cpp +@@ -231,7 +231,7 @@ void StreamServer::start() + { + LOG(INFO, LOG_TAG) << "Creating stream acceptor for address: " << address << ", port: " << settings_.stream.port << "\n"; + acceptor_.emplace_back(make_unique(boost::asio::make_strand(io_context_.get_executor()), +- tcp::endpoint(boost::asio::ip::address::from_string(address), settings_.stream.port))); ++ tcp::endpoint(boost::asio::ip::make_address(address), settings_.stream.port))); + } + catch (const boost::system::system_error& e) + { +--- a/server/streamreader/tcp_stream.cpp ++++ b/server/streamreader/tcp_stream.cpp +@@ -64,7 +64,7 @@ TcpStream::TcpStream(PcmStream::Listener + + LOG(INFO, LOG_TAG) << "TcpStream host: " << host_ << ", port: " << port_ << ", is server: " << is_server_ << "\n"; + if (is_server_) +- acceptor_ = make_unique(strand_, tcp::endpoint(boost::asio::ip::address::from_string(host_), port_)); ++ acceptor_ = make_unique(strand_, tcp::endpoint(boost::asio::ip::make_address(host_), port_)); + } + + +@@ -93,7 +93,7 @@ void TcpStream::connect() + else + { + stream_ = make_unique(strand_); +- boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(host_), port_); ++ boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address(host_), port_); + stream_->async_connect(endpoint, + [this](const boost::system::error_code& ec) + { diff --git a/sound/snapcast/patches/003-v0.30.0-fix-compilation-with-boost-v1.87.0.patch b/sound/snapcast/patches/003-v0.30.0-fix-compilation-with-boost-v1.87.0.patch new file mode 100644 index 00000000000000..b4dd2f87b88247 --- /dev/null +++ b/sound/snapcast/patches/003-v0.30.0-fix-compilation-with-boost-v1.87.0.patch @@ -0,0 +1,44 @@ +From 6190041e863968d76b6d16140bba90be6dff848f Mon Sep 17 00:00:00 2001 +From: badaix +Date: Thu, 19 Dec 2024 21:36:08 +0100 +Subject: [PATCH] Fix compilation with boost v1.87.0 + +--- + client/client_connection.cpp | 5 ++--- + server/stream_session_ws.cpp | 2 +- + 2 files changed, 3 insertions(+), 4 deletions(-) + +--- a/client/client_connection.cpp ++++ b/client/client_connection.cpp +@@ -124,10 +124,9 @@ std::string ClientConnection::getMacAddr + + void ClientConnection::connect(const ResultHandler& handler) + { +- tcp::resolver::query query(server_.host, cpt::to_string(server_.port), boost::asio::ip::resolver_query_base::numeric_service); + boost::system::error_code ec; + LOG(INFO, LOG_TAG) << "Resolving host IP for: " << server_.host << "\n"; +- auto iterator = resolver_.resolve(query, ec); ++ auto iterator = resolver_.resolve(server_.host, cpt::to_string(server_.port), boost::asio::ip::resolver_query_base::numeric_service, ec); + if (ec) + { + LOG(ERROR, LOG_TAG) << "Failed to resolve host '" << server_.host << "', error: " << ec.message() << "\n"; +@@ -141,7 +140,7 @@ void ClientConnection::connect(const Res + for (const auto& iter : iterator) + { + LOG(INFO, LOG_TAG) << "Connecting to " << iter.endpoint() << "\n"; +- socket_.connect(*iterator, ec); ++ socket_.connect(iter, ec); + if (!ec || (ec == boost::system::errc::interrupted)) + { + // We were successful or interrupted, e.g. by sig int +--- a/server/stream_session_ws.cpp ++++ b/server/stream_session_ws.cpp +@@ -114,7 +114,7 @@ void StreamSessionWebsocket::on_read_ws( + return; + } + +- auto* data = boost::asio::buffer_cast(buffer_.data()); ++ auto* data = static_cast(buffer_.data().data()); + baseMessage_.deserialize(data); + LOG(DEBUG, LOG_TAG) << "getNextMessage: " << baseMessage_.type << ", size: " << baseMessage_.size << ", id: " << baseMessage_.id + << ", refers: " << baseMessage_.refersTo << "\n"; diff --git a/sound/snapcast/patches/004-v0.31.101-fix-compilation-with-boost-1.88.patch b/sound/snapcast/patches/004-v0.31.101-fix-compilation-with-boost-1.88.patch new file mode 100644 index 00000000000000..b94c8ad2929ca3 --- /dev/null +++ b/sound/snapcast/patches/004-v0.31.101-fix-compilation-with-boost-1.88.patch @@ -0,0 +1,60 @@ +From 901b79a213b139c35ff689c05aff01d29610a8d4 Mon Sep 17 00:00:00 2001 +From: Tobias Hochwallner +Date: Wed, 7 May 2025 22:42:07 +0200 +Subject: Build failure with boost 1.88.0 #1367 + 1. [tobster] fix compilation errors by including the deprecated boost process v1 APIs + 2. [badaix] Define BOOST_PROCESS_VERSION=1 globally + +--- + client/player/player.cpp | 6 +++++- + server/streamreader/stream_control.hpp | 8 +++++--- + 2 files changed, 10 insertions(+), 4 deletions(-) + +--- a/client/player/player.cpp ++++ b/client/player/player.cpp +@@ -34,10 +34,17 @@ + #pragma GCC diagnostic ignored "-Wmissing-braces" + #pragma GCC diagnostic ignored "-Wnarrowing" + #pragma GCC diagnostic ignored "-Wc++11-narrowing" ++#if BOOST_VERSION >= 108800 ++#include ++#include ++#include ++#include ++#else + #include + #include + #include + #include ++#endif + #pragma GCC diagnostic pop + #endif + +--- a/server/streamreader/stream_control.hpp ++++ b/server/streamreader/stream_control.hpp +@@ -32,7 +32,13 @@ + #pragma GCC diagnostic ignored "-Wnarrowing" + #pragma GCC diagnostic ignored "-Wc++11-narrowing" + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" ++#if BOOST_VERSION >= 108800 ++#include ++#include ++#include ++#else + #include ++#endif + #pragma GCC diagnostic pop + #include + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -318,7 +318,8 @@ else() + add_compile_definitions(HAS_TREMOR) + endif() + +-add_compile_definitions(BOOST_ERROR_CODE_HEADER_ONLY BOOST_ASIO_NO_TS_EXECUTORS) ++add_compile_definitions(BOOST_ERROR_CODE_HEADER_ONLY BOOST_ASIO_NO_TS_EXECUTORS ++ BOOST_PROCESS_VERSION=1) + + if(WIN32) + include(FindPackageHandleStandardArgs) diff --git a/sound/snapcast/patches/005-v0.32.4-fix-building-with-boost-v1.89.0.patch b/sound/snapcast/patches/005-v0.32.4-fix-building-with-boost-v1.89.0.patch new file mode 100644 index 00000000000000..dc13fbc218f0ee --- /dev/null +++ b/sound/snapcast/patches/005-v0.32.4-fix-building-with-boost-v1.89.0.patch @@ -0,0 +1,19 @@ +From d2e1635ebd96ac76cc779cb3ab73bae10fd88fd1 Mon Sep 17 00:00:00 2001 +From: badaix +Date: Thu, 14 Aug 2025 20:25:01 +0200 +Subject: [PATCH] Fix building with boost v1.89.0 + +--- + client/player/alsa_player.cpp | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +--- a/client/player/alsa_player.cpp ++++ b/client/player/alsa_player.cpp +@@ -30,6 +30,7 @@ + + // standard headers + #include ++#include + + + using namespace std::chrono_literals; diff --git a/sound/snapcast/test.sh b/sound/snapcast/test.sh new file mode 100755 index 00000000000000..fb410e0d0aaa56 --- /dev/null +++ b/sound/snapcast/test.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +"$1" --version | grep -F "$PKG_VERSION"