Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to discover a new server over mdns if the previous one is down #1267

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 2 additions & 149 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ jobs:
fail-fast: false
matrix:
debian:
- bullseye
- bookworm
arch:
- amd64
Expand All @@ -26,10 +25,10 @@ jobs:
os: ubuntu-latest
image_prefix: "debian:"
- arch: "armhf"
os: self-hosted-rpi4
os: ARM
image_prefix: "badaix/raspios-lite:"
- arch: "arm64"
os: self-hosted-rpi5
os: ARM64
image_prefix: "badaix/raspios-lite:"

runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -97,150 +96,4 @@ jobs:
path: ${{env.PARENT_DIR}}/snap*_${{ matrix.arch }}.deb


# mac:
# strategy:
# fail-fast: false
# matrix:
# xcode: ['11']

# runs-on: macos-latest
# name: mac (xcode ${{ matrix.xcode }})

# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Checkout Snapcast
# uses: actions/checkout@v4
# with:
# repository: badaix/snapcast
# path: src/snapcast
# ref: ${{ env.VERSION }}
# - name: Setup environment
# run: |
# BOOST_DOT_VERSION=$(echo ${BOOST_VERSION} | sed 's/_/./g')
# echo "BOOST_DOT_VERSION=$BOOST_DOT_VERSION" >> $GITHUB_ENV
# echo "BOOST=boost_${BOOST_VERSION}" >> $GITHUB_ENV
# - name: Get dependencies
# run: brew install pkgconfig libsoxr ccache expat
# - name: Cache boost
# id: cache-boost
# uses: actions/cache@v4
# with:
# path: ${{env.BOOST}}
# key: ${{ runner.os }}-boost
# - name: Get boost
# if: steps.cache-boost.outputs.cache-hit != 'true'
# run: |
# wget https://boostorg.jfrog.io/artifactory/main/release/${{env.BOOST_DOT_VERSION}}/source/${{env.BOOST}}.tar.bz2
# tar xjf ${{env.BOOST}}.tar.bz2
# - name: Cache ccache
# id: cache-ccache
# uses: actions/cache@v4
# with:
# path: /Users/runner/.ccache
# key: ${{ runner.os }}-ccache-${{ github.sha }}
# restore-keys: ${{ runner.os }}-ccache-
# #- name: ccache dump config
# # run: ccache -p
# - name: configure
# run: cmake -S $GITHUB_WORKSPACE/src/snapcast -B build -DBOOST_ROOT=$GITHUB_WORKSPACE/${{env.BOOST}} -DCMAKE_BUILD_TYPE=Release -DREVISION=${{ github.sha }} -DWERROR=ON -DBUILD_TESTS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS="-I/usr/local/include -Wno-deprecated-declarations"
# - name: build
# run: cmake --build build --parallel 3


# rpm:
# if: ${{ false }} # disable for now
# strategy:
# fail-fast: false
# matrix:
# image:
# - 34
# - 35
# os:
# - ubuntu-latest
# - self-hosted-rpi4
# include:
# - os: ubuntu-latest
# arch: "x86_64"
# - os: self-hosted-rpi4
# arch: "armv7hl"

# runs-on: ${{ matrix.os }}
# name: rpm (${{ matrix.arch }}, fedora ${{ matrix.image }})

# container:
# image: fedora:${{matrix.image}}

# steps:
# - name: Get dependencies
# run: dnf -y update && dnf -y install wget git rpm-build gcc-c++ cmake boost-devel alsa-lib-devel avahi-devel libatomic libvorbis-devel opus-devel pulseaudio-libs-devel flac-devel soxr-devel libstdc++-static expat-devel
# - name: Checkout
# uses: actions/checkout@v4
# - name: Checkout Snapcast
# uses: actions/checkout@v4
# with:
# repository: badaix/snapcast
# path: src/snapcast
# ref: ${{ env.VERSION }}
# - name: Create rpm package
# run: |
# mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
# cp rpm/* ~/rpmbuild/SOURCES/
# tar -C $GITHUB_WORKSPACE/src/ -czvf ~/rpmbuild/SOURCES/snapcast.tar.gz snapcast
# rpmbuild --nodebuginfo --define '_reversion ${{ github.sha }}' --define '_version ${{ env.VERSION }}' -bb ~/rpmbuild/SOURCES/snapcast.spec
# - name: Archive artifacts
# uses: actions/upload-artifact@v4
# with:
# name: snapcast_${{ matrix.arch }}-fedora-${{matrix.image}}-${{ github.sha }}
# path: ~/rpmbuild/RPMS/${{ matrix.arch }}/snap*.rpm


