From 7a68775e6c6ce925b01409c0bc8b2db22e5818ec Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 23 Feb 2021 22:12:43 +0100 Subject: [PATCH] output/snapcast: Zeroconf support --- doc/plugins.rst | 3 +++ meson.build | 3 ++- src/output/plugins/meson.build | 2 +- src/output/plugins/snapcast/Internal.hxx | 11 ++++++++++ .../plugins/snapcast/SnapcastOutputPlugin.cxx | 21 ++++++++++++++++++- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/doc/plugins.rst b/doc/plugins.rst index ec5f000b4..f85692ae4 100644 --- a/doc/plugins.rst +++ b/doc/plugins.rst @@ -1167,6 +1167,9 @@ connect to it and receive audio data from MPD. - Binds the Snapcast server to the specified address. Multiple addresses in parallel are not supported. The default is to bind on all addresses on port :samp:`1704`. + * - **zeroconf yes|no** + - Publish the Snapcast server as service type ``_snapcast._tcp`` + via Zeroconf (Avahi or Bonjour). Default is :samp:`yes`. solaris diff --git a/meson.build b/meson.build index dce9ca2ea..e9e95a7e1 100644 --- a/meson.build +++ b/meson.build @@ -369,6 +369,8 @@ subdir('src/lib/yajl') subdir('src/lib/crypto') +subdir('src/zeroconf') + subdir('src/fs') subdir('src/config') subdir('src/tag') @@ -384,7 +386,6 @@ subdir('src/decoder') subdir('src/encoder') subdir('src/song') subdir('src/playlist') -subdir('src/zeroconf') if curl_dep.found() sources += 'src/RemoteTagCache.cxx' diff --git a/src/output/plugins/meson.build b/src/output/plugins/meson.build index 71b8bb55f..d0810fc47 100644 --- a/src/output/plugins/meson.build +++ b/src/output/plugins/meson.build @@ -119,7 +119,7 @@ if get_option('snapcast') 'snapcast/SnapcastOutputPlugin.cxx', 'snapcast/Client.cxx', ] - output_plugins_deps += [ event_dep, net_dep, yajl_dep ] + output_plugins_deps += [ event_dep, net_dep, yajl_dep, zeroconf_dep ] output_features.set('HAVE_YAJL', yajl_dep.found()) diff --git a/src/output/plugins/snapcast/Internal.hxx b/src/output/plugins/snapcast/Internal.hxx index 291dc378c..33fc85b26 100644 --- a/src/output/plugins/snapcast/Internal.hxx +++ b/src/output/plugins/snapcast/Internal.hxx @@ -30,14 +30,21 @@ #include "util/AllocatedArray.hxx" #include "util/IntrusiveList.hxx" +#include "config.h" // for HAVE_ZEROCONF + #include struct ConfigBlock; class SnapcastClient; class PreparedEncoder; class Encoder; +class ZeroconfHelper; class SnapcastOutput final : AudioOutput, ServerSocket { +#ifdef HAVE_ZEROCONF + unsigned zeroconf_port = 0; +#endif + /** * True if the audio output is open and accepts client * connections. @@ -46,6 +53,10 @@ class SnapcastOutput final : AudioOutput, ServerSocket { InjectEvent inject_event; +#ifdef HAVE_ZEROCONF + std::unique_ptr zeroconf_helper; +#endif + /** * The configured encoder plugin. */ diff --git a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx index 3face3a91..c62343471 100644 --- a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx +++ b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx @@ -32,6 +32,10 @@ #include "util/DeleteDisposer.hxx" #include "config/Net.hxx" +#ifdef HAVE_ZEROCONF +#include "zeroconf/Helper.hxx" +#endif + #ifdef HAVE_YAJL #include "lib/yajl/Gen.hxx" #endif @@ -49,8 +53,14 @@ SnapcastOutput::SnapcastOutput(EventLoop &_loop, const ConfigBlock &block) // TODO: support other encoder plugins? prepared_encoder(encoder_init(wave_encoder_plugin, block)) { + const unsigned port = block.GetBlockValue("port", 1704U); ServerSocketAddGeneric(*this, block.GetBlockValue("bind_to_address"), - block.GetBlockValue("port", 1704U)); + port); + +#ifdef HAVE_ZEROCONF + if (block.GetBlockValue("zeroconf", true)) + zeroconf_port = port; +#endif } SnapcastOutput::~SnapcastOutput() noexcept = default; @@ -62,6 +72,13 @@ SnapcastOutput::Bind() BlockingCall(GetEventLoop(), [this](){ ServerSocket::Open(); + +#ifdef HAVE_ZEROCONF + if (zeroconf_port > 0) + zeroconf_helper = std::make_unique + (GetEventLoop(), "Music Player Daemon", + "_snapcast._tcp", zeroconf_port); +#endif }); // TODO: Zeroconf integration @@ -73,6 +90,8 @@ SnapcastOutput::Unbind() noexcept assert(!open); BlockingCall(GetEventLoop(), [this](){ + zeroconf_helper.reset(); + ServerSocket::Close(); }); }