From 73c203322a1faaa67f60ad2a97ff255c0b1b03d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 10:02:32 +0000 Subject: [PATCH 01/31] Bump docker/setup-qemu-action from 3.7.0 to 4.0.0 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.7.0 to 4.0.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v3.7.0...v4.0.0) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-on-push-tag-or-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-on-push-tag-or-pr.yaml b/.github/workflows/docker-on-push-tag-or-pr.yaml index 9ebee0a2..e06bcf78 100644 --- a/.github/workflows/docker-on-push-tag-or-pr.yaml +++ b/.github/workflows/docker-on-push-tag-or-pr.yaml @@ -67,7 +67,7 @@ jobs: if: needs.docker-vars.outputs.push_docker_image == 'true' - name: Set up QEMU - uses: docker/setup-qemu-action@v3.7.0 + uses: docker/setup-qemu-action@v4.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.12.0 From 59c1c6ad598c6d1c22b97587191a056943919e64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 10:02:36 +0000 Subject: [PATCH 02/31] Bump docker/login-action from 3.7.0 to 4.0.0 Bumps [docker/login-action](https://github.com/docker/login-action) from 3.7.0 to 4.0.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v3.7.0...v4.0.0) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-on-push-tag-or-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-on-push-tag-or-pr.yaml b/.github/workflows/docker-on-push-tag-or-pr.yaml index 9ebee0a2..d5123d46 100644 --- a/.github/workflows/docker-on-push-tag-or-pr.yaml +++ b/.github/workflows/docker-on-push-tag-or-pr.yaml @@ -59,7 +59,7 @@ jobs: fi - name: Login to Docker Registry - uses: docker/login-action@v3.7.0 + uses: docker/login-action@v4.0.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_REGISTRY_USER }} From 7fb3506e3f1a59856b521181e1b6f666bb2700eb Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:43:17 +0000 Subject: [PATCH 03/31] Update man page link in README.md [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e74fd8c..f03b97df 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Shairport Sync does not support AirPlay video or photo streaming. * Next Steps and Advanced Topics are [here](ADVANCED%20TOPICS/README.md). * Runtime settings are documented [here](scripts/shairport-sync.conf). * Build configuration options are detailed in [CONFIGURATION FLAGS.md](CONFIGURATION%20FLAGS.md). -* The `man` page, detailing command line options, is [here](https://htmlpreview.github.io/?https://github.com/mikebrady/shairport-sync/blob/development/man/shairport-sync.html). +* The `man` page, detailing command line options, is [here](https://raw.githack.com/mikebrady/shairport-sync/development/man/shairport-sync.1.xml). * Some advanced topics and developed in [ADVANCED TOPICS](https://github.com/mikebrady/shairport-sync/tree/master/ADVANCED%20TOPICS). # Features From ad047779e336485c37127d9c0510fa92987eca09 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:48:29 +0000 Subject: [PATCH 04/31] Fix dead link in RELEASENOTES-DEVELOPMENT.md [skip ci] Updated documentation to fix a dead link to the online man page and acknowledged the contributor. --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index bf5e06e5..ddb68e92 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 5.0.1+-23-g7fb3506e +== +**Documentation Update** +* Fixed dead link to the online [`man`](https://raw.githack.com/mikebrady/shairport-sync/development/man/shairport-sync.1.xml) page. Thanks to [CornelisJ](https://github.com/CornelisJ) for the [report](https://github.com/mikebrady/shairport-sync/issues/2158). + Version 5.0.1+-13-ga14517d7 == **Change** From 4cf43357f9add7303f31465bebe139bced37eae2 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 27 Mar 2026 08:14:53 +0000 Subject: [PATCH 05/31] Update macOS version for build job --- .github/workflows/check_classic_mac_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_classic_mac_basic.yml b/.github/workflows/check_classic_mac_basic.yml index e0288d77..ae322e04 100644 --- a/.github/workflows/check_classic_mac_basic.yml +++ b/.github/workflows/check_classic_mac_basic.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: macos-13 + runs-on: macos-15 steps: - uses: actions/checkout@v6.0.2 From 36b20d4c8ff578fe0806420b101017875331b77b Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 27 Mar 2026 08:19:30 +0000 Subject: [PATCH 06/31] Update macOS runner to use latest version [skip ci] --- .github/workflows/check_classic_mac_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_classic_mac_basic.yml b/.github/workflows/check_classic_mac_basic.yml index ae322e04..4ad673dc 100644 --- a/.github/workflows/check_classic_mac_basic.yml +++ b/.github/workflows/check_classic_mac_basic.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: macos-15 + runs-on: macos-latest steps: - uses: actions/checkout@v6.0.2 From 7722a0e7bd2f335495a1f59738225f6bbcb7d685 Mon Sep 17 00:00:00 2001 From: Mike Lei Date: Sat, 28 Mar 2026 01:57:04 +0800 Subject: [PATCH 07/31] fix: avoid leading space in systemd unit option values --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 002639c0..1222b46e 100644 --- a/configure.ac +++ b/configure.ac @@ -228,8 +228,8 @@ if test "x$with_avahi" = "xyes" ; then AC_DEFINE([CONFIG_AVAHI], 1, [Include Avahi-based mDNS support.]) AC_CHECK_LIB([avahi-client], [avahi_client_new], , AC_MSG_ERROR(Avahi support requires the avahi-client library!)) AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!)) - systemd_after_args="${systemd_after_args} avahi-daemon.service" - systemd_requires_args="${systemd_requires_args} avahi-daemon.service" + systemd_after_args="${systemd_after_args}${systemd_after_args:+ }avahi-daemon.service" + systemd_requires_args="${systemd_requires_args}${systemd_requires_args:+ }avahi-daemon.service" fi AM_CONDITIONAL([USE_AVAHI], [test "x$with_avahi" = "xyes"]) From e41d52dad16b872fb3de0819a88b0d84d5f73420 Mon Sep 17 00:00:00 2001 From: Mike Lei Date: Sat, 28 Mar 2026 01:59:06 +0800 Subject: [PATCH 08/31] fix: add missing nqptp.service to systemd `After` and `Requires` options when building for Airplay 2 --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 1222b46e..cd9e0e6e 100644 --- a/configure.ac +++ b/configure.ac @@ -443,6 +443,8 @@ if test "x$with_airplay_2" = "xyes" ; then ) ]) AC_CHECK_LIB([uuid],[uuid_generate], [], [AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])]) + systemd_after_args="${systemd_after_args}${systemd_after_args:+ }nqptp.service" + systemd_requires_args="${systemd_requires_args}${systemd_requires_args:+ }nqptp.service" fi AM_CONDITIONAL([USE_AIRPLAY_2], [test "x$with_airplay_2" = "xyes"]) From 6cdd6251a4d4bb3d6f5951d90dedb68622b5df79 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:29:44 +0100 Subject: [PATCH 09/31] format tidy-up [skip ci] --- .github/workflows/check_classic_mac_basic.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/check_classic_mac_basic.yml b/.github/workflows/check_classic_mac_basic.yml index 4ad673dc..44f0def6 100644 --- a/.github/workflows/check_classic_mac_basic.yml +++ b/.github/workflows/check_classic_mac_basic.yml @@ -9,9 +9,7 @@ on: jobs: build: - runs-on: macos-latest - steps: - uses: actions/checkout@v6.0.2 - name: Install Dependencies From 11cd01c34aa6dbfa838c384e3ec8bc5beb2d8181 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:05:27 +0100 Subject: [PATCH 10/31] Replace an 'assert' with a standard debug message to avoid a possible compliler warning. Add copyright notices. [skip ci]. --- FFTConvolver/ConvolverThreadPool.cpp | 41 ++++++++++++++++++++++++++++ FFTConvolver/convolver.cpp | 26 +++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/FFTConvolver/ConvolverThreadPool.cpp b/FFTConvolver/ConvolverThreadPool.cpp index 8287417e..ef969572 100644 --- a/FFTConvolver/ConvolverThreadPool.cpp +++ b/FFTConvolver/ConvolverThreadPool.cpp @@ -1,7 +1,37 @@ +/* + * Convolver Thread Pool. This file is part of Shairport Sync + * Copyright (c) Mike Brady 2026 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + #include "ConvolverThreadPool.h" #include "FFTConvolver.h" #include "config.h" +extern "C" void _debug(const char *filename, const int linenumber, int level, const char *format, + ...); +#define debug(...) _debug(__FILE__, __LINE__, __VA_ARGS__) + ConvolverThreadPool::ConvolverThreadPool() : _convolvers(), _threads(), _taskQueue(), _queueMutex(), _condition(), _completionCV(), _stop(false), _activeTasks(0) {} @@ -105,6 +135,16 @@ void ConvolverThreadPool::waitForAll() { _completionCV.wait(lock, [this]() { return _taskQueue.empty() && _activeTasks == 0; }); } +void ConvolverThreadPool::clearState(size_t convolverId) { + // Do the replacement assertion check first, and then wait for all tasks to stop + if (convolverId < _convolvers.size()) { + waitForAll(); + } else { + debug(1, "assert(convolverId < _convolvers.size()) failed, with convolverId: %u and _convolvers.size(): %u.", convolverId, _convolvers.size()); + } +} + +/* this is the old version void ConvolverThreadPool::clearState(size_t convolverId) { assert(convolverId < _convolvers.size()); @@ -113,6 +153,7 @@ void ConvolverThreadPool::clearState(size_t convolverId) { // _convolvers[convolverId]->clearState(); } +*/ void ConvolverThreadPool::clearAllStates() { // Make sure no tasks are running before clearing state diff --git a/FFTConvolver/convolver.cpp b/FFTConvolver/convolver.cpp index 16eba015..8844bd41 100644 --- a/FFTConvolver/convolver.cpp +++ b/FFTConvolver/convolver.cpp @@ -1,4 +1,28 @@ - +/* + * Convolver. This file is part of Shairport Sync + * Copyright (c) Mike Brady 2026 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ #include "convolver.h" #include "ConvolverThreadPool.h" From 11d7fe6b8c9abfd1d421bae8e02f0650f55d1b0b Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:14:43 +0100 Subject: [PATCH 11/31] minor debug message update. [skip ci] --- FFTConvolver/convolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FFTConvolver/convolver.cpp b/FFTConvolver/convolver.cpp index 8844bd41..c276d778 100644 --- a/FFTConvolver/convolver.cpp +++ b/FFTConvolver/convolver.cpp @@ -46,8 +46,8 @@ void convolver_pool_init(size_t numThreads, size_t numConvolvers) { if (!pool.init(numThreads, numConvolvers)) { debug(1, "failed to initialize thread pool!"); } else { - debug(1, "thread pool initialized with %u threads and %u convolvers.", numThreads, - numConvolvers); + debug(1, "thread pool initialized with %u thread%s and %u convolver%s.", numThreads, numThreads == 1 ? "" : "s", + numConvolvers, numConvolvers == 1 ? "" : "s"); } } From a1b4be5d73eee134585499c58e3ebe18840c021c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:02:25 +0000 Subject: [PATCH 12/31] Bump docker/login-action from 4.0.0 to 4.1.0 Bumps [docker/login-action](https://github.com/docker/login-action) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-on-push-tag-or-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-on-push-tag-or-pr.yaml b/.github/workflows/docker-on-push-tag-or-pr.yaml index 11129949..23b5eec6 100644 --- a/.github/workflows/docker-on-push-tag-or-pr.yaml +++ b/.github/workflows/docker-on-push-tag-or-pr.yaml @@ -59,7 +59,7 @@ jobs: fi - name: Login to Docker Registry - uses: docker/login-action@v4.0.0 + uses: docker/login-action@v4.1.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_REGISTRY_USER }} From 0b0ffcc04b8c357c503600582fdacfe32c807742 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:39:50 +0100 Subject: [PATCH 13/31] Remove an requirement for libuuid for FFmpeg and update some carelessly reused AirPlay 2 error messages to refer to FFmpeg. [skip ci] --- configure.ac | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index 002639c0..a343f029 100644 --- a/configure.ac +++ b/configure.ac @@ -455,7 +455,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then PKG_CHECK_MODULES( [libavutil], [libavutil], [CFLAGS="${libavutil_CFLAGS} ${CFLAGS}" LIBS="${libavutil_LIBS} ${LIBS}"], - [AC_MSG_ERROR(AirPlay 2 support requires libavutil -- libavutil-dev suggested)] + [AC_MSG_ERROR(FFmpeg requires libavutil -- libavutil-dev suggested)] ) PKG_CHECK_MODULES( [libavcodec], [libavcodec], @@ -463,7 +463,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then [AC_CHECK_LIB( [avcodec], [avcodec_find_decoder], [], - [AC_MSG_ERROR(AirPlay 2 support requires libavcodec -- libavcodec-dev suggested)] + [AC_MSG_ERROR(FFmpeg requires libavcodec -- libavcodec-dev suggested)] )] ) PKG_CHECK_MODULES( @@ -472,7 +472,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then [AC_CHECK_LIB( [avformat], [avformat_new_stream], [], - [AC_MSG_ERROR([AirPlay 2 support requires libavformat -- libavformat-dev suggested])] + [AC_MSG_ERROR([FFmpeg requires libavformat -- libavformat-dev suggested])] )] ) PKG_CHECK_MODULES( @@ -481,16 +481,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then [AC_CHECK_LIB( [swresample], [swr_convert], [], - [AC_MSG_ERROR([AirPlay 2 support requires libswresample -- libswresample-dev suggested])] - )] - ) - PKG_CHECK_MODULES( - [libuuid], [uuid], - [CFLAGS="${libuuid_CFLAGS} ${CFLAGS}" LIBS="${libuuid_LIBS} ${LIBS}"], - [AC_CHECK_LIB( - [uuid], [uuid_generate], - [], - [AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])] + [AC_MSG_ERROR([FFmpeg requires libswresample -- libswresample-dev suggested])] )] ) else From 9b8c56dcea7eb8e102c77fba77038bdd0c924c6b Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:49:36 +0100 Subject: [PATCH 14/31] Update RELEASENOTES-DEVELOPMENT.md for version 5.0.1 [skip ci] Updated release notes for version 5.0.1, including bug fixes, documentation updates, and changes related to AirPlay 2. --- RELEASENOTES-DEVELOPMENT.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index ddb68e92..bf6bf514 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Verion 5.0.1+-32-g20a87fef +== +**Build Bugfix** +* Fix a bug at the `.configure...` stage of a build of Classic Shairport Sync whereby the UUID library was incorrectly required if the FFmpeg library was being used. (The wording on a few associated warning messages was also fixed.) Thanks to [phexian](https://github.com/phexian) for the [report](https://github.com/mikebrady/shairport-sync/issues/2177). + +**Dependabot Update** +* A Dependabot update to a building tool was added. + Version 5.0.1+-23-g7fb3506e == **Documentation Update** @@ -6,7 +14,7 @@ Version 5.0.1+-23-g7fb3506e Version 5.0.1+-13-ga14517d7 == **Change** -* When built for AirPlay 2, add pw=true to the Bonjour raop properties if the classic AirPlay password is set. This might help with Owntone compatibility. +* When built for AirPlay 2, add `pw=true` to the Bonjour raop properties if the classic AirPlay password is set. This might help with Owntone compatibility. Version 5.0.1+-11-g07546c35 == From 2edda96ac6de225026b6dc2e39301448b393f7c1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:18:27 +0100 Subject: [PATCH 15/31] Update release notes for version 5.0.1-36-g6729802d [skip ci] Updated release notes to reflect changes in version 5.0.1-36-g6729802d, including updates to the Shairport Sync systemd service file and a bugfix related to the UUID library requirement. --- RELEASENOTES-DEVELOPMENT.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index bf6bf514..abf9bb69 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,4 +1,13 @@ -Verion 5.0.1+-32-g20a87fef +Version 5.0.1+-36-g6729802d +== +**Update** +* The Shairport Sync `systemd` service file has been updated as follows: + * Use POSIX shell parameter expansion functionality to prevent adding leading space in option values. + * Add `nqptp.service` to the `After` and `Requires` options when building for Airplay 2. + + Many thanks to [mikelei8291](https://github.com/mikelei8291) for the [PR](https://github.com/mikebrady/shairport-sync/pull/2174). + +Version 5.0.1+-32-g20a87fef == **Build Bugfix** * Fix a bug at the `.configure...` stage of a build of Classic Shairport Sync whereby the UUID library was incorrectly required if the FFmpeg library was being used. (The wording on a few associated warning messages was also fixed.) Thanks to [phexian](https://github.com/phexian) for the [report](https://github.com/mikebrady/shairport-sync/issues/2177). From f784e463e434d0b93cb05b4d4f0a06bc1948b04f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:36:57 +0100 Subject: [PATCH 16/31] Don't allow the Apple ALAC decoder to be included in an AirPlay 2 build. [skip ci] --- configure.ac | 6 +++++- dbus-service.c | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4f66503b..5fb90de3 100644 --- a/configure.ac +++ b/configure.ac @@ -412,6 +412,7 @@ AM_CONDITIONAL([USE_METADATA], [test "x$with_metadata" = "xyes"]) # Look for AirPlay 2 flag AC_ARG_WITH(airplay-2, [AS_HELP_STRING([--with-airplay-2],[Build for AirPlay 2])]) if test "x$with_airplay_2" = "xyes" ; then + using_ffmpeg=true AC_DEFINE([CONFIG_AIRPLAY_2], 1, [Build for AirPlay 2]) AC_CHECK_PROG(PLISTUTIL_CHECK,plistutil,yes) AS_IF([test x"$PLISTUTIL_CHECK" != x"yes"], [AC_MSG_ERROR([plistutil can not be found. Please install plistutil for building for AirPlay 2. Maybe it's in the apt package libplist-utils.])]) @@ -498,8 +499,11 @@ AM_CONDITIONAL([USE_HAMMERTON], [test $using_ffmpeg = false]) # Look for Apple ALAC flag AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for the Apple ALAC decoder (deprecated)])]) if test "x${with_apple_alac}" = "xyes" ; then + if test "x${with_airplay_2}" = "xyes" ; then + AC_MSG_ERROR([the Apple ALAC Decoder (deprecated) can not be used in AirPlay 2.]) + fi if test $using_ffmpeg = true ; then - AC_MSG_RESULT([Note: the FFMPEG decoder, included in the build, supercedes the Apple ALAC decoder, which is deprecated. Consider omitting the Apple ALAC decoder.]) + AC_MSG_WARN([the FFMPEG decoder, included in the build, supercedes the Apple ALAC Decoder, which is deprecated. Consider omitting the Apple ALAC Decoder.]) fi AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Include support for using the Apple ALAC Decoder (deprecated)]) if test "x${with_pkg_config}" = xyes ; then diff --git a/dbus-service.c b/dbus-service.c index 15d02fa0..de859ba4 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -737,6 +737,12 @@ gboolean notify_alacdecoder_callback(ShairportSync *skeleton, __attribute__((unused)) gpointer user_data) { char *th = (char *)shairport_sync_get_alacdecoder(skeleton); +#ifdef CONFIG_AIRPLAY_2 + if (strcasecmp(th, "ffmpeg") != 0) { + warn(" This setting request is ignored: for AirPlay 2, the FFmpeg decoder must be used.", + th); + } +#else if ((strcasecmp(th, "hammerton") == 0) && ((config.decoders_supported & (1 << decoder_hammerton)) != 0)) config.decoder_in_use = 1 << decoder_hammerton; @@ -747,12 +753,13 @@ gboolean notify_alacdecoder_callback(ShairportSync *skeleton, ((config.decoders_supported & (1 << decoder_ffmpeg_alac)) != 0)) config.decoder_in_use = 1 << decoder_ffmpeg_alac; else { - warn("An unrecognised or unsupported ALAC decoder: \"%s\" was requested via D-Bus interface. " + warn("An unrecognised or unsupported decoder: \"%s\" was requested via D-Bus interface. " "(Possibly " "support for this decoder was not compiled " "into this version of Shairport Sync.)", th); } +#endif return TRUE; } From b3b3a43cd2626b43dc90a003e3a40ab5e98ce372 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Apr 2026 16:48:26 +0100 Subject: [PATCH 17/31] Update release notes for version 5.0.1-38 [skip ci] Removed Apple ALAC Decoder from AirPlay 2 build due to security issues. --- RELEASENOTES-DEVELOPMENT.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index abf9bb69..d3492eb8 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,9 +1,16 @@ +Version 5.0.1+-38-gf784e463 +== +**Build Update** +* The Apple ALAC Decoder can no longer be included in an AirPlay 2 build. The reason is that FFmpeg -- used instead -- deals comprehensively with ALAC, AAC, multichannel, mixdown and rate transcoding and is well maintained. + + The Apple ALAC Decoder is known to have security issues and is no longer maintained. This update prevents it from being included in Shairport Sync for AirPlay 2. + Version 5.0.1+-36-g6729802d == **Update** * The Shairport Sync `systemd` service file has been updated as follows: - * Use POSIX shell parameter expansion functionality to prevent adding leading space in option values. - * Add `nqptp.service` to the `After` and `Requires` options when building for Airplay 2. + * It uses POSIX shell parameter expansion functionality to prevent adding leading space in option values. + * It adds `nqptp.service` to the `After` and `Requires` options when building for Airplay 2. Many thanks to [mikelei8291](https://github.com/mikelei8291) for the [PR](https://github.com/mikebrady/shairport-sync/pull/2174). From abede0a4f7bd6686c06d1ee8946ba02e7643cf4c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:12:40 +0100 Subject: [PATCH 18/31] Improve behaviour when there is a large timing uncertainty/granularity and a largeish audio buffer. e.g. on a VM. --- ap2_buffered_audio_processor.c | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/ap2_buffered_audio_processor.c b/ap2_buffered_audio_processor.c index 30f3b00f..09581ded 100644 --- a/ap2_buffered_audio_processor.c +++ b/ap2_buffered_audio_processor.c @@ -235,6 +235,7 @@ void *rtp_buffered_audio_processor(void *arg) { uint16_t data_len; // here we read from the buffer that our thread has been reading + // debug(1,"read a block"); size_t bytes_remaining_in_buffer; nread = read_sized_block(buffered_audio, &data_len, sizeof(data_len), &bytes_remaining_in_buffer); @@ -249,6 +250,7 @@ void *rtp_buffered_audio_processor(void *arg) { // get the block itself // debug(1,"buffered audio packet of size %u detected.", data_len - 2); nread = read_sized_block(buffered_audio, packet, data_len - 2, &bytes_remaining_in_buffer); + // debug(1,"block read"); // diagnostic if ((conn->ap2_audio_buffer_minimum_size < 0) || @@ -442,7 +444,7 @@ void *rtp_buffered_audio_processor(void *arg) { // to decode it and pass it to the player if (new_audio_block_needed == 0) { // is there space in the player thread's buffer system? - size_t player_buffer_occupancy = get_audio_buffer_occupancy(conn); + // size_t player_buffer_occupancy = get_audio_buffer_occupancy(conn); // debug(1,"player buffer size and occupancy: %u and %u", player_buffer_size, // player_buffer_occupancy); @@ -451,17 +453,41 @@ void *rtp_buffered_audio_processor(void *arg) { // and send it to the player. Otherwise, keep the block and sleep for a while. // calculate if there is room in the decoded audio buffer... - int audio_decoded_buffer_below_desired_length = ((1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate) <= config.audio_decoded_buffer_desired_length; + + // debug(1, "frames buffered: %f seconds, desired length: %f seconds.", (1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate, config.audio_decoded_buffer_desired_length); + + // int audio_decoded_buffer_below_desired_length = ((1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate) <= config.audio_decoded_buffer_desired_length; uint64_t buffer_should_be_time; int have_valid_time = (frame_to_local_time(timestamp, &buffer_should_be_time, conn) == 0); // calculate the lead time to make sure it's not too early... int64_t lead_time = buffer_should_be_time - get_absolute_time_in_ns(); - - if ((play_enabled != 0) && (have_valid_time != 0) && - (audio_decoded_buffer_below_desired_length != 0) && - (lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1))) { + + + // debug(1,"play_enabled: %d, have_valid_time: %d, audio_decoded_buffer_below_desired_length: %d, lead_time * 1E-9: %f, (config.audio_decoded_buffer_desired_length + 0.1): %f, player_buffer_occupancy: %zu", + // play_enabled, have_valid_time, audio_decoded_buffer_below_desired_length, lead_time * 1E-9, (config.audio_decoded_buffer_desired_length + 0.1), player_buffer_occupancy + // ); + + // A slight problem here is that counting the number of buffers may not be sufficient, + // because the actual device may be + // taking data in large quantities at a single time. + + // So we just have to ensure that there + // is enough of a lead time maintained for sufficient audio to be available to prevent + // the device from under-running. + + // If means that the Shairport Sync player might riun out of audio occasionally, but + // as long as the device has enough in its buffer, everything is fine. + + // But it also means that Shairport Sync's buffers must be sufficient to hold all the + // entire lead-time's amount of audio in case the device has a zero-sized buffer. + + if ((play_enabled != 0) && (have_valid_time != 0) +// (audio_decoded_buffer_below_desired_length != 0) && + && (lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1)) +// && (audio_decoded_buffer_below_desired_length != 0) + ) { very_early_packets_signalled = 0; //reset very early packet warning signaller @@ -639,8 +665,7 @@ void *rtp_buffered_audio_processor(void *arg) { debug(1, "incoming frame suddenly (?) has a lead time of %f seconds, with a desired decoded buffer length of %f.", 1.0 * lead_time * 1E-9, config.audio_decoded_buffer_desired_length); very_early_packets_signalled = 1; } - - usleep(20000); // wait for a while + usleep(((1000000 * conn->frames_per_packet) / conn->input_rate) * 2); // wait for approximately the length of two packets } } } From 873a35c591a606968d251dab854a7585584227e2 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:13:36 +0100 Subject: [PATCH 19/31] fix a couple of error messages --- audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio.c b/audio.c index 7c08de00..a703d660 100644 --- a/audio.c +++ b/audio.c @@ -985,7 +985,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra } #ifdef CONFIG_FFMPEG if (local_format == SPS_FORMAT_UNKNOWN) { - debug(3, "check for an next highest rate above %u with %u channels.", rate, + debug(3, "check for the next highest rate above %u with %u channels.", rate, local_channels); unsigned int rate_pointer = 0; while ((rate_pointer < sizeof(rates) / sizeof(unsigned int)) && @@ -1003,7 +1003,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra if (local_format == SPS_FORMAT_UNKNOWN) { int rate_pointer = (int)(sizeof(rates) / sizeof(unsigned int) - 1); - debug(3, "check for an next lowest rate below %u with %u channels.", rate, + debug(3, "check for the next lowest rate below %u with %u channels.", rate, local_channels); while ((rate_pointer >= 0) && (local_format == SPS_FORMAT_UNKNOWN)) { local_rate = rates[rate_pointer]; From ad8389be5d72066886f7031ae5024d8b01afd423 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:46:38 +0100 Subject: [PATCH 20/31] Fix a warning message. --- dbus-service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbus-service.c b/dbus-service.c index de859ba4..c6b63251 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -739,7 +739,7 @@ gboolean notify_alacdecoder_callback(ShairportSync *skeleton, #ifdef CONFIG_AIRPLAY_2 if (strcasecmp(th, "ffmpeg") != 0) { - warn(" This setting request is ignored: for AirPlay 2, the FFmpeg decoder must be used.", + warn(" This request, to set the decoder to \"%s\", is ignored. For AirPlay 2, the FFmpeg decoder is always used.", th); } #else From a45bf3db017d556ce5cd9ee7e6b4a045a8c28f74 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:47:40 +0100 Subject: [PATCH 21/31] Quieten a buffer empty debug message [skip ci] --- player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/player.c b/player.c index e15de80e..e6c93f8c 100644 --- a/player.c +++ b/player.c @@ -2687,7 +2687,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) { // wait if the buffer is empty if ((conn->ab_synced != 0) && (conn->ab_read == conn->ab_write)) { // the buffer is empty! if (notified_buffer_empty == 0) { - debug(2, "Connection %d: Buffer Empty", conn->connection_number); + debug(4, "Connection %d: Buffer Empty", conn->connection_number); notified_buffer_empty = 1; // reset_input_flow_metrics(conn); // don't do a full flush parameters reset // conn->initial_reference_time = 0; From 0291d3656e52fbd1b351f4ace3a1888a54e2c5e9 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:04:38 +0100 Subject: [PATCH 22/31] Add warnings if string or boolean parameters were present but not strings. --- common.c | 53 +++++++++++++++++++++++++++++++++++------------------ common.h | 2 +- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/common.c b/common.c index e552af4c..5b3f8feb 100644 --- a/common.c +++ b/common.c @@ -1041,32 +1041,49 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) { #endif int config_lookup_non_empty_string(const config_t *cfg, const char *path, const char **value) { - int response = config_lookup_string(cfg, path, value); - if (response == CONFIG_TRUE) { - if ((value != NULL) && ((*value == NULL) || (*value[0] == 0))) { - warn("The \"%s\" parameter is an empty string and has been ignored.", path); - response = CONFIG_FALSE; + int response = CONFIG_FALSE; + config_setting_t *s = config_lookup(cfg, path); + if (s != NULL) { + // the setting exists, but might not be a string + if (config_setting_type(s) == CONFIG_TYPE_STRING) { + if (value != NULL) { + *value = config_setting_get_string(s); + response = CONFIG_TRUE; + // the string might be empty... + if ((*value == NULL) || (*value[0] == 0)) { + warn("The \"%s\" parameter is an empty string and has been ignored.", path); + response = CONFIG_FALSE; + } + } + } else { + warn("the \"%s\" parameter is not a string, as required, and has been ignored.", path); } } return response; } -int config_set_lookup_bool(config_t *cfg, char *where, int *dst) { - const char *str = 0; - if (config_lookup_string(cfg, where, &str)) { - if (strcasecmp(str, "no") == 0) { - (*dst) = 0; - return 1; - } else if (strcasecmp(str, "yes") == 0) { - (*dst) = 1; - return 1; +int config_set_lookup_bool(config_t *cfg, const char *where, int *dst) { + const char *str = NULL; + int response = CONFIG_FALSE; + config_setting_t *s = config_lookup(cfg, where); + if (s != NULL) { + if (config_setting_type(s) == CONFIG_TYPE_STRING) { + str = config_setting_get_string(s); + if (strcasecmp(str, "no") == 0) { + (*dst) = 0; + response = CONFIG_TRUE; + } else if (strcasecmp(str, "yes") == 0) { + (*dst) = 1; + response = CONFIG_TRUE; + } else { + die("invalid boolean parameter \"%s\" option choice \"%s\". It should be \"yes\" or \"no\"", where, str); + return 0; + } } else { - die("Invalid %s option choice \"%s\". It should be \"yes\" or \"no\"", where, str); - return 0; + warn("the \"%s\" parameter is not a string with a value of \"yes\" or \"no\", as required, and has been ignored.", where); } - } else { - return 0; } + return response; } // remember to free the returned array of strings. diff --git a/common.h b/common.h index bf472829..7e35e6ab 100644 --- a/common.h +++ b/common.h @@ -561,7 +561,7 @@ extern int type_of_exit_cleanup; // normal, emergency, dbus requested... extern uint64_t minimum_dac_queue_size; int config_lookup_non_empty_string(const config_t *cfg, const char *path, const char **value); -int config_set_lookup_bool(config_t *cfg, char *where, int *dst); +int config_set_lookup_bool(config_t *cfg, const char *where, int *dst); int check_string_or_list_setting(config_setting_t *setting, const char *item); int check_int_or_list_setting(config_setting_t *setting, const int item); From d84c7333806afcda8172d73619791f299ddbd729 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:07:56 +0100 Subject: [PATCH 23/31] Make the soxr delay checker thread behave better when being cancelled. --- shairport.c | 58 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/shairport.c b/shairport.c index 448f17e6..3c708322 100644 --- a/shairport.c +++ b/shairport.c @@ -195,13 +195,12 @@ int has_fltp_capable_aac_decoder(void) { #endif #ifdef CONFIG_SOXR -pthread_t soxr_time_check_thread; -int soxr_time_check_thread_started = 0; +pthread_t *soxr_time_check_thread = NULL; void *soxr_time_check(__attribute__((unused)) void *arg) { // this just checks how long it takes to process adding and subtracing a frame // from a buffer at 44100 - // #include - // debug(1, "soxr_time_check PID %d", syscall(SYS_gettid)); + // #include + // debug(1, "soxr_time_check PID %ld", syscall(SYS_gettid)); const int buffer_length = 352; int32_t inbuffer[buffer_length * 2]; @@ -221,7 +220,7 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { (uint64_t)1500000000 + soxr_start_time; // loop for a second and a half, max -- no need to be // able to cancel it, do _don't even try_! while (get_absolute_time_in_ns() < loop_until_time) { - + pthread_testcancel(); number_of_iterations++; for (i = 0; i < buffer_length; i++) { double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI / 44100); @@ -230,6 +229,9 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { inbuffer[i * 2 + 1] = wint; } + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + soxr_io_spec_t io_spec; io_spec.itype = SOXR_INT32_I; io_spec.otype = SOXR_INT32_I; @@ -256,6 +258,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { outbuffer, buffer_length - 1, &odone, // Output. &io_spec, // Input, output and transfer spec. NULL, NULL); // Default configuration. + + pthread_setcancelstate(oldState, NULL); } int64_t soxr_execution_time = @@ -271,8 +275,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { debug(1, "No soxr-timing iterations performed, so \"vernier\" iteration will be used."); config.soxr_delay_index = 0; // used as a flag } - debug(2, "soxr_delay: %d nanoseconds, soxr_delay_threshold: %d milliseconds.", - config.soxr_delay_index, config.soxr_delay_threshold / 1000000); + debug(2, "soxr_delay maximum permissible: %d milliseconds, actual: %f milliseconds.", + config.soxr_delay_threshold / 1000000, 1E-6 * config.soxr_delay_index); if ((config.packet_stuffing == ST_soxr) && (config.soxr_delay_index > config.soxr_delay_threshold)) inform("Note: this device may be too slow for \"soxr\" interpolation. Consider choosing the " @@ -1847,6 +1851,7 @@ void exit_rtsp_listener() { } void exit_function() { + debug(1, "exit_function called"); if (type_of_exit_cleanup != TOE_emergency) { // the following is to ensure that if libdaemon has been included // that most of this code will be skipped when the parent process is exiting @@ -1902,12 +1907,18 @@ void exit_function() { } #ifdef CONFIG_SOXR - // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down - if (soxr_time_check_thread_started != 0) { - debug(2, "Waiting for SoXr timecheck to terminate..."); - pthread_join(soxr_time_check_thread, NULL); - soxr_time_check_thread_started = 0; - debug(2, "Waiting for SoXr timecheck to terminate done"); + { + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + if (soxr_time_check_thread != NULL) { + debug(1, "Waiting for SoXr timecheck to terminate..."); + pthread_cancel(*soxr_time_check_thread); + pthread_join(*soxr_time_check_thread, NULL); + free(soxr_time_check_thread); + soxr_time_check_thread = NULL; + debug(1, "SoXr timecheck terminated"); + } + pthread_setcancelstate(oldState, NULL); } #endif @@ -2346,6 +2357,7 @@ int main(int argc, char **argv) { openlog(NULL, 0, LOG_DAEMON); #endif type_of_exit_cleanup = TOE_normal; // what kind of exit cleanup needed + debug(1, "adding the exit function"); atexit(exit_function); // get a device id -- the first non-local MAC address @@ -2957,7 +2969,9 @@ int main(int argc, char **argv) { config.output_channel_map[i] = strdup(channel_id); debug(2, "output channel %d is \"%s\".", i, config.output_channel_map[i]); } else { - warn("channel \"%s\" is not recognised -- output channel %d will be silent.", + + + warn("during channel mapping, \"%s\" was not recognised as a channel name -- as a result, output channel %d will be silent.", channel_id, i); config.output_channel_map[i] = strdup("--"); } @@ -3153,8 +3167,20 @@ int main(int argc, char **argv) { debug(option_print_level, "loudness reference level is %f", config.loudness_reference_volume_db); #ifdef CONFIG_SOXR - named_pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL, "soxr_checker"); - soxr_time_check_thread_started = 1; + + { + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + soxr_time_check_thread = malloc(sizeof(pthread_t)); + if (soxr_time_check_thread != NULL) { + named_pthread_create(soxr_time_check_thread, NULL, &soxr_time_check, NULL, "soxr_checker"); + } else { + debug(1,"couldn't get memory to start the soxr_checker"); + } + pthread_setcancelstate(oldState, NULL); // make this un-cancellable + + } + #endif #ifdef CONFIG_FFMPEG From c62c34a67e3174382ffe15685a5a2df2ff90e21a Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:42:46 +0100 Subject: [PATCH 24/31] Remove some debug messages. --- shairport.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shairport.c b/shairport.c index 3c708322..d58f2743 100644 --- a/shairport.c +++ b/shairport.c @@ -1851,7 +1851,6 @@ void exit_rtsp_listener() { } void exit_function() { - debug(1, "exit_function called"); if (type_of_exit_cleanup != TOE_emergency) { // the following is to ensure that if libdaemon has been included // that most of this code will be skipped when the parent process is exiting @@ -1861,7 +1860,6 @@ void exit_function() { (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not // actually daemonised at all #endif - debug(2, "exit function called..."); /* Actually, there is no terminate_mqtt() function. #ifdef CONFIG_MQTT @@ -1911,12 +1909,10 @@ void exit_function() { int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable if (soxr_time_check_thread != NULL) { - debug(1, "Waiting for SoXr timecheck to terminate..."); pthread_cancel(*soxr_time_check_thread); pthread_join(*soxr_time_check_thread, NULL); free(soxr_time_check_thread); soxr_time_check_thread = NULL; - debug(1, "SoXr timecheck terminated"); } pthread_setcancelstate(oldState, NULL); } @@ -2005,7 +2001,7 @@ void exit_function() { #else mdns_unregister(); // once the dacp handler is done and all player threrads are done it should // be safe - debug(1, "normal exit"); + debug(2, "normal exit"); #endif } else { debug(1, "emergency exit"); From 2474037b0c1da493c5600857d7106cb4253a95a1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:56:23 +0100 Subject: [PATCH 25/31] 5.0.3-dev and add some diagnostics around a pesky fault condition. --- configure.ac | 2 +- rtp.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 5fb90de3..1b99df38 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [5.0.1+], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [5.0.3-dev], [4265913+mikebrady@users.noreply.github.com]) : ${CFLAGS="-O3"} : ${CXXFLAGS="-O3"} AM_INIT_AUTOMAKE([subdir-objects]) diff --git a/rtp.c b/rtp.c index 79032ede..02e7c167 100644 --- a/rtp.c +++ b/rtp.c @@ -1513,9 +1513,12 @@ int frame_to_ptp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info * int32_t frame_difference = timestamp - anchor_rtptime; int64_t time_difference = frame_difference; time_difference = time_difference * 1000000000; - if (conn->input_rate == 0) - die("conn->input_rate is zero!"); - time_difference = time_difference / conn->input_rate; + if (conn->input_rate == 0) { + debug(1,"in a call to frame_to_ptp_local_time, conn->input_rate is zero!"); + time_difference = 0; + } else { + time_difference = time_difference / conn->input_rate; + } uint64_t ltime = anchor_local_time + time_difference; *time = ltime; result = 0; From b585f8c0893b2dd197ac70d937947ffc2b5af0e2 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:27:49 +0100 Subject: [PATCH 26/31] Update release notes for version 5.0.3-dev [skip ci] Added version 5.0.3-dev release notes with small changes and build update. --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index d3492eb8..18966ad5 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Version 5.0.3-dev +== +**Small Changes** +* Add warnings if string or boolean configuration parameters were present but not strings. +* Improve behaviour when there is a large timing uncertainty/granularity and a large audio buffer. e.g. on some virtual machines. +* Make the SoxR delay checker thread behave better when being cancelled. +* Remove some debug messages, quieten some others, add a few. + Version 5.0.1+-38-gf784e463 == **Build Update** From 5e0e12bebe79b5aa2601e49ec99500ef3e078f7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:02:31 +0000 Subject: [PATCH 27/31] Bump docker/build-push-action from 7.0.0 to 7.1.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.0.0 to 7.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v7.0.0...v7.1.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-on-push-tag-or-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-on-push-tag-or-pr.yaml b/.github/workflows/docker-on-push-tag-or-pr.yaml index 23b5eec6..c3380f4e 100644 --- a/.github/workflows/docker-on-push-tag-or-pr.yaml +++ b/.github/workflows/docker-on-push-tag-or-pr.yaml @@ -73,7 +73,7 @@ jobs: uses: docker/setup-buildx-action@v4.0.0 - name: Build and push ${{ matrix.name }} - uses: docker/build-push-action@v7.0.0 + uses: docker/build-push-action@v7.1.0 env: registry_and_name: ${{ secrets.DOCKER_REGISTRY }}/${{ secrets.DOCKER_IMAGE_NAME }} with: From 07ca1891da589951380f257e76609f5915a68363 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:31:32 +0100 Subject: [PATCH 28/31] Make code using getifaddrs uncancellable to prevent a "Unexpected error 9 on netlink descriptor" error. --- rtsp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rtsp.c b/rtsp.c index 7da2ffaf..68ad798c 100644 --- a/rtsp.c +++ b/rtsp.c @@ -2828,6 +2828,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) plist_array_append_item(addresses, plist_new_string(conn->self_ip_string)); // debug(1,"self ip: \"%s\"", conn->self_ip_string); + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); struct ifaddrs *addrs, *iap; getifaddrs(&addrs); for (iap = addrs; iap != NULL; iap = iap->ifa_next) { @@ -2862,6 +2864,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) } } freeifaddrs(addrs); + pthread_setcancelstate(oldState, NULL); // debug(1,"initial timing peer command: \"%s\".", timing_list_message); // ptp_send_control_message_string(timing_list_message); From 2537e7ff85ae0be73be97702a4a7c98e014a2cde Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:37:42 +0100 Subject: [PATCH 29/31] Update release notes for version 5.0.3-dev-4 [skip ci] Fixed an issue with getifaddrs() to prevent an uncancellable error. --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 18966ad5..b12b9aa9 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 5.0.3-dev-4-g07ca1891 +== +**Bug Fix** +* Make code using `getifaddrs()` uncancellable to prevent a mysterious `Unexpected error 9 on netlink descriptor` error. Thanks to [microfx](https://github.com/microfx) for reporting the [issue](https://github.com/mikebrady/shairport-sync/issues/2184). + Version 5.0.3-dev == **Small Changes** From 61f1730402e215f69b17469c5e50e407dbc2c5af Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 25 Apr 2026 11:23:41 +0100 Subject: [PATCH 30/31] Update release notes for version 5.0.3 [skip ci] --- RELEASENOTES-DEVELOPMENT.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index b12b9aa9..83b603a3 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,5 +1,8 @@ Version 5.0.3-dev-4-g07ca1891 == +**Release** +* This is effectively release 5.0.3 + **Bug Fix** * Make code using `getifaddrs()` uncancellable to prevent a mysterious `Unexpected error 9 on netlink descriptor` error. Thanks to [microfx](https://github.com/microfx) for reporting the [issue](https://github.com/mikebrady/shairport-sync/issues/2184). From eba76c21af5401ed4cf719417229976d3401c61a Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 25 Apr 2026 11:33:40 +0100 Subject: [PATCH 31/31] 5.0.4-dev --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1b99df38..74d13192 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [5.0.3-dev], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [5.0.4-dev], [4265913+mikebrady@users.noreply.github.com]) : ${CFLAGS="-O3"} : ${CXXFLAGS="-O3"} AM_INIT_AUTOMAKE([subdir-objects])