win:
runs-on: windows-2019
name: win

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Checkout Snapcast
uses: actions/checkout@v4
with:
repository: badaix/snapcast
path: src/snapcast
ref: ${{ env.VERSION }}
- name: Cache dependencies
id: cache-dependencies
uses: actions/cache@v4
with:
#path: ${VCPKG_INSTALLATION_ROOT}\installed
path: c:\vcpkg\installed
key: ${{ runner.os }}-dependencies
- name: Get dependenciesenv
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: vcpkg.exe install libflac libvorbis soxr opus boost-asio --triplet x64-windows
- name: configure
run: |
echo vcpkg installation root: $env:VCPKG_INSTALLATION_ROOT
cmake -S . -B build -G "Visual Studio 16 2019" -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET="x64-windows" -DCMAKE_BUILD_TYPE="Release" -DREVISION="${{ github.sha }}" -DWERROR=ON -DBUILD_TESTS=ON
- name: build
run: cmake --build build --config Release --parallel 3 --verbose
- name: installer
run: |
copy ${env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows\bin\FLAC.dll bin\Release\
copy ${env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows\bin\ogg.dll bin\Release\
copy ${env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows\bin\opus.dll bin\Release\
copy ${env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows\bin\vorbis.dll bin\Release\
copy ${env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows\bin\soxr.dll bin\Release\
copy "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Redist\MSVC\v142\vc_redist.x64.exe" bin\Release\
- name: Archive artifacts
uses: actions/upload-artifact@v4
with:
name: snapcast_win64-${{ github.sha }}
path: |
bin\Release\snapclient.exe
bin\Release\FLAC.dll
bin\Release\ogg.dll
bin\Release\opus.dll
bin\Release\vorbis.dll
bin\Release\soxr.dll
bin\Release\vc_redist.x64.exe
6 changes: 6 additions & 0 deletions client/client_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ struct ClientSettings
{
std::string host{""};
size_t port{1704};
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
bool use_mdns{true};
#else
bool use_mdns{false};
#endif
ssize_t connection_attempts{-1};
};

struct Player
Expand Down
48 changes: 33 additions & 15 deletions client/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static constexpr auto TIME_SYNC_INTERVAL = 1s;

Controller::Controller(boost::asio::io_context& io_context, const ClientSettings& settings) //, std::unique_ptr<MetadataAdapter> meta)
: io_context_(io_context), timer_(io_context), settings_(settings), stream_(nullptr), decoder_(nullptr), player_(nullptr),
serverSettings_(nullptr) // meta_(std::move(meta)),
serverSettings_(nullptr), connectionAttempts_(0) // meta_(std::move(meta)),
{
}

Expand Down Expand Up @@ -346,7 +346,13 @@ void Controller::browseMdns(const MdnsHandler& handler)

void Controller::start()
{
if (settings_.server.host.empty())
if (!settings_.server.host.empty())
{
clientConnection_ = make_unique<ClientConnection>(io_context_, settings_.server);
worker();

}
else if(settings_.server.use_mdns)
{
browseMdns(
[this](const boost::system::error_code& ec, const std::string& host, uint16_t port)
Expand All @@ -360,16 +366,13 @@ void Controller::start()
settings_.server.host = host;
settings_.server.port = port;
LOG(INFO, LOG_TAG) << "Found server " << settings_.server.host << ":" << settings_.server.port << "\n";

connectionAttempts_=0;
clientConnection_ = make_unique<ClientConnection>(io_context_, settings_.server);
worker();
}
});
}
else
{
clientConnection_ = make_unique<ClientConnection>(io_context_, settings_.server);
worker();
}
}


Expand All @@ -387,14 +390,29 @@ void Controller::reconnect()
stream_.reset();
decoder_.reset();
timer_.expires_after(1s);
timer_.async_wait(
[this](const boost::system::error_code& ec)
{
if (!ec)
{
worker();
}
});
if (settings_.server.connection_attempts == -1 || connectionAttempts_++ < settings_.server.connection_attempts)
{
timer_.async_wait(
[this](const boost::system::error_code& ec)
{
if (!ec)
{
worker();
}
});
}
else if (settings_.server.use_mdns)
{
settings_.server.host.clear();
timer_.async_wait(
[this](const boost::system::error_code& ec)
{
if (!ec)
{
start();
}
});
}
}

void Controller::worker()
Expand Down
2 changes: 2 additions & 0 deletions client/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@ class Controller
// std::unique_ptr<MetadataAdapter> meta_;
std::unique_ptr<msg::ServerSettings> serverSettings_;
std::unique_ptr<msg::CodecHeader> headerChunk_;

ssize_t connectionAttempts_;
};
14 changes: 11 additions & 3 deletions client/snapclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ int main(int argc, char** argv)
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
op.add<Value<string>>("h", "host", "server hostname or ip address", "", &settings.server.host);
op.add<Value<size_t>>("p", "port", "server port", 1704, &settings.server.port);
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
op.add<Value<bool>>("m", "mdns", "use mDNS (true/false)", "true", &settings.server.use_mdns);
op.add<Value<ssize_t>>("", "connection-attempts", "connectionAttempts before querying mDNS again", -1, &settings.server.connection_attempts);
#endif
op.add<Value<size_t>>("i", "instance", "instance id when running multiple instances on the same host", 1, &settings.instance);
op.add<Value<string>>("", "hostID", "unique host id, default is MAC address", "", &settings.host_id);

Expand Down Expand Up @@ -304,10 +308,14 @@ int main(int argc, char** argv)
else
throw SnapException("Invalid log sink: " + settings.logging.sink);

#if !defined(HAS_AVAHI) && !defined(HAS_BONJOUR)
if (settings.server.host.empty())
throw SnapException("Snapserver host not configured and mDNS not available, please configure with \"--host\".");

if (
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
!settings.server.use_mdns &&
#endif
settings.server.host.empty())
throw SnapException("Snapserver host not configured and mDNS is disabled or not available, please configure with \"--host\" or \"--mdns\".");



#ifdef HAS_DAEMON
Expand Down