From 9809f9d4be29fa3af71012e343130aa0a1343a6c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 6 Jul 2024 12:09:08 +0100 Subject: [PATCH 01/61] 4.3.5 starting point --- RELEASENOTES.md | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 64d85f21..8f98d434 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,7 @@ +Minor Release Notes +==== +Minor release notes are attached to the release themselves. + Version 4.3 -- Security Updates, Bug Fixes and Enhancements ==== This update contains important security updates, bug fixes and enhancements. [NQPTP](https://github.com/mikebrady/nqptp) must also be updated, and it should be updated before updating Shairport Sync. diff --git a/configure.ac b/configure.ac index e6295413..14993857 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [4.3.4-dev], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [4.3.5-dev], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 9909bc21b74fcabf35a3b2d47b353c5f42d540d1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:25:15 +0100 Subject: [PATCH 02/61] Use (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO now in place of bit-mapped AV_CH_LAYOUT_STEREO -- possible from FFmpeg 5.1, mandatory in FFmpeg 7. Also remove deprecated avcodec_close. --- rtp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rtp.c b/rtp.c index 0f35f034..e44ff253 100644 --- a/rtp.c +++ b/rtp.c @@ -2097,7 +2097,7 @@ void avcodec_alloc_context3_cleanup_handler(void *arg) { void avcodec_open2_cleanup_handler(void *arg) { debug(3, "avcodec_open2_cleanup_handler"); AVCodecContext *codec_context = arg; - avcodec_close(codec_context); + avcodec_free_context(&codec_context); } void av_parser_init_cleanup_handler(void *arg) { @@ -2313,8 +2313,15 @@ void *rtp_buffered_audio_processor(void *arg) { // push a deallocator -- av_packet_free(pkt); pthread_cleanup_push(swr_alloc_cleanup_handler, &swr); + +// FFmpeg 5.1 or later... +#if LIBAVUTIL_VERSION_MAJOR >= 57 + av_opt_set_chlayout(swr, "in_chlayout", &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO, 0); + av_opt_set_chlayout(swr, "out_chlayout", &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO, 0); +#else av_opt_set_int(swr, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0); av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); +#endif av_opt_set_int(swr, "in_sample_rate", conn->input_rate, 0); av_opt_set_int(swr, "out_sample_rate", conn->input_rate, 0); // must match or the timing will be wrong` From bde609c755b3e0df1bc8dcf202677f1dfd476a43 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:32:23 +0100 Subject: [PATCH 03/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 607abf9f..895c5f14 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 4.3.5-dev-1-g9909bc21 +== +**Bug Fix** +* Use `(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO` now in place of bit-mapped `AV_CH_LAYOUT_STEREO` when setting up the FFmpeg software resampler with `swr_init()`. It was possible to do this starting at FFmpeg 5.1, but is mandatory in FFmpeg 7. Also remove now-deprecated `avcodec_close`. Thanks to [Deyan Dragov](https://github.com/itsdeyan) for the [report](https://github.com/mikebrady/shairport-sync/issues/1876). + Version 4.3.4-dev-25-g15a7090a == **Change** From e28c566a7db944ce349342525b2eec4ea3d981ad Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:58:37 +0100 Subject: [PATCH 04/61] Remove the avcodec_free_context(&codec_context) -- seems to cause a double free. --- rtp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtp.c b/rtp.c index e44ff253..93e3fc84 100644 --- a/rtp.c +++ b/rtp.c @@ -2094,10 +2094,10 @@ void avcodec_alloc_context3_cleanup_handler(void *arg) { av_free(codec_context); } -void avcodec_open2_cleanup_handler(void *arg) { - debug(3, "avcodec_open2_cleanup_handler"); - AVCodecContext *codec_context = arg; - avcodec_free_context(&codec_context); +void avcodec_open2_cleanup_handler(__attribute__((unused)) void *arg) { + debug(3, "avcodec_open2_cleanup_handler -- does nothing right now"); + // AVCodecContext *codec_context = arg; + // avcodec_free_context(&codec_context); } void av_parser_init_cleanup_handler(void *arg) { From 58277920810f57f2f13f98fa3226089505190bed Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 16 Jul 2024 13:02:44 +0100 Subject: [PATCH 05/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 895c5f14..20d0dbc0 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 4.3.5-dev-3-ge28c566a +== +**Bug Fix** +* Fix a double-free fault introduced by replacing `avcodec_close` with `avcodec_free_context`. It seems as if the context is freed by a subsequent `av_free` anyway... Sigh. + Version 4.3.5-dev-1-g9909bc21 == **Bug Fix** From 10b965a7aae0e7a10385d08aef9de92521160e18 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:26:55 +0100 Subject: [PATCH 06/61] add a diagnostic function debug_print_buffer, which might be useful in the future. --- common.c | 32 ++++++++++++++++++++++++++++++++ common.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/common.c b/common.c index 0e2a0724..42411c5f 100644 --- a/common.c +++ b/common.c @@ -574,6 +574,38 @@ void _inform(const char *thefilename, const int linenumber, const char *format, pthread_setcancelstate(oldState, NULL); } +void _debug_print_buffer(const char *thefilename, const int linenumber, int level, void *vbuf, + size_t buf_len) { + if (level > debuglev) + return; + char *buf = (char *)vbuf; + char *obf = + malloc(buf_len * 4 + 1); // to be on the safe side -- 4 characters on average for each byte + if (obf != NULL) { + char *obfp = obf; + unsigned int obfc; + for (obfc = 0; obfc < buf_len; obfc++) { + snprintf(obfp, 3, "%02X", buf[obfc]); + obfp += 2; + if (obfc != buf_len - 1) { + if (obfc % 32 == 31) { + snprintf(obfp, 5, " || "); + obfp += 4; + } else if (obfc % 16 == 15) { + snprintf(obfp, 4, " | "); + obfp += 3; + } else if (obfc % 4 == 3) { + snprintf(obfp, 2, " "); + obfp += 1; + } + } + }; + *obfp = 0; + _debug(thefilename, linenumber, level, "%s", obf); + free(obf); + } +} + // The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample // code at // https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux diff --git a/common.h b/common.h index a1d50d44..9de26f63 100644 --- a/common.h +++ b/common.h @@ -382,11 +382,14 @@ void _die(const char *filename, const int linenumber, const char *format, ...); void _warn(const char *filename, const int linenumber, const char *format, ...); void _inform(const char *filename, const int linenumber, const char *format, ...); void _debug(const char *filename, const int linenumber, int level, const char *format, ...); +void _debug_print_buffer(const char *thefilename, const int linenumber, int level, void *buf, + size_t buf_len); #define die(...) _die(__FILE__, __LINE__, __VA_ARGS__) #define debug(...) _debug(__FILE__, __LINE__, __VA_ARGS__) #define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__) #define inform(...) _inform(__FILE__, __LINE__, __VA_ARGS__) +#define debug_print_buffer(...) _debug_print_buffer(__FILE__, __LINE__, __VA_ARGS__) uint8_t *base64_dec(char *input, int *outlen); char *base64_enc(uint8_t *input, int length); From 597aba0ca6f0ae15e862352e117b4d8d094f8060 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:28:30 +0100 Subject: [PATCH 07/61] Use an explicit 'is_running' flag in the sndio back end. Correct/improve some sndio documentation. --- audio_sndio.c | 94 +++++++++++++++++++++++-------------- player.c | 7 +-- scripts/shairport-sync.conf | 2 +- 3 files changed, 65 insertions(+), 38 deletions(-) diff --git a/audio_sndio.c b/audio_sndio.c index a369bfb5..9fa1ea98 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -4,7 +4,7 @@ * Copyright (c) 2017 Tobias Kortkamp * * Modifications for audio synchronisation - * and related work, copyright (c) Mike Brady 2014 -- 2022 + * and related work, copyright (c) Mike Brady 2014 -- 2024 * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any @@ -30,6 +30,7 @@ static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER; static struct sio_hdl *hdl; +static int is_running; static int framesize; static size_t played; static size_t written; @@ -58,7 +59,9 @@ static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, S {"S24_3BE", SPS_FORMAT_S24_3BE, 44100, 24, 3, 1, 0}, {"S32", SPS_FORMAT_S32, 44100, 32, 4, 1, SIO_LE_NATIVE}}; -static void help() { printf(" -d output-device set the output device [default*|...]\n"); } +static void help() { + printf(" -d output-device set the output device [default|rsnd/0|rsnd/1...]\n"); +} void onmove_cb(__attribute__((unused)) void *arg, int delta) { time_of_last_onmove_cb = get_absolute_time_in_ns(); @@ -155,6 +158,7 @@ static int init(int argc, char **argv) { debug(1, "sndio: rate: %u.", par.rate); debug(1, "sndio: bits: %u.", par.bits); + is_running = 0; hdl = sio_open(devname, SIO_PLAY, 0); if (!hdl) die("sndio: cannot open audio device"); @@ -206,26 +210,15 @@ static int init(int argc, char **argv) { } static void deinit() { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - sio_close(hdl); - // pthread_mutex_unlock(&sndio_mutex); - pthread_cleanup_pop(1); // unlock the mutex -} - -static void start(__attribute__((unused)) int sample_rate, - __attribute__((unused)) int sample_format) { - // pthread_mutex_lock(&sndio_mutex); - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - at_least_one_onmove_cb_seen = 0; - // any previously-reported frame count - - if (!sio_start(hdl)) - die("sndio: unable to start"); - written = played = 0; - time_of_last_onmove_cb = 0; - at_least_one_onmove_cb_seen = 0; - // pthread_mutex_unlock(&sndio_mutex); + if (hdl != NULL) { + if (is_running != 0) { + sio_flush(hdl); + is_running = 0; + } + sio_close(hdl); + hdl = NULL; + } pthread_cleanup_pop(1); // unlock the mutex } @@ -233,10 +226,20 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type, __attribute__((unused)) uint32_t timestamp, __attribute__((unused)) uint64_t playtime) { if (frames > 0) { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); + if (is_running == 0) { + if (hdl != NULL) { + if (sio_start(hdl) != 1) + debug(1, "sndio: unable to start"); + is_running = 1; + written = played = 0; + time_of_last_onmove_cb = 0; + at_least_one_onmove_cb_seen = 0; + } else { + debug(1, "sndio: output device is not open for play!"); + } + } written += sio_write(hdl, buf, frames * framesize); - // pthread_mutex_unlock(&sndio_mutex); pthread_cleanup_pop(1); // unlock the mutex } return 0; @@ -244,10 +247,17 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type, static void stop() { pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - - if (!sio_stop(hdl)) - die("sndio: unable to stop"); - written = played = 0; + if (hdl != NULL) { + if (is_running != 0) { + if (sio_flush(hdl) != 1) + debug(1, "sndio: unable to stop"); + written = played = is_running = 0; + } else { + debug(1, "sndio: stop: not running."); + } + } else { + debug(1, "sndio: output device is not open for stop!"); + } pthread_cleanup_pop(1); // unlock the mutex } @@ -276,18 +286,34 @@ int get_delay(long *delay) { static int delay(long *delay) { int result = 0; pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - result = get_delay(delay); + if (hdl != NULL) { + if (is_running != 0) { + result = get_delay(delay); + } else { + debug(1, "sndio: output device is not open for delay!"); + if (delay != NULL) + *delay = 0; + } + } else { + debug(1, "sndio: output device is not open for delay!"); + } pthread_cleanup_pop(1); // unlock the mutex return result; } static void flush() { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (!sio_stop(hdl) || !sio_start(hdl)) - die("sndio: unable to flush"); - written = played = 0; - // pthread_mutex_unlock(&sndio_mutex); + if (hdl != NULL) { + if (is_running != 0) { + if (sio_flush(hdl) != 1) + debug(1, "sndio: unable to flush"); + written = played = is_running = 0; + } else { + debug(1, "sndio: flush: not running."); + } + } else { + debug(1, "sndio: output device is not open for flush!"); + } pthread_cleanup_pop(1); // unlock the mutex } @@ -296,7 +322,7 @@ audio_output audio_sndio = {.name = "sndio", .init = &init, .deinit = &deinit, .prepare = NULL, - .start = &start, + .start = NULL, .stop = &stop, .is_running = NULL, .flush = &flush, diff --git a/player.c b/player.c index 46ef100f..11b0c538 100644 --- a/player.c +++ b/player.c @@ -2141,9 +2141,10 @@ void *player_thread_func(void *arg) { if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) || (config.playback_mode == ST_mono)) conn->enable_dither = 1; - - // remember, the output device may never have been initialised prior to this call - config.output->start(config.output_rate, config.output_format); // will need a corresponding stop + + // call the backend's start() function if it exists. + if (config.output->start != NULL) + config.output->start(config.output_rate, config.output_format); // we need an intermediate "transition" buffer diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 78a79c7e..517a7560 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -157,7 +157,7 @@ pw = // --with-sndio sndio = { -// device = "snd/0"; // optional setting to set the name of the output device. Default is the sndio system default. +// device = "default"; // optional setting to set the name of the output device, e.g. "rsnd/0", "rsnd/1", etc. // rate = 44100; // optional setting which can be 44100, 88200, 176400 or 352800, but the device must have the capability. Default is 44100. // format = "S16"; // optional setting which can be "U8", "S8", "S16", "S24", "S24_3LE", "S24_3BE" or "S32", but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor. // round = ; // advanced optional setting to set the period size near to this value From 10bef383501447b2c34e68ec7a9dcbf796d5ffd4 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:47:53 +0100 Subject: [PATCH 08/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 20d0dbc0..f64e104c 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Version 4.3.5-dev-6-g597aba0c +== +**Bug Chasing** +* Modify the `sndio` backend (native to OpenBSD, also used in FreeBSD) to try to deal with an intermittent bug. Specifically, use an explicit `is_running` flag to keep track of the playing status of the backend. + +**Configuration Comment Update** +* Update comments in the `sndio` section of the sample configuration file. + Version 4.3.5-dev-3-ge28c566a == **Bug Fix** From 0f051384d9463d90c5c5d2048f72ec1b75e73011 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:46:29 +0100 Subject: [PATCH 09/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index bc2d912d..c2920571 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -3,22 +3,22 @@ name: Configuration (without pa, soundio or apple-alac) for systemd. on: workflow_dispatch: push: - branches: [ "development", "danger" ] + branches: [ "development" ] pull_request: types: [opened, synchronize, reopened, ready_for_review] jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure run: | - autoreconf -i + autoreconf -fi ./configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-jack --with-pipe --with-sndio --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 - name: Make run: | From e6d244a7eefc90924d2f6f85335c9cbc24c6dbf0 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:51:39 +0100 Subject: [PATCH 10/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index c2920571..4620cba9 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev - name: Configure run: | autoreconf -fi From 5b57ed0c386ab03d319428409c70a5c59ca1791f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:55:53 +0100 Subject: [PATCH 11/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 4620cba9..64ec4fd9 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev - name: Configure run: | autoreconf -fi From 4b4080e09726835c3239c8cba1b57b08b5684ef0 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:13:53 +0100 Subject: [PATCH 12/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 64ec4fd9..29bce2d6 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 From 66bfcb0a4fac1e3413717a7f6da147b10ad58e58 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:18:01 +0100 Subject: [PATCH 13/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 29bce2d6..875e4219 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev - name: Configure run: | autoreconf -fi From 0ae197f23d5a437eee9ab5aaec1a3ac2bd79ccec Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:31:56 +0100 Subject: [PATCH 14/61] Update check_ap2_systemd_basic.yml --- .github/workflows/check_ap2_systemd_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_basic.yml b/.github/workflows/check_ap2_systemd_basic.yml index 36749564..10d910b9 100644 --- a/.github/workflows/check_ap2_systemd_basic.yml +++ b/.github/workflows/check_ap2_systemd_basic.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v3.5.2 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure run: | mkdir build From 6ef57dc4689ab70fb274768b70eae895b63c942c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:32:19 +0100 Subject: [PATCH 15/61] Update check_ap2_systemd_full.yml --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 875e4219..9deb930a 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 From 5c6c1e80851fd5b8bc65f594e38849ed36c651d5 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:32:36 +0100 Subject: [PATCH 16/61] Update check_ap2_systemd_basic.yml --- .github/workflows/check_ap2_systemd_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_basic.yml b/.github/workflows/check_ap2_systemd_basic.yml index 10d910b9..28e59476 100644 --- a/.github/workflows/check_ap2_systemd_basic.yml +++ b/.github/workflows/check_ap2_systemd_basic.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure From 813afefe5902892f83407600c96f16e6744b1312 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:33:18 +0100 Subject: [PATCH 17/61] Update check_ap2_systemv_full.yml --- .github/workflows/check_ap2_systemv_full.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_ap2_systemv_full.yml b/.github/workflows/check_ap2_systemv_full.yml index 3f46c0d6..fdd7177a 100644 --- a/.github/workflows/check_ap2_systemv_full.yml +++ b/.github/workflows/check_ap2_systemv_full.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure run: | autoreconf -i - ./configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-libdaemon --with-jack --with-pipe --with-sndio --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemv --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 + ./configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-libdaemon --with-jack --with-pipe --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemv --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 - name: Make run: | make -j From 17cb83e539046948dbdc18a49eb969a083edc76e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:33:55 +0100 Subject: [PATCH 18/61] Update check_classic_mac_basic.yml --- .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 1496be9a..038ded2e 100644 --- a/.github/workflows/check_classic_mac_basic.yml +++ b/.github/workflows/check_classic_mac_basic.yml @@ -3,7 +3,7 @@ name: Basic libao configuration for macOS with BREW -- classic only, because mac on: workflow_dispatch: push: - branches: [ "development", "danger" ] + branches: [ "development" ] pull_request: types: [opened, synchronize, reopened, ready_for_review] From a1af442801ea15f98fb124e342870c71cbcdbb83 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:34:37 +0100 Subject: [PATCH 19/61] Update check_classic_systemd_basic.yml --- .github/workflows/check_classic_systemd_basic.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_classic_systemd_basic.yml b/.github/workflows/check_classic_systemd_basic.yml index ead6de27..c28cd6f7 100644 --- a/.github/workflows/check_classic_systemd_basic.yml +++ b/.github/workflows/check_classic_systemd_basic.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev - name: Configure run: | mkdir build From 64ae434b7ae58a1cef311e8084556ef5bfce475f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:35:49 +0100 Subject: [PATCH 20/61] Update check_ap2_systemd_full_build_folder.yml --- .github/workflows/check_ap2_systemd_full_build_folder.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_ap2_systemd_full_build_folder.yml b/.github/workflows/check_ap2_systemd_full_build_folder.yml index 98f964cb..0e547a15 100644 --- a/.github/workflows/check_ap2_systemd_full_build_folder.yml +++ b/.github/workflows/check_ap2_systemd_full_build_folder.yml @@ -15,13 +15,13 @@ jobs: steps: - uses: actions/checkout@v3.5.2 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure run: | mkdir build cd build autoreconf -i .. - ../configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-jack --with-pipe --with-sndio --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 + ../configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-jack --with-pipe --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 - name: Make run: | cd build From b60029ccd472e959ca56e5338e90e5b16f852029 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:36:34 +0100 Subject: [PATCH 21/61] Update check_ap2_systemv_full.yml --- .github/workflows/check_ap2_systemv_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemv_full.yml b/.github/workflows/check_ap2_systemv_full.yml index fdd7177a..3723f9d4 100644 --- a/.github/workflows/check_ap2_systemv_full.yml +++ b/.github/workflows/check_ap2_systemv_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd - name: Configure run: | autoreconf -i From 8f99d1f40704e543115b37c5e5266a11a32fe0c5 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:37:36 +0100 Subject: [PATCH 22/61] Update check_classic_systemd_full.yml --- .github/workflows/check_classic_systemd_full.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_classic_systemd_full.yml b/.github/workflows/check_classic_systemd_full.yml index 4d289c53..ea153e96 100644 --- a/.github/workflows/check_classic_systemd_full.yml +++ b/.github/workflows/check_classic_systemd_full.yml @@ -3,7 +3,7 @@ name: Classic (without pa, soundio, apple-alac) for systemd, using a build folde on: workflow_dispatch: push: - branches: [ "development", "danger" ] + branches: [ "development" ] pull_request: types: [opened, synchronize, reopened, ready_for_review] @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install build-essential git xmltoman autoconf automake libtool libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev - name: Configure run: | mkdir build From 821551492323499455609710823fe34247885e27 Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 18 Jul 2024 22:23:13 +0300 Subject: [PATCH 23/61] Compile docker image with pipewire and add example in docker-compose --- docker/Dockerfile | 4 +++- docker/docker-compose.yaml | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e47dc5d7..c44ff267 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,6 +23,7 @@ RUN apk -U add \ libsndfile-dev \ libsodium-dev \ libtool \ + pipewire-dev \ mosquitto-dev \ popt-dev \ pulseaudio-dev \ @@ -58,7 +59,7 @@ RUN autoreconf -i ../ RUN CFLAGS="-O3" CXXFLAGS="-O3" ../configure --sysconfdir=/etc --with-alsa --with-pa --with-soxr --with-avahi --with-ssl=openssl \ --with-airplay-2 --with-metadata --with-dummy --with-pipe --with-dbus-interface \ --with-stdout --with-mpris-interface --with-mqtt-client \ - --with-apple-alac --with-convolution + --with-apple-alac --with-convolution --with-pw RUN make -j $(nproc) RUN DESTDIR=install make install WORKDIR / @@ -87,6 +88,7 @@ RUN apk -U add \ libsndfile \ libsodium \ libuuid \ + pipewire \ man-pages \ mandoc \ mosquitto \ diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 82f5632f..8a435bba 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -9,11 +9,14 @@ services: # S6_KEEP_ENV: 1 # Allow S6 to pass environment variables from compose file # PULSE_SERVER: unix:/tmp/pulseaudio.socket # Path for PulseAudio socket # PULSE_COOKIE: /tmp/pulseaudio.cookie # Path for PulseAudio cookie + # XDG_RUNTIME_DIR: /tmp # Path for pipewire devices: - "/dev/snd" # ALSA device, omit if using PulseAudio # volumes: # - ./volumes/shairport-sync/shairport-sync.conf:/etc/shairport-sync.conf # Customised Shairport Sync configuration file. # - /run/user/1000/pulse/native:/tmp/pulseaudio.socket # PulseAudio socket when using that backend + # - /run/user/1000/pipewire-0:/tmp/pipewire-0 # Pipewire socket when using pipewire + # command: -o pw # You can specify the desired output with command: logging: options: max-size: "200k" From 9650990523a719768fcedd234fa5c0dcff2185ec Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:08:48 +0100 Subject: [PATCH 24/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index f64e104c..fadc0f6e 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 4.3.5-dev-23-g1687b2a4 +== +**Docker Enhancement** +* The AirPlay 2 Docker image now incorporates the PipeWire backend. Thanks to [Maxim](https://github.com/irdkwmnsb) for the [PR](https://github.com/mikebrady/shairport-sync/pull/1880). + Version 4.3.5-dev-6-g597aba0c == **Bug Chasing** From 734f93a76ff5292a08d29a754428fe95a0b29978 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:08:55 +0100 Subject: [PATCH 25/61] Add a new call to the backends -- prepare_to_play() -- to give them advance warning that audio will be coming soon. Sent when the first frame is received. --- audio.h | 1 + 1 file changed, 1 insertion(+) diff --git a/audio.h b/audio.h index 583b2bbb..82b5409d 100644 --- a/audio.h +++ b/audio.h @@ -31,6 +31,7 @@ typedef struct { int (*init)(int argc, char **argv); // at end of program void (*deinit)(void); + void (*prepare_to_play)(void); // sent when audio is received for the first time -- advance warning. int (*prepare)(void); // looks and sets stuff in the config data structure From 67f422fb9d83ab58c061c77626afe981fb2d3abc Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:09:26 +0100 Subject: [PATCH 26/61] Add a new call to the backends -- prepare_to_play() -- to give them advance warning that audio will be coming soon. Sent when the first frame is received. --- player.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/player.c b/player.c index 11b0c538..102085a4 100644 --- a/player.c +++ b/player.c @@ -1179,6 +1179,10 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { if (conn->ab_buffering) { // if we are getting packets but not yet forwarding them to the // player if (conn->first_packet_timestamp == 0) { // if this is the very first packet + + if (config.output->prepare_to_play) // tell the player to get ready + config.output->prepare_to_play(); // there could be more than one of these sent + conn->first_packet_timestamp = curframe->given_timestamp; // we will keep buffering until we are // supposed to start playing this From 8e6bd06c036b6118d340ebf46ce930682697770d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:11:40 +0100 Subject: [PATCH 27/61] Go inactive when play stops. Use the new prepare_to_play() to go active ASAP when audio starts or resumes. --- audio_pw.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/audio_pw.c b/audio_pw.c index 76764d2f..13ca6e5e 100644 --- a/audio_pw.c +++ b/audio_pw.c @@ -56,6 +56,7 @@ static char *audio_lmb, *audio_umb, *audio_toq, *audio_eoq; static size_t audio_size = buffer_allocation; static size_t audio_occupancy; static int enable_fill; +static int stream_is_active; struct timing_data { int pw_time_is_valid; // set when the pw_time has been set @@ -270,7 +271,7 @@ static int init(__attribute__((unused)) int argc, __attribute__((unused)) char * PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, params, 1); - + stream_is_active = 1; pw_thread_loop_unlock(data.loop); return 0; } @@ -279,9 +280,27 @@ static void start(__attribute__((unused)) int sample_rate, __attribute__((unused)) int sample_format) { } +static void prepare_to_play() { + // debug(1, "prepare to play"); + if (stream_is_active == 0) { + pw_thread_loop_lock(data.loop); + pw_stream_set_active(data.stream, true); + pw_thread_loop_unlock(data.loop); + stream_is_active = 1; + debug(3, "prepare to play activating stream"); + } +} + static int play(__attribute__((unused)) void *buf, int samples, __attribute__((unused)) int sample_type, __attribute__((unused)) uint32_t timestamp, __attribute__((unused)) uint64_t playtime) { + if (stream_is_active == 0) { + pw_thread_loop_lock(data.loop); + pw_stream_set_active(data.stream, true); + pw_thread_loop_unlock(data.loop); + stream_is_active = 1; + debug(3, "set stream active"); + } // copy the samples into the queue debug(3, "play %u samples; %u bytes already in the buffer.", samples, audio_occupancy); size_t bytes_to_transfer = samples * DEFAULT_CHANNELS * DEFAULT_BYTES_PER_SAMPLE; @@ -382,8 +401,15 @@ static void stop(void) { // if (enable_fill == 0) { // debug(1, "stop enable_fill"); // } - enable_fill = 1; pthread_mutex_unlock(&buffer_mutex); + if (stream_is_active == 1) { + pw_thread_loop_lock(data.loop); + // pw_stream_flush(data.stream, true); + pw_stream_set_active(data.stream, false); + pw_thread_loop_unlock(data.loop); + stream_is_active = 0; + debug(3, "set stream inactive"); + } } audio_output audio_pw = {.name = "pw", @@ -398,6 +424,7 @@ audio_output audio_pw = {.name = "pw", .delay = &delay, .stats = NULL, .play = &play, + .prepare_to_play = &prepare_to_play, .volume = NULL, .parameters = NULL, .mute = NULL}; From f6dc04de150b063494a254d25d470fd45add9705 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:33:18 +0100 Subject: [PATCH 28/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index fadc0f6e..5a2c2007 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,12 +1,18 @@ +Version 4.3.5-dev-27-g8e6bd06c +== +**PipeWire Enhancement** +* Until now, the stream in the PipeWire backend remained permanently active. This meant that external progrmas could not tell when audio was really playing or not. Now the stream will go inactive (i.e. "paused") when play is stopped and active when play resumes. + Note that in AirPlay 2, play often stops momentarily between tracks, and so there will be momentary changes from active to inactive and back to active again in the PipeWire backend. This may change as the backend improves. Thanks to [Nemo157](https://github.com/Nemo157) for the Idea: https://github.com/mikebrady/shairport-sync/discussions/1889. + Version 4.3.5-dev-23-g1687b2a4 == **Docker Enhancement** -* The AirPlay 2 Docker image now incorporates the PipeWire backend. Thanks to [Maxim](https://github.com/irdkwmnsb) for the [PR](https://github.com/mikebrady/shairport-sync/pull/1880). +* The AirPlay 2 Docker image now incorporates the PipeWire backend. Thanks to [Maxim](https://github.com/irdkwmnsb) for the PR: https://github.com/mikebrady/shairport-sync/pull/1880. Version 4.3.5-dev-6-g597aba0c == -**Bug Chasing** -* Modify the `sndio` backend (native to OpenBSD, also used in FreeBSD) to try to deal with an intermittent bug. Specifically, use an explicit `is_running` flag to keep track of the playing status of the backend. +**FreeBSD Bug Fix** +* Modify the `sndio` backend (native to OpenBSD, also used in FreeBSD) to deal with an intermittent bug. Specifically, use an explicit `is_running` flag to keep track of the playing status of the backend. Thanks to [Jan Przybylak](https://github.com/janprzy), [Klemens Nanni](https://github.com/klemensn) and [Amanda Stjerna](https://github.com/amandasystems) for their help and persistence tracking down this problem. Resolves Issue: https://github.com/mikebrady/shairport-sync/issues/1765. **Configuration Comment Update** * Update comments in the `sndio` section of the sample configuration file. @@ -19,7 +25,7 @@ Version 4.3.5-dev-3-ge28c566a Version 4.3.5-dev-1-g9909bc21 == **Bug Fix** -* Use `(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO` now in place of bit-mapped `AV_CH_LAYOUT_STEREO` when setting up the FFmpeg software resampler with `swr_init()`. It was possible to do this starting at FFmpeg 5.1, but is mandatory in FFmpeg 7. Also remove now-deprecated `avcodec_close`. Thanks to [Deyan Dragov](https://github.com/itsdeyan) for the [report](https://github.com/mikebrady/shairport-sync/issues/1876). +* Use `(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO` now in place of bit-mapped `AV_CH_LAYOUT_STEREO` when setting up the FFmpeg software resampler with `swr_init()`. It was possible to do this starting at FFmpeg 5.1, but is mandatory in FFmpeg 7. Also remove now-deprecated `avcodec_close`. Thanks to [Deyan Dragov](https://github.com/itsdeyan) for the report: https://github.com/mikebrady/shairport-sync/issues/1876. Version 4.3.4-dev-25-g15a7090a == From 17b134b032f6377f1550e0d61153abf03e1e82d6 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:51:39 +0100 Subject: [PATCH 29/61] Initialise the PipeWire stream to be in the inactive state. --- audio_pw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio_pw.c b/audio_pw.c index 13ca6e5e..c2fd132c 100644 --- a/audio_pw.c +++ b/audio_pw.c @@ -269,9 +269,9 @@ static int init(__attribute__((unused)) int argc, __attribute__((unused)) char * // called in a realtime thread. pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | - PW_STREAM_FLAG_RT_PROCESS, + PW_STREAM_FLAG_RT_PROCESS | PW_STREAM_FLAG_INACTIVE, params, 1); - stream_is_active = 1; + stream_is_active = 0; pw_thread_loop_unlock(data.loop); return 0; } From 8901f028b36f25dfffe2f12e5dfa01ec2b0157e6 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:54:16 +0100 Subject: [PATCH 30/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 5a2c2007..c205733c 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 4.3.5-dev-29-g17b134b0 +== +**PipeWire Enhancement** +* Initialise the PipeWire backend so that the PipeWire stream is initially inactive. + Version 4.3.5-dev-27-g8e6bd06c == **PipeWire Enhancement** From 94eda9b9e11cb62eed1035430b0f29fc0b984f88 Mon Sep 17 00:00:00 2001 From: Andrew May Date: Fri, 16 Aug 2024 23:50:41 -0700 Subject: [PATCH 31/61] Check swr_init() error code and exit early. --- rtp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rtp.c b/rtp.c index 93e3fc84..8a7f0e0e 100644 --- a/rtp.c +++ b/rtp.c @@ -2364,7 +2364,11 @@ void *rtp_buffered_audio_processor(void *arg) { }; av_opt_set_sample_fmt(swr, "out_sample_fmt", av_format, 0); - swr_init(swr); + int swr_err = swr_init(swr); + if (swr_err !=0){ + die("FFMpeg swr_init() failed Error %d (%s)", + swr_err, av_err2str(swr_err)); + } uint8_t packet[16 * 1024]; unsigned char m[16 * 1024]; // leave the first 7 bytes blank to make room for the ADTS From feed0cb13fede4c24ce5d73c119e541c941e881c Mon Sep 17 00:00:00 2001 From: Tucker Kern Date: Fri, 13 Sep 2024 19:10:56 +0000 Subject: [PATCH 32/61] Send pvol metadata on mute --- player.c | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/player.c b/player.c index 102085a4..a41ed3ef 100644 --- a/player.c +++ b/player.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -3296,6 +3297,30 @@ void *player_thread_func(void *arg) { pthread_exit(NULL); } +static void player_send_volume_metadata(bool vol_mode_both, double airplay_volume, double scaled_attenuation, int32_t max_db, int32_t min_db, int32_t hw_max_db) +{ +#ifdef CONFIG_METADATA + // here, send the 'pvol' metadata message when the airplay volume information + // is being used by shairport sync to control the output volume + char dv[128]; + memset(dv, 0, 128); + if (config.ignore_volume_control == 0) { + if (vol_mode_both) { + // normalise the maximum output to the hardware device's max output + snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, + (scaled_attenuation - max_db + hw_max_db) / 100.0, + (min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0); + } else { + snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0, + min_db / 100.0, max_db / 100.0); + } + } else { + snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0); + } + send_ssnc_metadata('pvol', dv, strlen(dv), 1); +#endif +} + void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) { debug_mutex_lock(&conn->volume_control_mutex, 5000, 1); // first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw @@ -3384,6 +3409,8 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c volume_mode, airplay_volume); } } + + player_send_volume_metadata(volume_mode == vol_both, airplay_volume, 0, 0, 0, 0); } else { int32_t max_db = 0, min_db = 0; switch (volume_mode) { @@ -3494,26 +3521,7 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0); } -#ifdef CONFIG_METADATA - // here, send the 'pvol' metadata message when the airplay volume information - // is being used by shairport sync to control the output volume - char dv[128]; - memset(dv, 0, 128); - if (config.ignore_volume_control == 0) { - if (volume_mode == vol_both) { - // normalise the maximum output to the hardware device's max output - snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, - (scaled_attenuation - max_db + hw_max_db) / 100.0, - (min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0); - } else { - snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0, - min_db / 100.0, max_db / 100.0); - } - } else { - snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0); - } - send_ssnc_metadata('pvol', dv, strlen(dv), 1); -#endif + player_send_volume_metadata(volume_mode == vol_both, airplay_volume, scaled_attenuation, max_db, min_db, hw_max_db); if (config.output->mute) config.output->mute(0); From 73a9177e014c5f486bb213825722ce958b214359 Mon Sep 17 00:00:00 2001 From: Tucker Kern Date: Fri, 13 Sep 2024 13:41:51 -0600 Subject: [PATCH 33/61] Fix warnings when building without metadata --- player.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/player.c b/player.c index a41ed3ef..4baef1a3 100644 --- a/player.c +++ b/player.c @@ -3318,6 +3318,13 @@ static void player_send_volume_metadata(bool vol_mode_both, double airplay_volum snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0); } send_ssnc_metadata('pvol', dv, strlen(dv), 1); +#else + (void)vol_mode_both; + (void)airplay_volume; + (void)scaled_attenuation; + (void)max_db; + (void)min_db; + (void)hw_max_db; #endif } From 17784945b56ea2ca9498682deb41a3b379ef1bdb Mon Sep 17 00:00:00 2001 From: Tucker Kern Date: Mon, 16 Sep 2024 21:19:56 +0000 Subject: [PATCH 34/61] Avoid use of stdbool --- player.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/player.c b/player.c index 4baef1a3..ccfa09f5 100644 --- a/player.c +++ b/player.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -3297,7 +3296,7 @@ void *player_thread_func(void *arg) { pthread_exit(NULL); } -static void player_send_volume_metadata(bool vol_mode_both, double airplay_volume, double scaled_attenuation, int32_t max_db, int32_t min_db, int32_t hw_max_db) +static void player_send_volume_metadata(uint8_t vol_mode_both, double airplay_volume, double scaled_attenuation, int32_t max_db, int32_t min_db, int32_t hw_max_db) { #ifdef CONFIG_METADATA // here, send the 'pvol' metadata message when the airplay volume information @@ -3305,7 +3304,7 @@ static void player_send_volume_metadata(bool vol_mode_both, double airplay_volum char dv[128]; memset(dv, 0, 128); if (config.ignore_volume_control == 0) { - if (vol_mode_both) { + if (vol_mode_both == 1) { // normalise the maximum output to the hardware device's max output snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, (scaled_attenuation - max_db + hw_max_db) / 100.0, @@ -3417,7 +3416,8 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c } } - player_send_volume_metadata(volume_mode == vol_both, airplay_volume, 0, 0, 0, 0); + uint8_t vol_mode_both = (volume_mode == vol_both) ? 1 : 0; + player_send_volume_metadata(vol_mode_both, airplay_volume, 0, 0, 0, 0); } else { int32_t max_db = 0, min_db = 0; switch (volume_mode) { @@ -3528,7 +3528,8 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0); } - player_send_volume_metadata(volume_mode == vol_both, airplay_volume, scaled_attenuation, max_db, min_db, hw_max_db); + uint8_t vol_mode_both = (volume_mode == vol_both) ? 1 : 0; + player_send_volume_metadata(vol_mode_both, airplay_volume, scaled_attenuation, max_db, min_db, hw_max_db); if (config.output->mute) config.output->mute(0); From 654f59693240420ea96dba1354a06ce44d1293d7 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:20:01 +1000 Subject: [PATCH 35/61] Update BUILD.md Include libsoxr in the FreeBSD package list. --- BUILD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 30619161..ea534c6e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -107,12 +107,12 @@ Reboot for these changes to take effect. Next, install the packages that are needed for Shairport Sync and NQPTP: ``` -# pkg install git autotools pkgconf popt libconfig openssl alsa-utils \ +# pkg install git autotools pkgconf popt libconfig openssl alsa-utils libsoxr \ libplist libsodium ffmpeg e2fsprogs-libuuid vim ``` If you are building classic Shairport Sync, the list of packages is shorter: ``` -# pkg install git autotools pkgconf popt libconfig openssl alsa-utils +# pkg install git autotools pkgconf popt libconfig openssl alsa-utils libsoxr ``` ## 3. Build ### NQPTP From 6de30126a9013e69e3cc759a5c4e9ed531a69217 Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sat, 16 Mar 2024 17:40:59 +0100 Subject: [PATCH 36/61] MQTT: Add is_active and is _playing --- mqtt.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mqtt.c b/mqtt.c index f662009d..fd172161 100644 --- a/mqtt.c +++ b/mqtt.c @@ -21,6 +21,10 @@ struct mosquitto *global_mosq = NULL; char *topic = NULL; int connected = 0; +// track active/playing state +int is_active = 0; +int is_playing = 0; + // mosquitto logging void _cb_log(__attribute__((unused)) struct mosquitto *mosq, __attribute__((unused)) void *userdata, int level, const char *str) { @@ -167,12 +171,16 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le } else if (type == 'ssnc') { switch (code) { case 'abeg': + is_active = 1; + mqtt_publish("is_active", "1", 1); mqtt_publish("active_start", data, length); break; case 'acre': mqtt_publish("active_remote_id", data, length); break; case 'aend': + is_active = 0; + mqtt_publish("is_active", "0", 1); mqtt_publish("active_end", data, length); break; case 'asal': @@ -210,9 +218,13 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le mqtt_publish("output_frame_rate", data, length); break; case 'pbeg': + is_playing = 1; + mqtt_publish("is_playing", "1", 1); mqtt_publish("play_start", data, length); break; case 'pend': + is_playing = 0; + mqtt_publish("is_playing", "0", 1); mqtt_publish("play_end", data, length); break; case 'pfls': @@ -224,6 +236,8 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le } break; case 'prsm': + is_playing = 1; + mqtt_publish("is_playing", "1", 1); mqtt_publish("play_resume", data, length); break; case 'pvol': From cda315b6aac424656cf7155a0adb2ffc5b4d4060 Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sun, 17 Mar 2024 13:12:55 +0100 Subject: [PATCH 37/61] Add support for MQTT autodiscovery --- common.h | 2 ++ mqtt.c | 46 +++++++++++++++++++++++++++++++++++++ scripts/shairport-sync.conf | 2 ++ shairport.c | 5 ++++ 4 files changed, 55 insertions(+) diff --git a/common.h b/common.h index 9de26f63..6a341f7b 100644 --- a/common.h +++ b/common.h @@ -175,6 +175,8 @@ typedef struct { int mqtt_publish_parsed; int mqtt_publish_cover; int mqtt_enable_remote; + int mqtt_enable_autodiscovery; + char *mqtt_autodiscovery_prefix; char *mqtt_empty_payload_substitute; #endif uint8_t ap1_prefix[6]; diff --git a/mqtt.c b/mqtt.c index fd172161..2b9bd5b2 100644 --- a/mqtt.c +++ b/mqtt.c @@ -94,6 +94,52 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, snprintf(remotetopic, strlen(config.mqtt_topic) + 8, "%s/remote", config.mqtt_topic); mosquitto_subscribe(mosq, NULL, remotetopic, 0); } + + // send autodiscovery messages if enabled + if (config.mqtt_enable_autodiscovery && config.mqtt_publish_parsed) { + send_autodiscovery_messages(mosq); + } +} + +void send_autodiscovery_messages(struct mosquitto *mosq) { + const char *autodiscovery_prefix = (config.mqtt_autodiscovery_prefix != NULL) ? + config.autodiscovery_prefix : "homeassistant"; + char base_topic[256]; + snprintf(base_topic, sizeof(base_topic), "%s/sensor/shairport-sync-", autodiscovery_prefix); + + char full_topic[256]; + char payload[1024]; + const char *device_name = config.service_name; + + const char *sensors[] = { + "artist", "album", "title", "genre", "format", "track_id", + "client_ip", "client_name", "volume", "is_active", "is_playing", NULL + }; + // Human-readable names + const char *sensor_names[] = { + "Artist", "Album", "Title", "Genre", "Format", "Track ID", + "Client IP", "Client Name", "Volume", "Is Active", "Is Playing", NULL + }; + + for (int i = 0; sensors[i] != NULL; i++) { + snprintf(full_topic, sizeof(full_topic), "%s%s/config", base_topic, sensors[i]); + snprintf(payload, sizeof(payload), + "{\n" + " \"name\": \"%s\",\n" + " \"state_topic\": \"%s/%s\",\n" + " \"icon\": \"mdi:music-note\",\n" + " \"unique_id\": \"shairport-sync-%s\",\n" + " \"device\": {\n" + " \"identifiers\": [\"shairport-sync\"],\n" + " \"name\": \"%s\",\n" + " \"model\": \"Shairport-Sync\",\n" + " \"manufacturer\": \"shairport-sync\"\n" + " }\n" + "}", sensor_names[i], config.mqtt_topic, sensors[i], sensors[i], device_name); + + mosquitto_publish(mosq, NULL, full_topic, strlen(payload), payload, 0, true); + debug(2, "[MQTT Autodiscovery]: Published autodiscovery for %s", sensor_names[i]); + } } // helper function to publish under a topic and automatically append the main topic diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 517a7560..8423b807 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -285,6 +285,8 @@ mqtt = // Currently published topics:artist,album,title,genre,format,songalbum,volume,client_ip, // Additionally, messages at the topics play_start,play_end,play_flush,play_resume are published // publish_cover = "no"; //whether to publish the cover over mqtt in binary form. This may lead to a bit of load on the broker +// enable_autodiscovery = "no"; //whether to publish an autodiscovery message to automatically appear in Home Assistant +// autodiscovery_prefix = "homeassistant"; //string to prepend to autodiscovery topic // enable_remote = "no"; //whether to remote control via MQTT. RC is available under `topic`/remote. // Available commands are "command", "beginff", "beginrew", "mutetoggle", "nextitem", "previtem", "pause", "playpause", "play", "stop", "playresume", "shuffle_songs", "volumedown", "volumeup" }; diff --git a/shairport.c b/shairport.c index 1060e0b7..87ac0ea7 100644 --- a/shairport.c +++ b/shairport.c @@ -1300,6 +1300,10 @@ int parse_options(int argc, char **argv) { if (config.mqtt_publish_cover && !config.get_coverart) { die("You need to have metadata.include_cover_art enabled in order to use mqtt.publish_cover"); } + config_set_lookup_bool(config.cfg, "mqtt.enable_autodiscovery", &config.mqtt_enable_autodiscovery); + if (config_lookup_string(config.cfg, "mqtt.autodiscovery_prefix", &str)) { + config.mqtt_autodiscovery_prefix = (char *)str; + } config_set_lookup_bool(config.cfg, "mqtt.enable_remote", &config.mqtt_enable_remote); if (config_lookup_string(config.cfg, "mqtt.empty_payload_substitute", &str)) { if (strlen(str) == 0) @@ -2550,6 +2554,7 @@ int main(int argc, char **argv) { debug(1, "mqtt will%s publish parsed metadata.", config.mqtt_publish_parsed ? "" : " not"); debug(1, "mqtt will%s publish cover Art.", config.mqtt_publish_cover ? "" : " not"); debug(1, "mqtt remote control is %sabled.", config.mqtt_enable_remote ? "en" : "dis"); + debug(1, "mqtt autodiscovery is %sabled.", config.mqtt_enable_autodiscovery ? "en" : "dis"); #endif #ifdef CONFIG_CONVOLUTION From b4f160e3201295bd9e640bd3ca9258c4ac302c8e Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sun, 17 Mar 2024 19:26:18 +0100 Subject: [PATCH 38/61] MQTT autodiscovery (WIP) --- mqtt.c | 105 ++++++++++++++++++++++++++++++++++++++++++--------------- mqtt.h | 1 + 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/mqtt.c b/mqtt.c index 2b9bd5b2..51e474d8 100644 --- a/mqtt.c +++ b/mqtt.c @@ -102,43 +102,92 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, } void send_autodiscovery_messages(struct mosquitto *mosq) { - const char *autodiscovery_prefix = (config.mqtt_autodiscovery_prefix != NULL) ? - config.autodiscovery_prefix : "homeassistant"; - char base_topic[256]; - snprintf(base_topic, sizeof(base_topic), "%s/sensor/shairport-sync-", autodiscovery_prefix); - - char full_topic[256]; - char payload[1024]; const char *device_name = config.service_name; + const char *sw_version = get_version_string(); + const char *model = "shairport-sync"; + const char *manufacturer = "Mike Brady"; + const char *autodiscovery_prefix = (config.mqtt_autodiscovery_prefix != NULL) ? + config.mqtt_autodiscovery_prefix : "homeassistant"; + char topic[512]; + char payload[1024]; + char device_payload[512]; + + snprintf(device_payload, sizeof(device_payload), + "\"device\": {" + "\"identifiers\": [\"%s\"]," + "\"name\": \"%s\"," + "\"model\": \"%s\"," + "\"sw_version\": \"%s\"," + "\"manufacturer\": \"%s\"" + "}", + model, device_name, model, sw_version, manufacturer); + + // when adding sensors here, be sure to also update sensor_names and icons below! const char *sensors[] = { - "artist", "album", "title", "genre", "format", "track_id", - "client_ip", "client_name", "volume", "is_active", "is_playing", NULL + "artist", + "album", + "title", + "genre", + "format", + "track_id", + "client_ip", + "client_name", + "volume", + "is_active", + "is_playing", + NULL }; - // Human-readable names + const char *sensor_names[] = { - "Artist", "Album", "Title", "Genre", "Format", "Track ID", - "Client IP", "Client Name", "Volume", "Is Active", "Is Playing", NULL + "Artist", + "Album", + "Title", + "Genre", + "Format", + "Track ID", + "Client IP", + "Client Name", + "Volume", + "Is Active", + "Is Playing" + }; + + const char *icons[] = { + "mdi:account-music", // artist + "mdi:album", // album + "mdi:music", // title + "mdi:music-box-multiple", // genre + "mdi:file", // format + "mdi:identifier", // track ID + "mdi:ip", // client IP + "mdi:cellphone-wireless", // client name + "mdi:volume-high", // volume + "mdi:play-box-multiple", // is active + "mdi:play-box-multiple-outline" // is playing }; for (int i = 0; sensors[i] != NULL; i++) { - snprintf(full_topic, sizeof(full_topic), "%s%s/config", base_topic, sensors[i]); - snprintf(payload, sizeof(payload), - "{\n" - " \"name\": \"%s\",\n" - " \"state_topic\": \"%s/%s\",\n" - " \"icon\": \"mdi:music-note\",\n" - " \"unique_id\": \"shairport-sync-%s\",\n" - " \"device\": {\n" - " \"identifiers\": [\"shairport-sync\"],\n" - " \"name\": \"%s\",\n" - " \"model\": \"Shairport-Sync\",\n" - " \"manufacturer\": \"shairport-sync\"\n" - " }\n" - "}", sensor_names[i], config.mqtt_topic, sensors[i], sensors[i], device_name); + bool is_binary_sensor = (strcmp(sensors[i], "is_active") == 0 || strcmp(sensors[i], "is_playing") == 0); - mosquitto_publish(mosq, NULL, full_topic, strlen(payload), payload, 0, true); - debug(2, "[MQTT Autodiscovery]: Published autodiscovery for %s", sensor_names[i]); + snprintf(topic, sizeof(topic), "%s/%ssensor/%s/%s_%s%s/config", + autodiscovery_prefix, is_binary_sensor ? "binary_" : "", + model, model, device_name, sensors[i]); + + snprintf(payload, sizeof(payload), + "{" + "\"name\": \"%s\"," + "\"state_topic\": \"%s/%s\"," + "\"icon\": \"%s\"," + "\"unique_id\": \"%s_%s\"," + "%s%s" + "}", + sensor_names[i], config.mqtt_topic, sensors[i], icons[i], device_name, sensors[i], + is_binary_sensor ? "\"payload_on\": \"1\",\"payload_off\": \"0\"," : "", + device_payload); + + mosquitto_publish(mosq, NULL, topic, strlen(payload), payload, 0, true); + debug(2, "[MQTT]: Published autodiscovery for %s", device_name) } } diff --git a/mqtt.h b/mqtt.h index 4bf680ed..69ad75bd 100644 --- a/mqtt.h +++ b/mqtt.h @@ -7,6 +7,7 @@ int initialise_mqtt(); void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t length); void mqtt_publish(char *topic, char *data, uint32_t length); void mqtt_setup(); +void send_autodiscovery_messages(struct mosquitto *mosq); void on_connect(struct mosquitto *mosq, void *userdata, int rc); void on_disconnect(struct mosquitto *mosq, void *userdata, int rc); void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg); From e252ee7fb5ac54dc52a21e87441e78b3eedd7fed Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sun, 17 Mar 2024 21:22:47 +0000 Subject: [PATCH 39/61] MQTT auto discovery working --- mqtt.c | 79 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/mqtt.c b/mqtt.c index 51e474d8..c85dfeb9 100644 --- a/mqtt.c +++ b/mqtt.c @@ -21,10 +21,6 @@ struct mosquitto *global_mosq = NULL; char *topic = NULL; int connected = 0; -// track active/playing state -int is_active = 0; -int is_playing = 0; - // mosquitto logging void _cb_log(__attribute__((unused)) struct mosquitto *mosq, __attribute__((unused)) void *userdata, int level, const char *str) { @@ -101,17 +97,20 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, } } +// function to send autodiscovery messages for Home Assistant void send_autodiscovery_messages(struct mosquitto *mosq) { const char *device_name = config.service_name; const char *sw_version = get_version_string(); const char *model = "shairport-sync"; + const char *model_friendly = "Shairport Sync"; const char *manufacturer = "Mike Brady"; const char *autodiscovery_prefix = (config.mqtt_autodiscovery_prefix != NULL) ? config.mqtt_autodiscovery_prefix : "homeassistant"; char topic[512]; - char payload[1024]; + char payload[1280]; char device_payload[512]; + char id_string[128]; snprintf(device_payload, sizeof(device_payload), "\"device\": {" @@ -121,7 +120,7 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { "\"sw_version\": \"%s\"," "\"manufacturer\": \"%s\"" "}", - model, device_name, model, sw_version, manufacturer); + model, device_name, model_friendly, sw_version, manufacturer); // when adding sensors here, be sure to also update sensor_names and icons below! const char *sensors[] = { @@ -130,12 +129,18 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { "title", "genre", "format", + "output_format", + "output_frame_rate", "track_id", "client_ip", + "client_mac_address", "client_name", + "client_model", + "client_device_id", + "server_ip", "volume", - "is_active", - "is_playing", + "active", + "playing", NULL }; @@ -145,12 +150,18 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { "Title", "Genre", "Format", + "Output Format", + "Output Frame Rate", "Track ID", "Client IP", + "Client MAC Address", "Client Name", + "Client Model", + "Client Device ID", + "Server IP", "Volume", - "Is Active", - "Is Playing" + "Active Session", + "Playing" }; const char *icons[] = { @@ -159,35 +170,48 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { "mdi:music", // title "mdi:music-box-multiple", // genre "mdi:file", // format + "mdi:file", // output format + "mdi:file-chart", // output frame rate "mdi:identifier", // track ID "mdi:ip", // client IP - "mdi:cellphone-wireless", // client name + "mdi:hexadecimal", // client MAC address + "mdi:cellphone-text", // client name + "mdi:cellphone-text", // client model + "mdi:hexadecimal", // client device ID + "mdi:ip-network", // server IP "mdi:volume-high", // volume - "mdi:play-box-multiple", // is active - "mdi:play-box-multiple-outline" // is playing + "mdi:play-box-multiple", // active + "mdi:play-box-multiple-outline" // playing }; for (int i = 0; sensors[i] != NULL; i++) { - bool is_binary_sensor = (strcmp(sensors[i], "is_active") == 0 || strcmp(sensors[i], "is_playing") == 0); + bool is_binary_sensor = (strcmp(sensors[i], "active") == 0 || strcmp(sensors[i], "playing") == 0); + bool is_volume_sensor = strcmp(sensors[i], "volume") == 0; - snprintf(topic, sizeof(topic), "%s/%ssensor/%s/%s_%s%s/config", + snprintf(topic, sizeof(topic), "%s/%ssensor/%s/%s_%s/config", autodiscovery_prefix, is_binary_sensor ? "binary_" : "", - model, model, device_name, sensors[i]); + model, device_name, sensors[i]); + + snprintf(id_string, sizeof(id_string), "%s_%s_%s", model, device_name, sensors[i]); snprintf(payload, sizeof(payload), "{" "\"name\": \"%s\"," "\"state_topic\": \"%s/%s\"," "\"icon\": \"%s\"," - "\"unique_id\": \"%s_%s\"," - "%s%s" + "\"unique_id\": \"%s\"," + "\"object_id\": \"%s\"," + "%s%s%s" "}", - sensor_names[i], config.mqtt_topic, sensors[i], icons[i], device_name, sensors[i], + sensor_names[i], config.mqtt_topic, sensors[i], icons[i], id_string, id_string, is_binary_sensor ? "\"payload_on\": \"1\",\"payload_off\": \"0\"," : "", + is_volume_sensor ? "\"value_template\": \"{{ ((value | regex_findall_index(" + "find='^(.+?),', index=0, ignorecase=False) | float / 30 + 1) * 100) | round(0) }}\"," + "\"unit_of_measurement\": \"%\"," : "", device_payload); mosquitto_publish(mosq, NULL, topic, strlen(payload), payload, 0, true); - debug(2, "[MQTT]: Published autodiscovery for %s", device_name) + debug(2, "[MQTT]: published autodiscovery for %s", id_string); } } @@ -266,16 +290,14 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le } else if (type == 'ssnc') { switch (code) { case 'abeg': - is_active = 1; - mqtt_publish("is_active", "1", 1); + mqtt_publish("active", "1", 1); mqtt_publish("active_start", data, length); break; case 'acre': mqtt_publish("active_remote_id", data, length); break; case 'aend': - is_active = 0; - mqtt_publish("is_active", "0", 1); + mqtt_publish("active", "0", 1); mqtt_publish("active_end", data, length); break; case 'asal': @@ -313,13 +335,11 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le mqtt_publish("output_frame_rate", data, length); break; case 'pbeg': - is_playing = 1; - mqtt_publish("is_playing", "1", 1); + mqtt_publish("playing", "1", 1); mqtt_publish("play_start", data, length); break; case 'pend': - is_playing = 0; - mqtt_publish("is_playing", "0", 1); + mqtt_publish("playing", "0", 1); mqtt_publish("play_end", data, length); break; case 'pfls': @@ -331,8 +351,7 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le } break; case 'prsm': - is_playing = 1; - mqtt_publish("is_playing", "1", 1); + mqtt_publish("playing", "1", 1); mqtt_publish("play_resume", data, length); break; case 'pvol': From af6d90282e5c223a8682b2088fc329957b08146c Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sun, 17 Mar 2024 22:02:58 +0000 Subject: [PATCH 40/61] Set unique device identifier --- mqtt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mqtt.c b/mqtt.c index c85dfeb9..cc85e039 100644 --- a/mqtt.c +++ b/mqtt.c @@ -100,6 +100,7 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, // function to send autodiscovery messages for Home Assistant void send_autodiscovery_messages(struct mosquitto *mosq) { const char *device_name = config.service_name; + const char *device_id = config.airplay_device_id ? config.airplay_device_id : config.service_name; const char *sw_version = get_version_string(); const char *model = "shairport-sync"; const char *model_friendly = "Shairport Sync"; @@ -120,7 +121,7 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { "\"sw_version\": \"%s\"," "\"manufacturer\": \"%s\"" "}", - model, device_name, model_friendly, sw_version, manufacturer); + device_id, device_name, model_friendly, sw_version, manufacturer); // when adding sensors here, be sure to also update sensor_names and icons below! const char *sensors[] = { From 6258021d5585fb7d1a370494ec5ba94b5b348967 Mon Sep 17 00:00:00 2001 From: aaronk6 Date: Sun, 17 Mar 2024 22:33:54 +0000 Subject: [PATCH 41/61] Fixed multiple devices getting merged in Home Assistant --- mqtt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mqtt.c b/mqtt.c index cc85e039..addf04f6 100644 --- a/mqtt.c +++ b/mqtt.c @@ -101,6 +101,7 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, void send_autodiscovery_messages(struct mosquitto *mosq) { const char *device_name = config.service_name; const char *device_id = config.airplay_device_id ? config.airplay_device_id : config.service_name; + const char *device_id_no_colons = str_replace(device_id, ":", ""); const char *sw_version = get_version_string(); const char *model = "shairport-sync"; const char *model_friendly = "Shairport Sync"; @@ -189,9 +190,9 @@ void send_autodiscovery_messages(struct mosquitto *mosq) { bool is_binary_sensor = (strcmp(sensors[i], "active") == 0 || strcmp(sensors[i], "playing") == 0); bool is_volume_sensor = strcmp(sensors[i], "volume") == 0; - snprintf(topic, sizeof(topic), "%s/%ssensor/%s/%s_%s/config", + snprintf(topic, sizeof(topic), "%s/%ssensor/%s_%s/%s/config", autodiscovery_prefix, is_binary_sensor ? "binary_" : "", - model, device_name, sensors[i]); + model, device_id_no_colons, sensors[i]); snprintf(id_string, sizeof(id_string), "%s_%s_%s", model, device_name, sensors[i]); From 3c226578c79bec166c00d554eee75c955bafd4cb Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:52:21 +0100 Subject: [PATCH 42/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index c205733c..85a57d35 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Version 4.3.5-dev-44-ge4c2ce4a +== +**Enhancements** +* Add Home Assistant MQTT Auto-Discovery and active/playing State Messages, thanks to [aaronk6](https://github.com/aaronk6). +* Send `pvol` metadata on mute, thanks to [Tucker Kern](https://github.com/mill1000). +**Bug Fix** +* Check `swr_init()` and exit if error code found, thanks to [Andrew May](https://github.com/acmay). + Version 4.3.5-dev-29-g17b134b0 == **PipeWire Enhancement** From c6f5aa9641aa8592b7c83daf4b33aa12ea77fc35 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:52:38 +0100 Subject: [PATCH 43/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 85a57d35..e8d755d3 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -3,6 +3,7 @@ Version 4.3.5-dev-44-ge4c2ce4a **Enhancements** * Add Home Assistant MQTT Auto-Discovery and active/playing State Messages, thanks to [aaronk6](https://github.com/aaronk6). * Send `pvol` metadata on mute, thanks to [Tucker Kern](https://github.com/mill1000). + **Bug Fix** * Check `swr_init()` and exit if error code found, thanks to [Andrew May](https://github.com/acmay). From df8663655e1d156cef98b16eb4f05a803a07dcce Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:41:30 +0100 Subject: [PATCH 44/61] Fix compilation bug in classic mode -- don't look for config.airplay_device_id. --- mqtt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mqtt.c b/mqtt.c index addf04f6..105928fd 100644 --- a/mqtt.c +++ b/mqtt.c @@ -100,7 +100,11 @@ void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata, // function to send autodiscovery messages for Home Assistant void send_autodiscovery_messages(struct mosquitto *mosq) { const char *device_name = config.service_name; +#ifdef CONFIG_AIRPLAY_2 const char *device_id = config.airplay_device_id ? config.airplay_device_id : config.service_name; +#else + const char *device_id = config.service_name; +#endif const char *device_id_no_colons = str_replace(device_id, ":", ""); const char *sw_version = get_version_string(); const char *model = "shairport-sync"; From fe743fdf70c17a07c0b5b5c765a664f762d20827 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:42:28 +0100 Subject: [PATCH 45/61] Remove Avahi static service file definitions for SSH and SFTP --- docker/Dockerfile | 5 ++++- docker/classic/Dockerfile | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c44ff267..84ba6dcf 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -121,8 +121,11 @@ RUN addgroup -g 29 docker_audio && addgroup shairport-sync docker_audio && addgr # Remove anything we don't need. RUN rm -rf /lib/apk/db/* +# Remove any statically-defined Avahi services, e.g. SSH and SFTP +RUN rm -rf /etc/avahi/services/*.service + # Add run script that will start SPS COPY ./docker/run.sh ./run.sh RUN chmod +x /run.sh -Entrypoint ["/init","./run.sh"] +ENTRYPOINT ["/init","./run.sh"] diff --git a/docker/classic/Dockerfile b/docker/classic/Dockerfile index d41cac4f..283442bd 100644 --- a/docker/classic/Dockerfile +++ b/docker/classic/Dockerfile @@ -97,8 +97,11 @@ RUN addgroup -g 29 docker_audio && addgroup shairport-sync docker_audio && addgr # Remove anything we don't need. RUN rm -rf /lib/apk/db/* +# Remove any statically-defined Avahi services, e.g. SSH and SFTP +RUN rm -rf /etc/avahi/services/*.service + # Add run script that will start SPS COPY ./docker/run.sh ./run.sh RUN chmod +x /run.sh -Entrypoint ["/init","./run.sh"] +ENTRYPOINT ["/init","./run.sh"] From bb37a080b705efc2d738be583b45fa1267443bfd Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:00:24 +0100 Subject: [PATCH 46/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index e8d755d3..ea844252 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Version 4.3.5-dev-48-gfe743fdf +== +**Docker Bug Fixes** +* Stop advertising the SFTP and SSH services on Bonjour. Thanks to [https://github.com/jpbaril](https://github.com/jpbaril) for bringing this to notice. + +**Bug Fix** +* Dues to recent changes in the MQTT client, a compilation error occured when building for Classic Airplay only. Fixed. + Version 4.3.5-dev-44-ge4c2ce4a == **Enhancements** From 616b6e71fd2fca8ac5968d3ee18fbcfadeadbd8c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:00:41 +0100 Subject: [PATCH 47/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index ea844252..84ca42b0 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -4,7 +4,7 @@ Version 4.3.5-dev-48-gfe743fdf * Stop advertising the SFTP and SSH services on Bonjour. Thanks to [https://github.com/jpbaril](https://github.com/jpbaril) for bringing this to notice. **Bug Fix** -* Dues to recent changes in the MQTT client, a compilation error occured when building for Classic Airplay only. Fixed. +* Due to recent changes in the MQTT client, a compilation error occured when building for Classic Airplay only. Fixed. Version 4.3.5-dev-44-ge4c2ce4a == From b221e1322d42de8add086bc4ce53bd65c0831985 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:03:23 +0000 Subject: [PATCH 48/61] in audio_jack.c, return the delay in terms of frames at Shairport Sync's output rate, not the Jack Audio server's internal rate. --- audio_jack.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/audio_jack.c b/audio_jack.c index 911fada7..0a00900e 100644 --- a/audio_jack.c +++ b/audio_jack.c @@ -1,6 +1,6 @@ /* * jack output driver. This file is part of Shairport Sync. - * Copyright (c) 2019 -- 2022 Mike Brady <4265913+mikebrady@users.noreply.github.com>, + * Copyright (c) 2019 -- 2024 Mike Brady <4265913+mikebrady@users.noreply.github.com>, * Jörn Nettingsmeier * * All rights reserved. @@ -50,8 +50,11 @@ pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER; jack_port_t *port[NPORTS]; const char *port_name[NPORTS] = {"out_L", "out_R"}; + +int sps_sample_rate; + jack_client_t *client; -jack_nframes_t sample_rate; +jack_nframes_t jack_sample_rate; jack_nframes_t jack_latency; jack_ringbuffer_t *jackbuf; @@ -235,17 +238,17 @@ static int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) c if (!client) { die("Could not start JACK server. JackStatus is %x", status); } - sample_rate = jack_get_sample_rate(client); + jack_sample_rate = jack_get_sample_rate(client); #ifdef CONFIG_SOXR if (config.jack_soxr_resample_quality >= SOXR_QQ) { quality_spec = soxr_quality_spec(config.jack_soxr_resample_quality, 0); io_spec = soxr_io_spec(SOXR_INT16_I, SOXR_FLOAT32_I); } else #endif - if (sample_rate != 44100) { + if (jack_sample_rate != 44100) { die("The JACK server is running at the wrong sample rate (%d) for Shairport Sync." " Must be 44100 Hz.", - sample_rate); + jack_sample_rate); } jack_set_process_callback(client, &process, NULL); jack_set_graph_order_callback(client, &graph, NULL); @@ -329,6 +332,7 @@ static void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_f // Nothing to do, JACK client has already been set up at jack_init(). // Also, we have no say over the sample rate or sample format of JACK, // We convert the 16bit samples to float, and die if the sample rate is != 44k1 without soxr. + sps_sample_rate = i_sample_rate; #ifdef CONFIG_SOXR if (config.jack_soxr_resample_quality >= SOXR_QQ) { // we might improve a bit with soxr_clear if the sample_rate doesn't change @@ -336,7 +340,7 @@ static void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_f soxr_delete(soxr); } soxr_error_t e = NULL; - soxr = soxr_create(i_sample_rate, sample_rate, NPORTS, &e, &io_spec, &quality_spec, NULL); + soxr = soxr_create(sps_sample_rate, jack_sample_rate, NPORTS, &e, &io_spec, &quality_spec, NULL); if (!soxr) { die("Unable to create soxr resampler for JACK: %s", e); } @@ -366,13 +370,15 @@ static int jack_delay(long *the_delay) { debug(2, "audio_occupancy_now is %d.", audio_occupancy_now); pthread_mutex_unlock(&buffer_mutex); - int64_t frames_processed_since_latest_latency_check = (delta * sample_rate) / 1000000000; + int64_t frames_processed_since_latest_latency_check = (delta * jack_sample_rate) / 1000000000; // debug(1,"delta: %" PRId64 " frames.",frames_processed_since_latest_latency_check); // jack_latency is set by the graph() callback, it's the average of the maximum // latencies of all our output ports. Adjust this constant baseline delay according // to the buffer fill level: - *the_delay = jack_latency + audio_occupancy_now - frames_processed_since_latest_latency_check; - // debug(1,"reporting a delay of %d frames",*the_delay); + int64_t the_delay_in_jack_frames = jack_latency + audio_occupancy_now - frames_processed_since_latest_latency_check; + int64_t the_delay_in_sps_frames = (the_delay_in_jack_frames * sps_sample_rate) / jack_sample_rate; + *the_delay = the_delay_in_sps_frames; + // debug(2, "reporting a delay of %ld frames at Shairport Sync's rate of %d FPS.",*the_delay, sps_sample_rate); return 0; } From 1488bf0a34da7199cc7b7e9b527821c180cd58be Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:17:29 +0000 Subject: [PATCH 49/61] Remove a misleading comment. --- audio_jack.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/audio_jack.c b/audio_jack.c index 0a00900e..c1e92104 100644 --- a/audio_jack.c +++ b/audio_jack.c @@ -88,12 +88,7 @@ soxr_io_spec_t io_spec; #endif static inline sample_t sample_conv(short sample) { - // It sounds correct, but I don't understand it. - // Zero int needs to be zero float. Check. - // Plus 32767 int is 1.0. Check. - // Minus 32767 int is -0.99997. And here my brain shuts down. - // In my head, it should be 1.0, and we should tolerate an overflow - // at minus 32768. But I'm sure there's a textbook explanation somewhere. + // signed 16-bit int to float return ((sample < 0) ? (-1.0 * sample / SHRT_MIN) : (1.0 * sample / SHRT_MAX)); } From aa3af3b368ffef08db35f4b7ffc15a89a74c35e8 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:28:12 +0000 Subject: [PATCH 50/61] Add a check for when a read() returns zero. --- rtsp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rtsp.c b/rtsp.c index 1712e480..e7618995 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1179,9 +1179,13 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time read_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, buf, count); } else { result = read(conn->fd, buf, count); + if (result == 0) + debug(1, "AP2 read result 0, for a request count of %u.", count); } #else result = read(conn->fd, buf, count); + if (result == 0) + debug(1, "AP1 read result 0, for a request count of %u.", count); #endif if (wait_time != 0) remaining_time = time_to_wait_to - get_absolute_time_in_ns(); From 50d5199318ca8f5cdc16e549164f388ca7716b2d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:27:18 +0000 Subject: [PATCH 51/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 84ca42b0..7703a899 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 4.3.5-dev-53-gaa3af3b3 +== +**Bug Fix** +* Fix a problem in the Jack Audio backend `audio_jack.c`. The problem was that if the system's jack server was running at anything other than 44,100 frames per second, the delay information being returned by the backend was incorrect. Thanks to [lucianoiam](https://github.com/lucianoiam) for [reporting the problem](https://github.com/mikebrady/shairport-sync/issues/1926) and for checking the proposed solution. + Version 4.3.5-dev-48-gfe743fdf == **Docker Bug Fixes** From ab6225c1ac1c57f5af50890d722437ec8a921d0d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:44:12 +0000 Subject: [PATCH 52/61] Add some extra diagnostics to investigate a read of zero bytes. --- rtsp.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/rtsp.c b/rtsp.c index e7618995..7ed0378d 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1179,19 +1179,29 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time read_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, buf, count); } else { result = read(conn->fd, buf, count); - if (result == 0) - debug(1, "AP2 read result 0, for a request count of %u.", count); + if (result == 0) { + debug(1, "AP2 read result 0, for a request count of %u.", count); + } } #else result = read(conn->fd, buf, count); - if (result == 0) + if (result == 0) { debug(1, "AP1 read result 0, for a request count of %u.", count); + + } #endif + if ((result == 0) && (errno != 0)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "Connection %d: read result 0, error %d: \"%s\".", + conn->connection_number, errno, (char *)errorstring); + } + if (wait_time != 0) remaining_time = time_to_wait_to - get_absolute_time_in_ns(); - if (((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && (remaining_time > 0)) + if ((((result == -1) || (result == 0)) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && (remaining_time > 0)) debug(1, "remaining time on a timed read is %" PRId64 " ns.", remaining_time); - } while (((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && + } while ((((result == -1) || (result == 0)) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && (remaining_time > 0)); } else { From 758648a5a4f9203897a729138ce9803a0856fc48 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:44:30 +0000 Subject: [PATCH 53/61] Deprecate the soundio backend. Nobody seems to use it, and as it stands it can't be much use. It would be nice to be wrong about this. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 14993857..ea371fc6 100644 --- a/configure.ac +++ b/configure.ac @@ -300,8 +300,8 @@ AM_CONDITIONAL([USE_AO], [test "x$with_ao" = "xyes"]) # Look for Soundio flag AC_ARG_WITH(soundio, [AS_HELP_STRING([--with-soundio],[choose soundio API support.])]) if test "x$with_soundio" = "xyes" ; then - AC_DEFINE([CONFIG_SOUNDIO], 1, [Include SoundIO Support.]) - AC_CHECK_LIB([soundio], [soundio_create], , AC_MSG_ERROR(soundio support requires the soundio library -- libsoundio-dev suggested)) + AC_DEFINE([CONFIG_SOUNDIO], 1, [Include the SoundIO (libsoundio) backend.]) + AC_CHECK_LIB([soundio], [soundio_create], AC_MSG_WARN([The soundio (libsoundio) backend is deprecated and will be removed in a future update.]), AC_MSG_ERROR(soundio support requires the libsoundio library -- libsoundio-dev suggested (note: the soundio backend is deprecated and will be removed in a future update))) fi AM_CONDITIONAL([USE_SOUNDIO], [test "x$with_soundio" = "xyes"]) From 8ac45a5a25d67ae3e45a815cef52a8c844089c40 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:52:07 +0000 Subject: [PATCH 54/61] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 7703a899..62ca4478 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,10 @@ +Version 4.3.5-dev-56-g758648a5 +== +**Deprecation Note** +* The `soundio` backend is being deprecated and will be removed in a future update. It seems that nobody is using it, and as it stands it is very inflexible. + + _"If this be error and upon me proved,"_ -- as [Shakespeare wrote](https://www.poetryfoundation.org/poems/45106/sonnet-116-let-me-not-to-the-marriage-of-true-minds) -- please let us know... + Version 4.3.5-dev-53-gaa3af3b3 == **Bug Fix** From d8442c1afcd5f2871f024423495ddf205555f2d5 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:21:54 +0000 Subject: [PATCH 55/61] Fix error that prevented the libsoundio library from being used at all, duh. --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ea371fc6..ffe83394 100644 --- a/configure.ac +++ b/configure.ac @@ -301,7 +301,8 @@ AM_CONDITIONAL([USE_AO], [test "x$with_ao" = "xyes"]) AC_ARG_WITH(soundio, [AS_HELP_STRING([--with-soundio],[choose soundio API support.])]) if test "x$with_soundio" = "xyes" ; then AC_DEFINE([CONFIG_SOUNDIO], 1, [Include the SoundIO (libsoundio) backend.]) - AC_CHECK_LIB([soundio], [soundio_create], AC_MSG_WARN([The soundio (libsoundio) backend is deprecated and will be removed in a future update.]), AC_MSG_ERROR(soundio support requires the libsoundio library -- libsoundio-dev suggested (note: the soundio backend is deprecated and will be removed in a future update))) + AC_CHECK_LIB([soundio], [soundio_create], , AC_MSG_ERROR(soundio support requires the libsoundio library -- libsoundio-dev suggested (note: the soundio backend is deprecated and will be removed in a future update))) + AC_MSG_WARN([The soundio (libsoundio) backend is deprecated and will be removed in a future update.]) fi AM_CONDITIONAL([USE_SOUNDIO], [test "x$with_soundio" = "xyes"]) From da1c20c6724ad867162d17a87ca9d731aa62aae0 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:59:29 +0000 Subject: [PATCH 56/61] Quieten some debug messages. --- rtsp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtsp.c b/rtsp.c index 7ed0378d..3c2e0b0e 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1180,20 +1180,20 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time } else { result = read(conn->fd, buf, count); if (result == 0) { - debug(1, "AP2 read result 0, for a request count of %u.", count); + debug(3, "AP2 read result 0, for a request count of %u.", count); } } #else result = read(conn->fd, buf, count); if (result == 0) { - debug(1, "AP1 read result 0, for a request count of %u.", count); + debug(3, "AP1 read result 0, for a request count of %u.", count); } #endif if ((result == 0) && (errno != 0)) { char errorstring[1024]; strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "Connection %d: read result 0, error %d: \"%s\".", + debug(2, "Connection %d: read result 0, error %d: \"%s\".", conn->connection_number, errno, (char *)errorstring); } From 9b8a647d0006e13eb05093c47e824d9356a8704f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:17:09 +0000 Subject: [PATCH 57/61] Update check_ap2_systemv_full.yml --- .github/workflows/check_ap2_systemv_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemv_full.yml b/.github/workflows/check_ap2_systemv_full.yml index 3723f9d4..0665e836 100644 --- a/.github/workflows/check_ap2_systemv_full.yml +++ b/.github/workflows/check_ap2_systemv_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev - name: Configure run: | autoreconf -i From e4dbc0fc8d234d9afce18e555e1cdd6d51244e4c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:19:07 +0000 Subject: [PATCH 58/61] Update check_ap2_systemd_basic.yml --- .github/workflows/check_ap2_systemd_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_basic.yml b/.github/workflows/check_ap2_systemd_basic.yml index 28e59476..2596cfe9 100644 --- a/.github/workflows/check_ap2_systemd_basic.yml +++ b/.github/workflows/check_ap2_systemd_basic.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev - name: Configure run: | mkdir build From 7dfad43dc0c64b12684744e2c86c6d249822c2a1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:21:57 +0000 Subject: [PATCH 59/61] Update check_ap2_systemd_basic.yml Remove package that are already installed --- .github/workflows/check_ap2_systemd_basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_basic.yml b/.github/workflows/check_ap2_systemd_basic.yml index 2596cfe9..381d39a1 100644 --- a/.github/workflows/check_ap2_systemd_basic.yml +++ b/.github/workflows/check_ap2_systemd_basic.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev - name: Configure run: | mkdir build From 7b46d0f171550ccaf8ca318d39be81576f3a4687 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:25:48 +0000 Subject: [PATCH 60/61] Update check_ap2_systemd_full.yml Stop building with sndio and remove install requests for packages already installed --- .github/workflows/check_ap2_systemd_full.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 9deb930a..9ba7a4be 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -15,11 +15,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev - name: Configure run: | autoreconf -fi - ./configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-jack --with-pipe --with-sndio --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 + ./configure --sysconfdir=/etc --with-alsa --with-ao --with-dummy --with-jack --with-pipe --with-stdout --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-dbus-interface --with-mpris-interface --with-mqtt-client --with-airplay-2 - name: Make run: | make -j From 4895f41bc1e1dfe2e9e7bb7384a0dcf4363a4cfd Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:28:19 +0000 Subject: [PATCH 61/61] Update check_ap2_systemd_full.yml Remove libsndio-dev --- .github/workflows/check_ap2_systemd_full.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_ap2_systemd_full.yml b/.github/workflows/check_ap2_systemd_full.yml index 9ba7a4be..549b904b 100644 --- a/.github/workflows/check_ap2_systemd_full.yml +++ b/.github/workflows/check_ap2_systemd_full.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Dependencies - run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libsndio-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev + run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev - name: Configure run: | autoreconf -fi