From 77a5cf1e80c05065b210467752c9b506bee87caf Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Jul 2020 10:47:10 +0100 Subject: Ensure metadata and cover art are enabled if metadata support is included at compilation. Reword some misleading error messages, set convolution defaults even if no configuration file is found. --- dacp.c | 6 ++++-- metadata_hub.c | 4 ++-- shairport.c | 19 +++++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/dacp.c b/dacp.c index c6b9968..a5d0d65 100644 --- a/dacp.c +++ b/dacp.c @@ -154,7 +154,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // debug(1,"dacp_send_command: command is: \"%s\".",command); if (dacp_server.port == 0) { - debug(3, "No DACP port specified yet"); + // debug(3, "No DACP port specified yet"); result = 490; // no port specified } else { @@ -504,11 +504,13 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { (metadata_store.advanced_dacp_server_active != 0); metadata_store.dacp_server_active = 0; metadata_store.advanced_dacp_server_active = 0; + /* debug(3, "setting metadata_store.dacp_server_active and " "metadata_store.advanced_dacp_server_active to 0 with an update " "flag value of %d", ch); + */ metadata_hub_modify_epilog(ch); uint64_t time_to_wait_for_wakeup_ns = @@ -1249,7 +1251,7 @@ int dacp_get_volume(int32_t *the_actual_volume) { } else { debug(1, "Unexpected return code %d from dacp_get_speaker_list.", http_response); } - } else if (http_response != 400) { + } else if ((http_response != 400) && (http_response != 490)) { debug(3, "Unexpected return code %d from dacp_get_client_volume.", http_response); } if (the_actual_volume) { diff --git a/metadata_hub.c b/metadata_hub.c index 190895c..7b20ae1 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -148,7 +148,7 @@ void _metadata_hub_modify_prolog(const char *filename, const int linenumber) { } last_metadata_hub_modify_prolog_file = strdup(filename); last_metadata_hub_modify_prolog_line = linenumber; - debug(3, "Metadata_hub write lock acquired."); + // debug(3, "Metadata_hub write lock acquired."); } metadata_hub_re_lock_access_is_delayed = 0; } @@ -169,7 +169,7 @@ void _metadata_hub_modify_epilog(int modified, const char *filename, const int l } } pthread_rwlock_unlock(&metadata_hub_re_lock); - debug(3, "Metadata_hub write lock unlocked."); + // debug(3, "Metadata_hub write lock unlocked."); } /* diff --git a/shairport.c b/shairport.c index 14e5072..975b71f 100644 --- a/shairport.c +++ b/shairport.c @@ -266,7 +266,7 @@ void usage(char *progname) { printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. " "--metadata-pipename=/tmp/shairport-sync-metadata.\n"); printf(" The default is /tmp/shairport-sync-metadata.\n"); - printf(" --get-coverart send cover art through the metadata pipe.\n"); + printf(" -g, --get-coverart send cover art through the metadata pipe.\n"); #endif printf(" -u, --use-stderr log messages through STDERR rather than the system log.\n"); printf("\n"); @@ -418,6 +418,17 @@ int parse_options(int argc, char **argv) { // seconds then we can add an offset of 5.17 seconds and still leave a second's worth of buffers // for unexpected circumstances +#ifdef CONFIG_METADATA + /* Get the metadata setting. */ + config.metadata_enabled = 1; // if metadata support is included, then enable it by default + config.get_coverart = 1; // if metadata support is included, then enable it by default +#endif + +#ifdef CONFIG_CONVOLUTION + config.convolution_max_length = 8192; +#endif + config.loudness_reference_volume_db = -20; + #ifdef CONFIG_METADATA_HUB config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart"; config.scan_interval_when_active = @@ -846,7 +857,6 @@ int parse_options(int argc, char **argv) { #ifdef CONFIG_METADATA /* Get the metadata setting. */ - config.metadata_enabled = 1; // if metadata support is included, then enable it by default if (config_lookup_string(config.cfg, "metadata.enabled", &str)) { if (strcasecmp(str, "no") == 0) config.metadata_enabled = 0; @@ -856,7 +866,6 @@ int parse_options(int argc, char **argv) { die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\""); } - config.get_coverart = 1; // if metadata support is included, then enable it by default if (config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) { if (strcasecmp(str, "no") == 0) config.get_coverart = 0; @@ -988,7 +997,6 @@ int parse_options(int argc, char **argv) { dvalue); } - config.convolution_max_length = 8192; if (config_lookup_int(config.cfg, "dsp.convolution_max_length", &value)) { config.convolution_max_length = value; @@ -1015,7 +1023,6 @@ int parse_options(int argc, char **argv) { die("Invalid dsp.convolution. It should be \"yes\" or \"no\""); } - config.loudness_reference_volume_db = -20; if (config_lookup_float(config.cfg, "dsp.loudness_reference_volume_db", &dvalue)) { config.loudness_reference_volume_db = dvalue; if (dvalue > 0 || dvalue < -100) @@ -1168,7 +1175,7 @@ int parse_options(int argc, char **argv) { break; case 'g': if (config.metadata_enabled == 0) - die("If you want to get cover art, you must also select the --metadata-pipename option."); + die("If you want to get cover art, ensure metadata_enabled is true."); break; #endif case 'S': -- cgit v1.2.3 From 18616a97cdb56b0114d3dadc5fa6e93d598fd9df Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Jul 2020 10:57:52 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 4814b7d..1c7aa8a 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,5 +1,19 @@ +Version 3.3.7d14 +==== +**Enhancements** +* Fixed a situation where a misleading error message was given if a configuration file cound not be located and `get_coverart` was selected as a command line option. Apart from a misleading message, `get_coverart` and `enable_metadata` should have been enabled automatically where metadata support is included in the build configuration. + +Version 3.3.7d13 +==== +This is 3.3.7rc1 + +**Enhancements** +* Fixed another bug, related to the bug fixed in 3.3.7d4, where leading zeros were not removed from the DACP ID. Thanks again to [julianc1969](https://github.com/juliandc1969). + Version 3.3.7d12 ==== +This is 3.3.7rc0 + **Enhancements** * Added the the string `-alac` to the the version string in '-V' if Apple ALAC decoder support is included. -- cgit v1.2.3 From 9a390afcd69b34433a1d9a6924cf6a7ec415e101 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Jul 2020 10:59:00 +0100 Subject: 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 1c7aa8a..7b13112 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,7 +1,7 @@ Version 3.3.7d14 ==== **Enhancements** -* Fixed a situation where a misleading error message was given if a configuration file cound not be located and `get_coverart` was selected as a command line option. Apart from a misleading message, `get_coverart` and `enable_metadata` should have been enabled automatically where metadata support is included in the build configuration. +* Fixed a situation where a misleading error message was given if a configuration file cound not be located and `get_coverart` was selected as a command line option. Apart from a misleading message, `get_coverart` and `enable_metadata` should have been enabled automatically where metadata support is included in the build configuration. Thanks to [Craig Fletcher](https://github.com/leakypixel) for the bug report. Version 3.3.7d13 ==== -- cgit v1.2.3 From d3365f8bdcae1963d6eabf993adc90b1223920ee Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Jul 2020 11:52:52 +0100 Subject: Quieten a debug message --- dacp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dacp.c b/dacp.c index a5d0d65..a25bf9e 100644 --- a/dacp.c +++ b/dacp.c @@ -1249,7 +1249,7 @@ int dacp_get_volume(int32_t *the_actual_volume) { // metadata_store.speaker_volume = actual_volume; // metadata_hub_modify_epilog(1); } else { - debug(1, "Unexpected return code %d from dacp_get_speaker_list.", http_response); + debug(2, "Unexpected return code %d from dacp_get_speaker_list.", http_response); } } else if ((http_response != 400) && (http_response != 490)) { debug(3, "Unexpected return code %d from dacp_get_client_volume.", http_response); -- cgit v1.2.3 From 69d3b8d4a9ec7782f6d10b351116ce1064034790 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 13 Jul 2020 11:53:54 +0100 Subject: Ensure metadata and cover art are enabled if metadata support is included at compilation. Reword some misleading error messages, set convolution defaults even if no configuration file is found. Quieten a few debug messages. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 182d330..215148c 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], [3.3.7d13], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d14], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 06c80713bb4bdc97eda1f1d62859a2dc3dc61a17 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 27 Jul 2020 09:40:05 +0100 Subject: Open metadata and audio pipes with 666 permissions. --- audio_pipe.c | 2 +- rtsp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/audio_pipe.c b/audio_pipe.c index 6dedc46..45e0f06 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -121,7 +121,7 @@ static int init(int argc, char **argv) { // here, create the pipe mode_t oldumask = umask(000); - if (mkfifo(pipename, 0644) && errno != EEXIST) + if (mkfifo(pipename, 0666) && errno != EEXIST) die("Could not create audio pipe \"%s\"", pipename); umask(oldumask); diff --git a/rtsp.c b/rtsp.c index 2febacd..1f1cb9f 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1572,7 +1572,7 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { snprintf(path, pl + 1, "%s", config.metadata_pipename); mode_t oldumask = umask(000); - if (mkfifo(path, 0644) && errno != EEXIST) + if (mkfifo(path, 0666) && errno != EEXIST) die("Could not create metadata pipe \"%s\".", path); umask(oldumask); debug(1, "metadata pipe name is \"%s\".", path); -- cgit v1.2.3 From 7e2b8561218d0c71670684909f4c8bb8a946644d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 27 Jul 2020 09:41:33 +0100 Subject: Open metadata and audio pipes with 666 permissions. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 215148c..67d0a31 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], [3.3.7d14], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d15], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From b6cb19be176c763bd8cf4a741d5ba393683392d1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 27 Jul 2020 09:46:07 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 7b13112..4982993 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.7d15 +==== +**Enhancements** +* Changed permissions when creating the metadata pipe (and the audio pipe in the `pipe` backend) to `rw-rw-rw-` for compatibility with `snapserver`. + Version 3.3.7d14 ==== **Enhancements** -- cgit v1.2.3 From 65391a6916c1ce72d90d56bbc7da3982ae6551b5 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 13 Aug 2020 17:44:35 +0100 Subject: Potentially fix a flaw in resyncing. A flush is set up but not triggered. --- player.c | 1 + 1 file changed, 1 insertion(+) diff --git a/player.c b/player.c index dcef6c4..d725ed9 100644 --- a/player.c +++ b/player.c @@ -2320,6 +2320,7 @@ void *player_thread_func(void *arg) { uint32_t frames_to_drop_sized = local_frames_to_drop; debug_mutex_lock(&conn->flush_mutex, 1000, 1); + conn->flush_requested = 1; conn->flush_rtp_timestamp = inframe->given_timestamp + frames_to_drop_sized; // flush all packets up to (and including?) this -- cgit v1.2.3 From 42ea538a6279d4e295cf7c99f79a8e7197aad286 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 13 Aug 2020 19:35:31 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 4982993..db018d0 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.7d16 +==== +**Bug Fix** +* Fix potential inability to restore synchrnoisation quickly in certain situations. + Version 3.3.7d15 ==== **Enhancements** -- cgit v1.2.3 From ad1015788a9af5eb85b6dfb0ff9ca2446c28a020 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 13 Aug 2020 19:38:15 +0100 Subject: Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 67d0a31..9f64310 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], [3.3.7d15], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d17], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 5ddb80e0db201eedfbe0a80217283ba86b984955 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 13 Aug 2020 19:38:40 +0100 Subject: Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9f64310..72fe895 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], [3.3.7d17], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d16], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 919e4d6ac1bf7fcbe8d2b57cab9f413aced69f2f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 13 Aug 2020 20:41:30 +0100 Subject: A gentler flush process for resyncing. --- player.c | 119 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/player.c b/player.c index d725ed9..edca685 100644 --- a/player.c +++ b/player.c @@ -950,71 +950,71 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { debug(2, "flush request: flush output device."); } conn->flush_output_flushed = 1; - // now check to see it the flush request is for frames in the buffer or not - // if the first_packet_timestamp is zero, don't check - int flush_needed = 0; - int drop_request = 0; - if (conn->flush_rtp_timestamp == 0) { - debug(1, "flush request: flush frame 0 -- flush assumed to be needed."); - flush_needed = 1; - drop_request = 1; - } else { - if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) { - abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read); - abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1); - if ((firstPacket != NULL) && (firstPacket->ready)) { - // discard flushes more than 10 seconds into the future -- they are probably bogus - uint32_t first_frame_in_buffer = firstPacket->given_timestamp; - int32_t offset_from_first_frame = (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer); - if (offset_from_first_frame > (int)conn->input_rate * 10) { - debug(1, "flush request: sanity check -- flush frame %u is too far into the future from the first frame %u -- discarded.", conn->flush_rtp_timestamp, first_frame_in_buffer); - drop_request = 1; - } else { - if ((lastPacket != NULL) && (lastPacket->ready)) { - // we have enough information to check if the flush is needed or can be discarded - uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1; - // now we have to work out if the flush frame is in the buffer - // if it is later than the end of the buffer, flush everything and keep the request active. - // if it is in the buffer, we need to flush part of the buffer. Actually we flush the entire buffer and drop the request. - // if it is before the buffer, no flush is needed. Drop the request. - if (offset_from_first_frame > 0) { - int32_t offset_to_last_frame = (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp); - if (offset_to_last_frame >= 0) { - debug(2,"flush request: flush frame %u active -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - drop_request = 1; - flush_needed = 1; - } else { - debug(2,"flush request: flush frame %u pending -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - flush_needed = 1; - } + } + // now check to see it the flush request is for frames in the buffer or not + // if the first_packet_timestamp is zero, don't check + int flush_needed = 0; + int drop_request = 0; + if ((conn->flush_requested == 1) && (conn->flush_rtp_timestamp == 0)) { + debug(1, "flush request: flush frame 0 -- flush assumed to be needed."); + flush_needed = 1; + drop_request = 1; + } else if (conn->flush_rtp_timestamp != 0) { + if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) { + abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read); + abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1); + if ((firstPacket != NULL) && (firstPacket->ready)) { + // discard flushes more than 10 seconds into the future -- they are probably bogus + uint32_t first_frame_in_buffer = firstPacket->given_timestamp; + int32_t offset_from_first_frame = (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer); + if (offset_from_first_frame > (int)conn->input_rate * 10) { + debug(1, "flush request: sanity check -- flush frame %u is too far into the future from the first frame %u -- discarded.", conn->flush_rtp_timestamp, first_frame_in_buffer); + drop_request = 1; + } else { + if ((lastPacket != NULL) && (lastPacket->ready)) { + // we have enough information to check if the flush is needed or can be discarded + uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1; + // now we have to work out if the flush frame is in the buffer + // if it is later than the end of the buffer, flush everything and keep the request active. + // if it is in the buffer, we need to flush part of the buffer. Actually we flush the entire buffer and drop the request. + // if it is before the buffer, no flush is needed. Drop the request. + if (offset_from_first_frame > 0) { + int32_t offset_to_last_frame = (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp); + if (offset_to_last_frame >= 0) { + debug(2,"flush request: flush frame %u active -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); + drop_request = 1; + flush_needed = 1; } else { - debug(2,"flush request: flush frame %u expired -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - drop_request = 1; + debug(2,"flush request: flush frame %u pending -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); + flush_needed = 1; } + } else { + debug(2,"flush request: flush frame %u expired -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); + drop_request = 1; } } } - } else { - debug(3, "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: %u, ab_write: %u", conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write); - // leave flush request pending and don't do a buffer flush, because there isn't one } - } - if (flush_needed) { - debug(2, "flush request: flush done."); - ab_resync(conn); // no cancellation points - conn->first_packet_timestamp = 0; - conn->first_packet_time_to_play = 0; - conn->time_since_play_started = 0; - have_sent_prefiller_silence = 0; - dac_delay = 0; - } - if (drop_request) { - debug(2, "flush request: request dropped."); - conn->flush_requested = 0; - conn->flush_rtp_timestamp = 0; - conn->flush_output_flushed = 0; + } else { + debug(3, "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: %u, ab_write: %u", conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write); + // leave flush request pending and don't do a buffer flush, because there isn't one } - } + } + if (flush_needed) { + debug(2, "flush request: flush done."); + ab_resync(conn); // no cancellation points + conn->first_packet_timestamp = 0; + conn->first_packet_time_to_play = 0; + conn->time_since_play_started = 0; + have_sent_prefiller_silence = 0; + dac_delay = 0; + } + if (drop_request) { + debug(2, "flush request: request dropped."); + conn->flush_requested = 0; + conn->flush_rtp_timestamp = 0; + conn->flush_output_flushed = 0; + } debug_mutex_unlock(&conn->flush_mutex, 0); if (conn->ab_synced) { curframe = conn->audio_buffer + BUFIDX(conn->ab_read); @@ -1997,7 +1997,7 @@ void *player_thread_func(void *arg) { // debug(3, "Play frame %d.", play_number); conn->play_number_after_flush++; if (inframe->given_timestamp == 0) { - debug(1, + debug(2, "Player has supplied a silent frame, (possibly frame %u) for play number %d, " "status 0x%X after %u resend requests.", SUCCESSOR(conn->last_seqno_read), play_number, inframe->status, @@ -2320,7 +2320,6 @@ void *player_thread_func(void *arg) { uint32_t frames_to_drop_sized = local_frames_to_drop; debug_mutex_lock(&conn->flush_mutex, 1000, 1); - conn->flush_requested = 1; conn->flush_rtp_timestamp = inframe->given_timestamp + frames_to_drop_sized; // flush all packets up to (and including?) this -- cgit v1.2.3 From 41868430eb65a79b5a651e16c82a208ab8a760e7 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 14 Aug 2020 13:27:34 +0100 Subject: 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 db018d0..4ec5761 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,7 +1,7 @@ Version 3.3.7d16 ==== **Bug Fix** -* Fix potential inability to restore synchrnoisation quickly in certain situations. +* Fix potential inability to restore synchronisation quickly in certain situations. Version 3.3.7d15 ==== -- cgit v1.2.3 From aa38f7820c58a67cdac08063792f0665f506dc75 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:27:36 +0100 Subject: Add the word 'jack' to the version string if Jack Audi support is compiled in. --- common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common.c b/common.c index 39c4d62..dbb4bad 100644 --- a/common.c +++ b/common.c @@ -1460,6 +1460,9 @@ char *get_version_string() { #ifdef CONFIG_SNDIO strcat(version_string, "-sndio"); #endif +#ifdef CONFIG_JACK + strcat(version_string, "-jack"); +#endif #ifdef CONFIG_AO strcat(version_string, "-ao"); #endif -- cgit v1.2.3 From 08f0878055a5ad530cba343240f0706cc34cc927 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:28:02 +0100 Subject: Add the word 'jack' to the version string if Jack Audi support is compiled in. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 72fe895..9f64310 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], [3.3.7d16], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d17], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 2c2ac1be9344f2ab2210bb425fc601215427b02f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 2 Sep 2020 17:31:47 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 4ec5761..8abde68 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.7d17 +==== +**Bug Fix** +* Include the word `jack` in the version string if support for [Jack Audio](https://jackaudio.org) is included. + Version 3.3.7d16 ==== **Bug Fix** -- cgit v1.2.3 From ca56287254776bb5e3838258da57784d70132ab1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 3 Sep 2020 09:53:13 +0100 Subject: Make the first output backend in the list of backends the default and make its name the default output_name. Clang-format everything --- audio.c | 2 +- audio.h | 2 +- audio_pipe.c | 32 +- common.c | 180 +++++----- common.h | 21 +- metadata_hub.c | 44 +-- metadata_hub.h | 7 +- player.c | 1032 +++++++++++++++++++++++++++++--------------------------- player.h | 4 +- rtp.c | 99 +++--- rtsp.c | 348 ++++++++++--------- shairport.c | 209 ++++++------ 12 files changed, 1038 insertions(+), 942 deletions(-) diff --git a/audio.c b/audio.c index 23b1760..0fe7b04 100644 --- a/audio.c +++ b/audio.c @@ -89,7 +89,7 @@ static audio_output *outputs[] = { #endif NULL}; -audio_output *audio_get_output(char *name) { +audio_output *audio_get_output(const char *name) { audio_output **out; // default to the first diff --git a/audio.h b/audio.h index 856f184..3413250 100644 --- a/audio.h +++ b/audio.h @@ -54,7 +54,7 @@ typedef struct { } audio_output; -audio_output *audio_get_output(char *name); +audio_output *audio_get_output(const char *name); void audio_ls_outputs(void); void parse_general_audio_options(void); diff --git a/audio_pipe.c b/audio_pipe.c index 45e0f06..05c9862 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -51,33 +51,34 @@ static void start(__attribute__((unused)) int sample_rate, // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO // open for reading." - fd = try_to_open_pipe_for_writing(pipename); - // we check that it's not a "real" error. From the "man 2 open" page: - // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO - // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "audio_pipe start -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, pipename); - warn("can not open audio pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, pipename); - } + fd = try_to_open_pipe_for_writing(pipename); + // we check that it's not a "real" error. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO + // open for reading." Which is okay. + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "audio_pipe start -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, pipename); + warn("can not open audio pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, pipename); + } } static int play(void *buf, int samples) { // if the file is not open, try to open it. char errorstring[1024]; if (fd == -1) { - fd = try_to_open_pipe_for_writing(pipename); + fd = try_to_open_pipe_for_writing(pipename); } // if it's got a reader, write to it. if (fd > 0) { - //int rc = non_blocking_write(fd, buf, samples * 4); + // int rc = non_blocking_write(fd, buf, samples * 4); int rc = write(fd, buf, samples * 4); if ((rc < 0) && (errno != EPIPE)) { strerror_r(errno, (char *)errorstring, 1024); - debug(1, "audio_pip play: error %d writing to the pipe named \"%s\": \"%s\".", errno, pipename, errorstring); + debug(1, "audio_pip play: error %d writing to the pipe named \"%s\": \"%s\".", errno, + pipename, errorstring); } } return 0; @@ -85,7 +86,6 @@ static int play(void *buf, int samples) { static void stop(void) { // Don't close the pipe just because a play session has stopped. - } static int init(int argc, char **argv) { diff --git a/common.c b/common.c index dbb4bad..c1deea3 100644 --- a/common.c +++ b/common.c @@ -29,6 +29,8 @@ #include "common.h" #include #include +#include +#include #include #include #include @@ -40,8 +42,6 @@ #include #include #include -#include -#include #ifdef COMPILE_FOR_OSX #include @@ -141,64 +141,66 @@ void do_sps_log_to_stdout(__attribute__((unused)) int prio, const char *t, ...) fprintf(stdout, "%s\n", s); } -int create_log_file(const char* path) { - int fd = -1; - if (path != NULL) { - char *dirc = strdup(path); - if (dirc) { - char *dname = dirname(dirc); - // create the directory, if necessary - int result = 0; - if (dname) { - char *pdir = realpath(dname, NULL); // will return a NULL if the directory doesn't exist - if (pdir == NULL) { - mode_t oldumask = umask(000); - result = mkpath(dname, 0777); - umask(oldumask); - } else { - free(pdir); - } - if ((result == 0) || (result == -EEXIST)) { - // now open the file - fd = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if ((fd == -1) && (errno == EEXIST)) - fd = open(path, O_WRONLY | O_APPEND | O_NONBLOCK); - - if (fd >= 0) { - // now we switch to blocking mode - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { -// strerror_r(errno, (char *)errorstring, sizeof(errorstring)); -// debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno, -// (char *)errorstring, pathname); - } else { - flags = fcntl(fd, F_SETFL,flags & ~O_NONBLOCK); -// if (flags == -1) { -// strerror_r(errno, (char *)errorstring, sizeof(errorstring)); -// debug(1, "create_log_file -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, -// (char *)errorstring, pathname); - } - } - } - } - free(dirc); - } - } - return fd; +int create_log_file(const char *path) { + int fd = -1; + if (path != NULL) { + char *dirc = strdup(path); + if (dirc) { + char *dname = dirname(dirc); + // create the directory, if necessary + int result = 0; + if (dname) { + char *pdir = realpath(dname, NULL); // will return a NULL if the directory doesn't exist + if (pdir == NULL) { + mode_t oldumask = umask(000); + result = mkpath(dname, 0777); + umask(oldumask); + } else { + free(pdir); + } + if ((result == 0) || (result == -EEXIST)) { + // now open the file + fd = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if ((fd == -1) && (errno == EEXIST)) + fd = open(path, O_WRONLY | O_APPEND | O_NONBLOCK); + + if (fd >= 0) { + // now we switch to blocking mode + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + // strerror_r(errno, (char + //*)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d (\"%s\") + //getting flags of pipe: \"%s\".", errno, (char *)errorstring, pathname); + } else { + flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); + // if (flags == -1) { + // strerror_r(errno, + //(char *)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d + //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, (char *)errorstring, + //pathname); + } + } + } + } + free(dirc); + } + } + return fd; } void do_sps_log_to_fd(__attribute__((unused)) int prio, const char *t, ...) { - char s[1024]; - va_list args; - va_start(args, t); - vsnprintf(s, sizeof(s), t, args); - va_end(args); - if (config.log_fd == -1) - config.log_fd = create_log_file(config.log_file_path); - if (config.log_fd >= 0) { - dprintf(config.log_fd, "%s\n", s); + char s[1024]; + va_list args; + va_start(args, t); + vsnprintf(s, sizeof(s), t, args); + va_end(args); + if (config.log_fd == -1) + config.log_fd = create_log_file(config.log_file_path); + if (config.log_fd >= 0) { + dprintf(config.log_fd, "%s\n", s); } else if (errno != ENXIO) { // maybe there is a pipe there but not hooked up - fprintf(stderr, "%s\n", s); + fprintf(stderr, "%s\n", s); } } @@ -207,9 +209,9 @@ void log_to_stdout() { sps_log = do_sps_log_to_stdout; } void log_to_file() { sps_log = do_sps_log_to_fd; } void log_to_syslog() { #ifdef CONFIG_LIBDAEMON - sps_log = daemon_log; + sps_log = daemon_log; #else - sps_log = syslog; + sps_log = syslog; #endif } @@ -309,8 +311,8 @@ void _die(const char *filename, const int linenumber, const char *format, ...) { 1.0 * time_since_last_debug_message / 1000000000, filename, linenumber, " *fatal error: "); } else { - strncpy(b, "fatal error: ", sizeof(b)); - s = b+strlen(b); + strncpy(b, "fatal error: ", sizeof(b)); + s = b + strlen(b); } va_list args; va_start(args, format); @@ -339,8 +341,8 @@ void _warn(const char *filename, const int linenumber, const char *format, ...) 1.0 * time_since_last_debug_message / 1000000000, filename, linenumber, " *warning: "); } else { - strncpy(b, "warning: ", sizeof(b)); - s = b+strlen(b); + strncpy(b, "warning: ", sizeof(b)); + s = b + strlen(b); } va_list args; va_start(args, format); @@ -1134,12 +1136,12 @@ uint64_t get_absolute_time_in_ns() { return time_now_ns; } -int try_to_open_pipe_for_writing(const char* pathname) { - // tries to open the pipe in non-blocking mode first. - // if it succeeds, it sets it to blocking. - // if not, it returns -1. +int try_to_open_pipe_for_writing(const char *pathname) { + // tries to open the pipe in non-blocking mode first. + // if it succeeds, it sets it to blocking. + // if not, it returns -1. - int fdis = open(pathname, O_WRONLY | O_NONBLOCK); // open it in non blocking mode first + int fdis = open(pathname, O_WRONLY | O_NONBLOCK); // open it in non blocking mode first // we check that it's not a "real" error. From the "man 2 open" page: // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO @@ -1147,24 +1149,24 @@ int try_to_open_pipe_for_writing(const char* pathname) { // This is checked by the caller. if (fdis >= 0) { - // now we switch to blocking mode - int flags = fcntl(fdis, F_GETFL); - if (flags == -1) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "try_to_open_pipe -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno, - (char *)errorstring, pathname); - } else { - flags = fcntl(fdis, F_SETFL,flags & ~O_NONBLOCK); - if (flags == -1) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "try_to_open_pipe -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, - (char *)errorstring, pathname); - } - } + // now we switch to blocking mode + int flags = fcntl(fdis, F_GETFL); + if (flags == -1) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "try_to_open_pipe -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno, + (char *)errorstring, pathname); + } else { + flags = fcntl(fdis, F_SETFL, flags & ~O_NONBLOCK); + if (flags == -1) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "try_to_open_pipe -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, + (char *)errorstring, pathname); + } + } } - return fdis; + return fdis; } /* from @@ -1710,11 +1712,11 @@ int string_update_with_size(char **str, int *flag, char *s, size_t len) { } // from https://stackoverflow.com/questions/13663617/memdup-function-in-c, with thanks -void* memdup(const void* mem, size_t size) { - void* out = malloc(size); +void *memdup(const void *mem, size_t size) { + void *out = malloc(size); - if(out != NULL) - memcpy(out, mem, size); + if (out != NULL) + memcpy(out, mem, size); - return out; + return out; } diff --git a/common.h b/common.h index 1f5d1c8..68666ac 100644 --- a/common.h +++ b/common.h @@ -182,8 +182,8 @@ typedef struct { char *pidfile; #endif - int log_fd; // file descriptor of the file or pipe to log stuff to. - char *log_file_path; // path to file or pipe to log to, if any + int log_fd; // file descriptor of the file or pipe to log stuff to. + char *log_file_path; // path to file or pipe to log to, if any int logOutputLevel; // log output level int debugger_show_elapsed_time; // in the debug message, display the time since startup int debugger_show_relative_time; // in the debug message, display the time since the last one @@ -285,10 +285,10 @@ typedef struct { int jack_soxr_resample_quality; #endif #endif - void *gradients; // a linked list of the clock gradients discovered for all DACP IDs - // can't use IP numbers as they might be given to different devices - // can't get hold of MAC addresses. - // can't define the nvll linked list struct here + void *gradients; // a linked list of the clock gradients discovered for all DACP IDs + // can't use IP numbers as they might be given to different devices + // can't get hold of MAC addresses. + // can't define the nvll linked list struct here } shairport_cfg; // accessors to config for multi-thread access @@ -303,9 +303,7 @@ void memory_barrier(); void log_to_stderr(); // call this to direct logging to stderr; void log_to_stdout(); // call this to direct logging to stdout; void log_to_syslog(); // call this to direct logging to the system log; -void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe; - - +void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe; // true if Shairport Sync is supposed to be sending output to the output device, false otherwise @@ -313,7 +311,8 @@ int get_requested_connection_state_to_output(); void set_requested_connection_state_to_output(int v); -int try_to_open_pipe_for_writing(const char* pathname); // open it without blocking if it's not hooked up +int try_to_open_pipe_for_writing( + const char *pathname); // open it without blocking if it's not hooked up /* from * http://coding.debuntu.org/c-implementing-str_replace-replace-all-occurrences-substring#comment-722 @@ -446,6 +445,6 @@ void malloc_cleanup(void *arg); int string_update_with_size(char **str, int *flag, char *s, size_t len); // from https://stackoverflow.com/questions/13663617/memdup-function-in-c, with thanks -void* memdup(const void* mem, size_t size); +void *memdup(const void *mem, size_t size); #endif // _COMMON_H diff --git a/metadata_hub.c b/metadata_hub.c index 7b20ae1..a899574 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -136,18 +136,20 @@ void _metadata_hub_modify_prolog(const char *filename, const int linenumber) { // debug(1, "locking metadata hub for writing"); if (pthread_rwlock_trywrlock(&metadata_hub_re_lock) != 0) { if (last_metadata_hub_modify_prolog_file) - debug(2, "Metadata_hub write lock at \"%s:%d\" is already taken at \"%s:%d\" -- must wait.", filename, linenumber, last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line); + debug(2, "Metadata_hub write lock at \"%s:%d\" is already taken at \"%s:%d\" -- must wait.", + filename, linenumber, last_metadata_hub_modify_prolog_file, + last_metadata_hub_modify_prolog_line); else - debug(2, "Metadata_hub write lock is already taken by unknown -- must wait."); + debug(2, "Metadata_hub write lock is already taken by unknown -- must wait."); metadata_hub_re_lock_access_is_delayed = 0; pthread_rwlock_wrlock(&metadata_hub_re_lock); debug(2, "Okay -- acquired the metadata_hub write lock at \"%s:%d\".", filename, linenumber); } else { - if (last_metadata_hub_modify_prolog_file) { - free(last_metadata_hub_modify_prolog_file); - } - last_metadata_hub_modify_prolog_file = strdup(filename); - last_metadata_hub_modify_prolog_line = linenumber; + if (last_metadata_hub_modify_prolog_file) { + free(last_metadata_hub_modify_prolog_file); + } + last_metadata_hub_modify_prolog_file = strdup(filename); + last_metadata_hub_modify_prolog_line = linenumber; // debug(3, "Metadata_hub write lock acquired."); } metadata_hub_re_lock_access_is_delayed = 0; @@ -160,13 +162,16 @@ void _metadata_hub_modify_epilog(int modified, const char *filename, const int l run_metadata_watchers(); } if (metadata_hub_re_lock_access_is_delayed) { - if (last_metadata_hub_modify_prolog_file) { - debug(1, "Metadata_hub write lock taken at \"%s:%d\" is freed at \"%s:%d\".", last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line, filename, linenumber); - free(last_metadata_hub_modify_prolog_file); - last_metadata_hub_modify_prolog_file = NULL; - } else { - debug(1, "Metadata_hub write lock taken at an unknown place is freed at \"%s:%d\".", filename, linenumber); - } + if (last_metadata_hub_modify_prolog_file) { + debug(1, "Metadata_hub write lock taken at \"%s:%d\" is freed at \"%s:%d\".", + last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line, filename, + linenumber); + free(last_metadata_hub_modify_prolog_file); + last_metadata_hub_modify_prolog_file = NULL; + } else { + debug(1, "Metadata_hub write lock taken at an unknown place is freed at \"%s:%d\".", filename, + linenumber); + } } pthread_rwlock_unlock(&metadata_hub_re_lock); // debug(3, "Metadata_hub write lock unlocked."); @@ -506,10 +511,11 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin debug(2, "MH Picture received, length %u bytes.", length); char uri[2048]; - if ((length > 16) && (strcmp(config.cover_art_cache_dir,"")!=0)) { // if it's okay to write the file - // make this uncancellable - int oldState; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + if ((length > 16) && + (strcmp(config.cover_art_cache_dir, "") != 0)) { // if it's okay to write the file + // make this uncancellable + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable char *pathname = metadata_write_image_file(data, length); snprintf(uri, sizeof(uri), "file://%s", pathname); free(pathname); @@ -524,7 +530,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin changed = 1; else changed = 0; -// pthread_cleanup_pop(0); // don't remove the lock -- it'll have been done + // pthread_cleanup_pop(0); // don't remove the lock -- it'll have been done break; case 'clip': cs = strndup(data, length); diff --git a/metadata_hub.h b/metadata_hub.h index 760a848..bdbbf32 100644 --- a/metadata_hub.h +++ b/metadata_hub.h @@ -149,7 +149,9 @@ void metadata_hub_release_track_artwork(void); // these functions lock and unlock the read-write mutex on the metadata hub and run the watchers // afterwards void _metadata_hub_modify_prolog(const char *filename, const int linenumber); -void _metadata_hub_modify_epilog(int modified, const char *filename, const int linenumber); // set to true if modifications occurred, 0 otherwise +void _metadata_hub_modify_epilog( + int modified, const char *filename, + const int linenumber); // set to true if modifications occurred, 0 otherwise /* // these are for safe reading @@ -158,7 +160,8 @@ void _metadata_hub_read_epilog(const char *filename, const int linenumber); */ #define metadata_hub_modify_prolog(void) _metadata_hub_modify_prolog(__FILE__, __LINE__) -#define metadata_hub_modify_epilog(modified) _metadata_hub_modify_epilog(modified, __FILE__, __LINE__) +#define metadata_hub_modify_epilog(modified) \ + _metadata_hub_modify_epilog(modified, __FILE__, __LINE__) #define metadata_hub_read_prolog(void) _metadata_hub_read_prolog(__FILE__, __LINE__) #define metadata_hub_read_epilog(void) _metadata_hub_modify_epilog(__FILE__, __LINE__) diff --git a/player.c b/player.c index edca685..78912e3 100644 --- a/player.c +++ b/player.c @@ -143,7 +143,6 @@ static void ab_resync(rtsp_conn_info *conn) { conn->ab_buffering = 1; } - // given starting and ending points as unsigned 16-bit integers running modulo 2^16, returns the // position of x in the interval in *pos // returns true if x is actually within the buffer @@ -174,25 +173,21 @@ int position_in_modulo_uint16_t_buffer(uint16_t x, uint16_t start, uint16_t end, return response; } -static inline seq_t SUCCESSOR(seq_t x) { - return x + 1; -} +static inline seq_t SUCCESSOR(seq_t x) { return x + 1; } // a minus b int16_t seq_diff(seq_t a, seq_t b) { - int16_t response; - seq_t diff = a - b; - seq_t invdiff = b - a; - if (diff < invdiff) - response = diff; - else - response = -invdiff; + int16_t response; + seq_t diff = a - b; + seq_t invdiff = b - a; + if (diff < invdiff) + response = diff; + else + response = -invdiff; return response; } -static inline seq_t seq_sum(seq_t a, seq_t b) { - return a + b; -} +static inline seq_t seq_sum(seq_t a, seq_t b) { return a + b; } // This orders u and v by picking the smaller of the two modulo differences // in unsigned modulo arithmetic and setting the sign of the result accordingly. @@ -215,16 +210,15 @@ static inline seq_t seq_sum(seq_t a, seq_t b) { // If that ever happens and there is a real ambiguity in the application, // the modulo chosen is too small. - int64_t int64_mod_difference(const uint64_t u, const uint64_t v, const uint64_t modulo) { - int64_t response; - uint64_t diff = (u - v) % modulo; - uint64_t invdiff = (v - u) % modulo; - if (diff < invdiff) - response = diff; - else - response = -invdiff; - return response; + int64_t response; + uint64_t diff = (u - v) % modulo; + uint64_t invdiff = (v - u) % modulo; + if (diff < invdiff) + response = diff; + else + response = -invdiff; + return response; } void reset_input_flow_metrics(rtsp_conn_info *conn) { @@ -466,172 +460,173 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in conn->packet_count_since_flush++; conn->time_of_last_audio_packet = time_now; if (conn->connection_state_to_output) { // if we are supposed to be processing these packets - abuf_t *abuf = 0; - if (!conn->ab_synced) { - // if this is the first packet... - debug(3, "syncing to seqno %u.", seqno); - conn->ab_write = seqno; - conn->ab_read = seqno; - conn->ab_synced = 1; - } - int16_t write_point_gap = seq_diff(seqno,conn->ab_write); // this is the difference between - // the incoming packet number and the packet number that was expected. - if (write_point_gap == 0) { // if this is the expected packet (which could be the first packet...) - if (conn->input_frame_rate_starting_point_is_valid == 0) { - if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) { - conn->frames_inward_measurement_start_time = time_now; - conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp; - conn->input_frame_rate_starting_point_is_valid = 1; // valid now - } - } - conn->frames_inward_measurement_time = time_now; - conn->frames_inward_frames_received_at_measurement_time = actual_timestamp; - abuf = conn->audio_buffer + BUFIDX(seqno); - conn->ab_write = SUCCESSOR(seqno); // move the write pointer to the next free space - } else if (write_point_gap > 0) { // newer than expected - // initialise the frames in between - int i; - for (i = 0; i < write_point_gap; i++) { - abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i)); - abuf->ready = 0; // to be sure, to be sure - abuf->resend_request_number = 0; - abuf->initialisation_time = - time_now; // this represents when the packet was noticed to be missing - abuf->status = 1 << 0; // signifying missing - abuf->resend_time = 0; - abuf->given_timestamp = 0; - abuf->sequence_number = 0; - } - abuf = conn->audio_buffer + BUFIDX(seqno); - conn->ab_write = SUCCESSOR(seqno); - } else if (seq_diff(seqno,conn->ab_read) > 0) { // older than expected but still not too late - conn->late_packets++; - abuf = conn->audio_buffer + BUFIDX(seqno); - } else { // too late. - conn->too_late_packets++; - } - - if (abuf) { - int datalen = conn->max_frames_per_packet; - abuf->initialisation_time = time_now; - abuf->resend_time = 0; - if (audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0) { - abuf->ready = 1; - abuf->status = 0; // signifying that it was received - abuf->length = datalen; - abuf->given_timestamp = actual_timestamp; - abuf->sequence_number = seqno; - } else { - debug(1, "Bad audio packet detected and discarded."); - abuf->ready = 0; - abuf->status = 1 << 1; // bad packet, discarded - abuf->resend_request_number = 0; - abuf->given_timestamp = 0; - abuf->sequence_number = 0; - } - } - - int rc = pthread_cond_signal(&conn->flowcontrol); - if (rc) - debug(1, "Error signalling flowcontrol."); - - // resend checks - { - uint64_t minimum_wait_time = - (uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000); - uint64_t resend_repeat_interval = - (uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000); - uint64_t minimum_remaining_time = (uint64_t)( - (config.resend_control_last_check_time + config.audio_backend_buffer_desired_length) * - (uint64_t)1000000000); - uint64_t latency_time = (uint64_t)(conn->latency * (uint64_t)1000000000); - latency_time = latency_time / (uint64_t)conn->input_rate; - - int x; // this is the first frame to be checked - // if we detected a first empty frame before and if it's still in the buffer! - if ((first_possibly_missing_frame >= 0) && - (position_in_modulo_uint16_t_buffer(first_possibly_missing_frame, conn->ab_read, - conn->ab_write, NULL))) { - x = first_possibly_missing_frame; - } else { - x = conn->ab_read; - } - - first_possibly_missing_frame = -1; // has not been set - - int missing_frame_run_count = 0; - int start_of_missing_frame_run = -1; - int number_of_missing_frames = 0; - while (x != conn->ab_write) { - abuf_t *check_buf = conn->audio_buffer + BUFIDX(x); - if (!check_buf->ready) { - if (first_possibly_missing_frame < 0) - first_possibly_missing_frame = x; - number_of_missing_frames++; - // debug(1, "frame %u's initialisation_time is 0x%" PRIx64 ", latency_time is 0x%" - // PRIx64 ", time_now is 0x%" PRIx64 ", minimum_remaining_time is 0x%" PRIx64 ".", x, - // check_buf->initialisation_time, latency_time, time_now, minimum_remaining_time); - int too_late = ((check_buf->initialisation_time < (time_now - latency_time)) || - ((check_buf->initialisation_time - (time_now - latency_time)) < - minimum_remaining_time)); - int too_early = ((time_now - check_buf->initialisation_time) < minimum_wait_time); - int too_soon_after_last_request = - ((check_buf->resend_time != 0) && - ((time_now - check_buf->resend_time) < - resend_repeat_interval)); // time_now can never be less than the time_tag - - if (too_late) - check_buf->status |= 1 << 2; // too late - else - check_buf->status &= 0xFF - (1 << 2); // not too late - if (too_early) - check_buf->status |= 1 << 3; // too early - else - check_buf->status &= 0xFF - (1 << 3); // not too early - if (too_soon_after_last_request) - check_buf->status |= 1 << 4; // too soon after last request - else - check_buf->status &= 0xFF - (1 << 4); // not too soon after last request - - if ((!too_soon_after_last_request) && (!too_late) && (!too_early)) { - if (start_of_missing_frame_run == -1) { - start_of_missing_frame_run = x; - missing_frame_run_count = 1; - } else { - missing_frame_run_count++; - } - check_buf->resend_time = time_now; // setting the time to now because we are - // definitely going to take action - check_buf->resend_request_number++; - debug(3, "Frame %d is missing with ab_read of %u and ab_write of %u.", x, - conn->ab_read, conn->ab_write); - } - // if (too_late) { - // debug(1,"too late to get missing frame %u.", x); - // } - } - // if (number_of_missing_frames != 0) - // debug(1,"check with x = %u, ab_read = %u, ab_write = %u, first_possibly_missing_frame - // = %d.", x, conn->ab_read, conn->ab_write, first_possibly_missing_frame); - x = (x + 1) & 0xffff; - if (((check_buf->ready) || (x == conn->ab_write)) && (missing_frame_run_count > 0)) { - // send a resend request - if (missing_frame_run_count > 1) - debug(3, "request resend of %d packets starting at seqno %u.", - missing_frame_run_count, start_of_missing_frame_run); - if (config.disable_resend_requests == 0) { - debug_mutex_unlock(&conn->ab_mutex, 3); - rtp_request_resend(start_of_missing_frame_run, missing_frame_run_count, conn); - debug_mutex_lock(&conn->ab_mutex, 20000, 1); - conn->resend_requests++; - } - start_of_missing_frame_run = -1; - missing_frame_run_count = 0; - } - } - if (number_of_missing_frames == 0) - first_possibly_missing_frame = conn->ab_write; - } + abuf_t *abuf = 0; + if (!conn->ab_synced) { + // if this is the first packet... + debug(3, "syncing to seqno %u.", seqno); + conn->ab_write = seqno; + conn->ab_read = seqno; + conn->ab_synced = 1; + } + int16_t write_point_gap = seq_diff(seqno, conn->ab_write); // this is the difference between + // the incoming packet number and the packet number that was expected. + if (write_point_gap == + 0) { // if this is the expected packet (which could be the first packet...) + if (conn->input_frame_rate_starting_point_is_valid == 0) { + if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) { + conn->frames_inward_measurement_start_time = time_now; + conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp; + conn->input_frame_rate_starting_point_is_valid = 1; // valid now + } + } + conn->frames_inward_measurement_time = time_now; + conn->frames_inward_frames_received_at_measurement_time = actual_timestamp; + abuf = conn->audio_buffer + BUFIDX(seqno); + conn->ab_write = SUCCESSOR(seqno); // move the write pointer to the next free space + } else if (write_point_gap > 0) { // newer than expected + // initialise the frames in between + int i; + for (i = 0; i < write_point_gap; i++) { + abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i)); + abuf->ready = 0; // to be sure, to be sure + abuf->resend_request_number = 0; + abuf->initialisation_time = + time_now; // this represents when the packet was noticed to be missing + abuf->status = 1 << 0; // signifying missing + abuf->resend_time = 0; + abuf->given_timestamp = 0; + abuf->sequence_number = 0; + } + abuf = conn->audio_buffer + BUFIDX(seqno); + conn->ab_write = SUCCESSOR(seqno); + } else if (seq_diff(seqno, conn->ab_read) > 0) { // older than expected but still not too late + conn->late_packets++; + abuf = conn->audio_buffer + BUFIDX(seqno); + } else { // too late. + conn->too_late_packets++; + } + + if (abuf) { + int datalen = conn->max_frames_per_packet; + abuf->initialisation_time = time_now; + abuf->resend_time = 0; + if (audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0) { + abuf->ready = 1; + abuf->status = 0; // signifying that it was received + abuf->length = datalen; + abuf->given_timestamp = actual_timestamp; + abuf->sequence_number = seqno; + } else { + debug(1, "Bad audio packet detected and discarded."); + abuf->ready = 0; + abuf->status = 1 << 1; // bad packet, discarded + abuf->resend_request_number = 0; + abuf->given_timestamp = 0; + abuf->sequence_number = 0; + } + } + + int rc = pthread_cond_signal(&conn->flowcontrol); + if (rc) + debug(1, "Error signalling flowcontrol."); + + // resend checks + { + uint64_t minimum_wait_time = + (uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000); + uint64_t resend_repeat_interval = + (uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000); + uint64_t minimum_remaining_time = (uint64_t)( + (config.resend_control_last_check_time + config.audio_backend_buffer_desired_length) * + (uint64_t)1000000000); + uint64_t latency_time = (uint64_t)(conn->latency * (uint64_t)1000000000); + latency_time = latency_time / (uint64_t)conn->input_rate; + + int x; // this is the first frame to be checked + // if we detected a first empty frame before and if it's still in the buffer! + if ((first_possibly_missing_frame >= 0) && + (position_in_modulo_uint16_t_buffer(first_possibly_missing_frame, conn->ab_read, + conn->ab_write, NULL))) { + x = first_possibly_missing_frame; + } else { + x = conn->ab_read; + } + + first_possibly_missing_frame = -1; // has not been set + + int missing_frame_run_count = 0; + int start_of_missing_frame_run = -1; + int number_of_missing_frames = 0; + while (x != conn->ab_write) { + abuf_t *check_buf = conn->audio_buffer + BUFIDX(x); + if (!check_buf->ready) { + if (first_possibly_missing_frame < 0) + first_possibly_missing_frame = x; + number_of_missing_frames++; + // debug(1, "frame %u's initialisation_time is 0x%" PRIx64 ", latency_time is 0x%" + // PRIx64 ", time_now is 0x%" PRIx64 ", minimum_remaining_time is 0x%" PRIx64 ".", x, + // check_buf->initialisation_time, latency_time, time_now, minimum_remaining_time); + int too_late = ((check_buf->initialisation_time < (time_now - latency_time)) || + ((check_buf->initialisation_time - (time_now - latency_time)) < + minimum_remaining_time)); + int too_early = ((time_now - check_buf->initialisation_time) < minimum_wait_time); + int too_soon_after_last_request = + ((check_buf->resend_time != 0) && + ((time_now - check_buf->resend_time) < + resend_repeat_interval)); // time_now can never be less than the time_tag + + if (too_late) + check_buf->status |= 1 << 2; // too late + else + check_buf->status &= 0xFF - (1 << 2); // not too late + if (too_early) + check_buf->status |= 1 << 3; // too early + else + check_buf->status &= 0xFF - (1 << 3); // not too early + if (too_soon_after_last_request) + check_buf->status |= 1 << 4; // too soon after last request + else + check_buf->status &= 0xFF - (1 << 4); // not too soon after last request + + if ((!too_soon_after_last_request) && (!too_late) && (!too_early)) { + if (start_of_missing_frame_run == -1) { + start_of_missing_frame_run = x; + missing_frame_run_count = 1; + } else { + missing_frame_run_count++; + } + check_buf->resend_time = time_now; // setting the time to now because we are + // definitely going to take action + check_buf->resend_request_number++; + debug(3, "Frame %d is missing with ab_read of %u and ab_write of %u.", x, conn->ab_read, + conn->ab_write); + } + // if (too_late) { + // debug(1,"too late to get missing frame %u.", x); + // } + } + // if (number_of_missing_frames != 0) + // debug(1,"check with x = %u, ab_read = %u, ab_write = %u, first_possibly_missing_frame + // = %d.", x, conn->ab_read, conn->ab_write, first_possibly_missing_frame); + x = (x + 1) & 0xffff; + if (((check_buf->ready) || (x == conn->ab_write)) && (missing_frame_run_count > 0)) { + // send a resend request + if (missing_frame_run_count > 1) + debug(3, "request resend of %d packets starting at seqno %u.", missing_frame_run_count, + start_of_missing_frame_run); + if (config.disable_resend_requests == 0) { + debug_mutex_unlock(&conn->ab_mutex, 3); + rtp_request_resend(start_of_missing_frame_run, missing_frame_run_count, conn); + debug_mutex_lock(&conn->ab_mutex, 20000, 1); + conn->resend_requests++; + } + start_of_missing_frame_run = -1; + missing_frame_run_count = 0; + } + } + if (number_of_missing_frames == 0) + first_possibly_missing_frame = conn->ab_write; + } } debug_mutex_unlock(&conn->ab_mutex, 0); } @@ -944,77 +939,98 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { debug_mutex_lock(&conn->flush_mutex, 1000, 0); if (conn->flush_requested == 1) { - if (conn->flush_output_flushed == 0) - if (config.output->flush) { - config.output->flush(); // no cancellation points - debug(2, "flush request: flush output device."); + if (conn->flush_output_flushed == 0) + if (config.output->flush) { + config.output->flush(); // no cancellation points + debug(2, "flush request: flush output device."); } conn->flush_output_flushed = 1; } - // now check to see it the flush request is for frames in the buffer or not - // if the first_packet_timestamp is zero, don't check - int flush_needed = 0; - int drop_request = 0; - if ((conn->flush_requested == 1) && (conn->flush_rtp_timestamp == 0)) { - debug(1, "flush request: flush frame 0 -- flush assumed to be needed."); - flush_needed = 1; - drop_request = 1; - } else if (conn->flush_rtp_timestamp != 0) { - if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) { - abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read); - abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1); - if ((firstPacket != NULL) && (firstPacket->ready)) { - // discard flushes more than 10 seconds into the future -- they are probably bogus - uint32_t first_frame_in_buffer = firstPacket->given_timestamp; - int32_t offset_from_first_frame = (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer); - if (offset_from_first_frame > (int)conn->input_rate * 10) { - debug(1, "flush request: sanity check -- flush frame %u is too far into the future from the first frame %u -- discarded.", conn->flush_rtp_timestamp, first_frame_in_buffer); - drop_request = 1; - } else { - if ((lastPacket != NULL) && (lastPacket->ready)) { - // we have enough information to check if the flush is needed or can be discarded - uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1; - // now we have to work out if the flush frame is in the buffer - // if it is later than the end of the buffer, flush everything and keep the request active. - // if it is in the buffer, we need to flush part of the buffer. Actually we flush the entire buffer and drop the request. - // if it is before the buffer, no flush is needed. Drop the request. - if (offset_from_first_frame > 0) { - int32_t offset_to_last_frame = (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp); - if (offset_to_last_frame >= 0) { - debug(2,"flush request: flush frame %u active -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - drop_request = 1; - flush_needed = 1; - } else { - debug(2,"flush request: flush frame %u pending -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - flush_needed = 1; - } - } else { - debug(2,"flush request: flush frame %u expired -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer); - drop_request = 1; - } - } - } - } - } else { - debug(3, "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: %u, ab_write: %u", conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write); - // leave flush request pending and don't do a buffer flush, because there isn't one - } - } - if (flush_needed) { - debug(2, "flush request: flush done."); - ab_resync(conn); // no cancellation points - conn->first_packet_timestamp = 0; - conn->first_packet_time_to_play = 0; - conn->time_since_play_started = 0; - have_sent_prefiller_silence = 0; - dac_delay = 0; - } - if (drop_request) { - debug(2, "flush request: request dropped."); - conn->flush_requested = 0; - conn->flush_rtp_timestamp = 0; - conn->flush_output_flushed = 0; - } + // now check to see it the flush request is for frames in the buffer or not + // if the first_packet_timestamp is zero, don't check + int flush_needed = 0; + int drop_request = 0; + if ((conn->flush_requested == 1) && (conn->flush_rtp_timestamp == 0)) { + debug(1, "flush request: flush frame 0 -- flush assumed to be needed."); + flush_needed = 1; + drop_request = 1; + } else if (conn->flush_rtp_timestamp != 0) { + if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) { + abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read); + abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1); + if ((firstPacket != NULL) && (firstPacket->ready)) { + // discard flushes more than 10 seconds into the future -- they are probably bogus + uint32_t first_frame_in_buffer = firstPacket->given_timestamp; + int32_t offset_from_first_frame = + (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer); + if (offset_from_first_frame > (int)conn->input_rate * 10) { + debug(1, + "flush request: sanity check -- flush frame %u is too far into the future from " + "the first frame %u -- discarded.", + conn->flush_rtp_timestamp, first_frame_in_buffer); + drop_request = 1; + } else { + if ((lastPacket != NULL) && (lastPacket->ready)) { + // we have enough information to check if the flush is needed or can be discarded + uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1; + // now we have to work out if the flush frame is in the buffer + // if it is later than the end of the buffer, flush everything and keep the request + // active. if it is in the buffer, we need to flush part of the buffer. Actually we + // flush the entire buffer and drop the request. if it is before the buffer, no flush + // is needed. Drop the request. + if (offset_from_first_frame > 0) { + int32_t offset_to_last_frame = + (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp); + if (offset_to_last_frame >= 0) { + debug(2, + "flush request: flush frame %u active -- buffer contains %u frames, from " + "%u to %u", + conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, + first_frame_in_buffer, last_frame_in_buffer); + drop_request = 1; + flush_needed = 1; + } else { + debug(2, + "flush request: flush frame %u pending -- buffer contains %u frames, from " + "%u to %u", + conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, + first_frame_in_buffer, last_frame_in_buffer); + flush_needed = 1; + } + } else { + debug(2, + "flush request: flush frame %u expired -- buffer contains %u frames, from %u " + "to %u", + conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, + first_frame_in_buffer, last_frame_in_buffer); + drop_request = 1; + } + } + } + } + } else { + debug(3, + "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: " + "%u, ab_write: %u", + conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write); + // leave flush request pending and don't do a buffer flush, because there isn't one + } + } + if (flush_needed) { + debug(2, "flush request: flush done."); + ab_resync(conn); // no cancellation points + conn->first_packet_timestamp = 0; + conn->first_packet_time_to_play = 0; + conn->time_since_play_started = 0; + have_sent_prefiller_silence = 0; + dac_delay = 0; + } + if (drop_request) { + debug(2, "flush request: request dropped."); + conn->flush_requested = 0; + conn->flush_rtp_timestamp = 0; + conn->flush_output_flushed = 0; + } debug_mutex_unlock(&conn->flush_mutex, 0); if (conn->ab_synced) { curframe = conn->audio_buffer + BUFIDX(conn->ab_read); @@ -1037,7 +1053,6 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { } } - if ((curframe) && (curframe->ready)) { notified_buffer_empty = 0; // at least one buffer now -- diagnostic only. if (conn->ab_buffering) { // if we are getting packets but not yet forwarding them to the @@ -1058,7 +1073,6 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { // Here, calculate when we should start playing. We need to know when to allow the // packets to be sent to the player. - // every second or so, we get a reference on when a particular packet should be // played. @@ -1093,8 +1107,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { &should_be_time, conn); conn->first_packet_time_to_play = should_be_time; - debug(2,"first_packet_time set for frame %u.", conn->first_packet_timestamp); - + debug(2, "first_packet_time set for frame %u.", conn->first_packet_timestamp); if (local_time_now > conn->first_packet_time_to_play) { uint64_t lateness = local_time_now - conn->first_packet_time_to_play; @@ -1105,172 +1118,180 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { } } + if (conn->first_packet_time_to_play != 0) { + // Now that we know the timing of the first packet... + if (config.output->delay) { + // and that the output device is capable of synchronization... + // We may send packets of + // silence from now until the time the first audio packet should be sent + // and then we will send the first packet, which will be followed by + // the subsequent packets. + // here, we figure out whether and what silence to send. - if (conn->first_packet_time_to_play != 0) { - // Now that we know the timing of the first packet... - if (config.output->delay) { - // and that the output device is capable of synchronization... - - // We may send packets of - // silence from now until the time the first audio packet should be sent - // and then we will send the first packet, which will be followed by - // the subsequent packets. - // here, we figure out whether and what silence to send. - - uint64_t should_be_time; - uint32_t effective_latency = conn->latency; - - switch (get_and_check_effective_latency(conn, &effective_latency, - config.audio_backend_latency_offset)) { - case -1: - if (conn->unachievable_audio_backend_latency_offset_notified == 0) { - warn("Negative latency! A latency of %d frames requested by the player, when " - "combined with an audio_backend_latency_offset of %f seconds, would make the " - "overall latency negative. The audio_backend_latency_offset setting is " - "ignored.", - conn->latency, config.audio_backend_latency_offset); - config.audio_backend_latency_offset = 0; // set it to zero - conn->unachievable_audio_backend_latency_offset_notified = 1; - }; - break; - case 1: - if (conn->unachievable_audio_backend_latency_offset_notified == 0) { - warn("An audio_backend_latency_offset of %f seconds may exceed the frame buffering " - "capacity -- the setting is ignored.", - config.audio_backend_latency_offset); - config.audio_backend_latency_offset = 0; // set it to zero; - conn->unachievable_audio_backend_latency_offset_notified = 1; - }; - break; - default: - break; - } - - // readjust first packet time to play - frame_to_local_time(conn->first_packet_timestamp + - effective_latency, // this will go modulo 2^32 - &should_be_time, conn); - - int64_t change_in_should_be_time = (int64_t)(should_be_time - conn->first_packet_time_to_play); - - if (fabs(0.000001*change_in_should_be_time) > 0.001) // the clock drift estimation might be nudging the estimate, and we can ignore this unless if's more than a microsecond - debug(2,"Change in estimated first_packet_time: %8.4f milliseconds.", 0.000001*change_in_should_be_time); - - conn->first_packet_time_to_play = should_be_time; - - if (local_time_now > conn->first_packet_time_to_play) { - uint64_t lateness = local_time_now - conn->first_packet_time_to_play; - debug(2, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness); - conn->ab_buffering = 0; - } else { - // do some calculations - int64_t lead_time = conn->first_packet_time_to_play - local_time_now; - if ((config.audio_backend_silent_lead_in_time_auto == 1) || - (lead_time <= - (int64_t)(config.audio_backend_silent_lead_in_time * (int64_t)1000000000))) { - // debug(1, "Lead time: %" PRId64 " nanoseconds.", lead_time); - int resp = 0; - dac_delay = 0; - if (have_sent_prefiller_silence != 0) - resp = config.output->delay(&dac_delay); // we know the output device must have a delay function - if (resp == 0) { - int64_t gross_frame_gap = - ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) / - 1000000000; - int64_t exact_frame_gap = gross_frame_gap - dac_delay; - int64_t frames_needed_to_maintain_desired_buffer = - (int64_t)(config.audio_backend_buffer_desired_length * config.output_rate) - - dac_delay; - // below, remember that exact_frame_gap and - // frames_needed_to_maintain_desired_buffer could both be negative - int64_t fs = frames_needed_to_maintain_desired_buffer; - - // if there isn't enough time to have the desired buffer size - if (exact_frame_gap <= frames_needed_to_maintain_desired_buffer) { - fs = conn->max_frames_per_packet * 2; - } - - // if we are very close to the end of buffering, i.e. within two frame-lengths, - // add the remaining silence needed and end buffering - if (exact_frame_gap <= conn->max_frames_per_packet * 2) { - fs = exact_frame_gap; - if (fs > first_frame_early_bias) - fs = fs - first_frame_early_bias; // deliberately make the first packet a tiny bit early so that the player may compensate for it at the last minute - conn->ab_buffering = 0; - } - void *silence; - if (fs > 0) { - silence = malloc(conn->output_bytes_per_frame * fs); - if (silence == NULL) - debug(1, "Failed to allocate %d byte silence buffer.", fs); - else { - // generate frames of silence with dither if necessary - conn->previous_random_number = - generate_zero_frames(silence, fs, config.output_format, - conn->enable_dither, conn->previous_random_number); - config.output->play(silence, fs); - // debug(1, "Sent %" PRId64 " frames of silence", fs); - free(silence); - have_sent_prefiller_silence = 1; - } - } - } else { - - if (resp == sps_extra_code_output_stalled) { - if (conn->unfixable_error_reported == 0) { - conn->unfixable_error_reported = 1; - if (config.cmd_unfixable) { - command_execute(config.cmd_unfixable, "output_device_stalled", 1); - } else { - warn("an unrecoverable error, \"output_device_stalled\", has been " - "detected.", - conn->connection_number); - } - } - } else { - debug(2, "Unexpected response to getting dac delay: %d.", resp); - } - } - } - } - } else { - // if the output device doesn't have a delay, we simply send the lead-in - int64_t lead_time = conn->first_packet_time_to_play - local_time_now; // negative if we are late - void *silence; - int64_t frame_gap = (lead_time * config.output_rate) / 1000000000; - // debug(1,"%d frames needed.",frame_gap); - while (frame_gap > 0) { - ssize_t fs = config.output_rate / 10; - if (fs > frame_gap) - fs = frame_gap; - - silence = malloc(conn->output_bytes_per_frame * fs); - if (silence == NULL) - debug(1, "Failed to allocate %d frame silence buffer.", fs); - else { - // debug(1, "No delay function -- outputting %d frames of silence.", fs); - conn->previous_random_number = - generate_zero_frames(silence, fs, config.output_format, - conn->enable_dither, conn->previous_random_number); - config.output->play(silence, fs); - free(silence); - } - frame_gap -= fs; - } - conn->ab_buffering = 0; - } - } + uint64_t should_be_time; + uint32_t effective_latency = conn->latency; + + switch (get_and_check_effective_latency(conn, &effective_latency, + config.audio_backend_latency_offset)) { + case -1: + if (conn->unachievable_audio_backend_latency_offset_notified == 0) { + warn( + "Negative latency! A latency of %d frames requested by the player, when " + "combined with an audio_backend_latency_offset of %f seconds, would make the " + "overall latency negative. The audio_backend_latency_offset setting is " + "ignored.", + conn->latency, config.audio_backend_latency_offset); + config.audio_backend_latency_offset = 0; // set it to zero + conn->unachievable_audio_backend_latency_offset_notified = 1; + }; + break; + case 1: + if (conn->unachievable_audio_backend_latency_offset_notified == 0) { + warn("An audio_backend_latency_offset of %f seconds may exceed the frame " + "buffering " + "capacity -- the setting is ignored.", + config.audio_backend_latency_offset); + config.audio_backend_latency_offset = 0; // set it to zero; + conn->unachievable_audio_backend_latency_offset_notified = 1; + }; + break; + default: + break; + } + + // readjust first packet time to play + frame_to_local_time(conn->first_packet_timestamp + + effective_latency, // this will go modulo 2^32 + &should_be_time, conn); + + int64_t change_in_should_be_time = + (int64_t)(should_be_time - conn->first_packet_time_to_play); + + if (fabs(0.000001 * change_in_should_be_time) > + 0.001) // the clock drift estimation might be nudging the estimate, and we can + // ignore this unless if's more than a microsecond + debug(2, "Change in estimated first_packet_time: %8.4f milliseconds.", + 0.000001 * change_in_should_be_time); + + conn->first_packet_time_to_play = should_be_time; + + if (local_time_now > conn->first_packet_time_to_play) { + uint64_t lateness = local_time_now - conn->first_packet_time_to_play; + debug(2, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness); + conn->ab_buffering = 0; + } else { + // do some calculations + int64_t lead_time = conn->first_packet_time_to_play - local_time_now; + if ((config.audio_backend_silent_lead_in_time_auto == 1) || + (lead_time <= + (int64_t)(config.audio_backend_silent_lead_in_time * (int64_t)1000000000))) { + // debug(1, "Lead time: %" PRId64 " nanoseconds.", lead_time); + int resp = 0; + dac_delay = 0; + if (have_sent_prefiller_silence != 0) + resp = config.output->delay( + &dac_delay); // we know the output device must have a delay function + if (resp == 0) { + int64_t gross_frame_gap = + ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) / + 1000000000; + int64_t exact_frame_gap = gross_frame_gap - dac_delay; + int64_t frames_needed_to_maintain_desired_buffer = + (int64_t)(config.audio_backend_buffer_desired_length * config.output_rate) - + dac_delay; + // below, remember that exact_frame_gap and + // frames_needed_to_maintain_desired_buffer could both be negative + int64_t fs = frames_needed_to_maintain_desired_buffer; + + // if there isn't enough time to have the desired buffer size + if (exact_frame_gap <= frames_needed_to_maintain_desired_buffer) { + fs = conn->max_frames_per_packet * 2; + } + + // if we are very close to the end of buffering, i.e. within two frame-lengths, + // add the remaining silence needed and end buffering + if (exact_frame_gap <= conn->max_frames_per_packet * 2) { + fs = exact_frame_gap; + if (fs > first_frame_early_bias) + fs = fs - first_frame_early_bias; // deliberately make the first packet a + // tiny bit early so that the player may + // compensate for it at the last minute + conn->ab_buffering = 0; + } + void *silence; + if (fs > 0) { + silence = malloc(conn->output_bytes_per_frame * fs); + if (silence == NULL) + debug(1, "Failed to allocate %d byte silence buffer.", fs); + else { + // generate frames of silence with dither if necessary + conn->previous_random_number = + generate_zero_frames(silence, fs, config.output_format, + conn->enable_dither, conn->previous_random_number); + config.output->play(silence, fs); + // debug(1, "Sent %" PRId64 " frames of silence", fs); + free(silence); + have_sent_prefiller_silence = 1; + } + } + } else { + + if (resp == sps_extra_code_output_stalled) { + if (conn->unfixable_error_reported == 0) { + conn->unfixable_error_reported = 1; + if (config.cmd_unfixable) { + command_execute(config.cmd_unfixable, "output_device_stalled", 1); + } else { + warn("an unrecoverable error, \"output_device_stalled\", has been " + "detected.", + conn->connection_number); + } + } + } else { + debug(2, "Unexpected response to getting dac delay: %d.", resp); + } + } + } + } + } else { + // if the output device doesn't have a delay, we simply send the lead-in + int64_t lead_time = + conn->first_packet_time_to_play - local_time_now; // negative if we are late + void *silence; + int64_t frame_gap = (lead_time * config.output_rate) / 1000000000; + // debug(1,"%d frames needed.",frame_gap); + while (frame_gap > 0) { + ssize_t fs = config.output_rate / 10; + if (fs > frame_gap) + fs = frame_gap; + + silence = malloc(conn->output_bytes_per_frame * fs); + if (silence == NULL) + debug(1, "Failed to allocate %d frame silence buffer.", fs); + else { + // debug(1, "No delay function -- outputting %d frames of silence.", fs); + conn->previous_random_number = + generate_zero_frames(silence, fs, config.output_format, conn->enable_dither, + conn->previous_random_number); + config.output->play(silence, fs); + free(silence); + } + frame_gap -= fs; + } + conn->ab_buffering = 0; + } + } #ifdef CONFIG_METADATA - if (conn->ab_buffering == 0) { - debug(2, "prsm"); - send_ssnc_metadata('prsm', NULL, 0, - 0); // "resume", but don't wait if the queue is locked - } -#endif + if (conn->ab_buffering == 0) { + debug(2, "prsm"); + send_ssnc_metadata('prsm', NULL, 0, + 0); // "resume", but don't wait if the queue is locked } +#endif } } + } // Here, we work out whether to release a packet or wait // We release a packet when the time is right. @@ -1665,8 +1686,8 @@ void player_thread_cleanup_handler(void *arg) { } if (conn->statistics) { - free(conn->statistics); - conn->statistics = NULL; + free(conn->statistics); + conn->statistics = NULL; } free_audio_buffers(conn); if (conn->stream.type == ast_apple_lossless) @@ -1690,8 +1711,8 @@ void *player_thread_func(void *arg) { conn->first_packet_timestamp = 0; conn->flush_requested = 0; conn->flush_output_flushed = 0; // only send a flush command to the output device once - conn->flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to - // be used as a null operand, so we'll use it like that too + conn->flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to + // be used as a null operand, so we'll use it like that too conn->fix_volume = 0x10000; if (conn->latency == 0) { @@ -1902,9 +1923,9 @@ void *player_thread_func(void *arg) { int sync_error_out_of_bounds = 0; // number of times in a row that there's been a serious sync error - conn->statistics = malloc(sizeof(stats_t)*trend_interval); + conn->statistics = malloc(sizeof(stats_t) * trend_interval); if (conn->statistics == NULL) - die("Failed to allocate a statistics buffer"); + die("Failed to allocate a statistics buffer"); conn->framesProcessedInThisEpoch = 0; conn->framesGeneratedInThisEpoch = 0; @@ -2002,7 +2023,8 @@ void *player_thread_func(void *arg) { "status 0x%X after %u resend requests.", SUCCESSOR(conn->last_seqno_read), play_number, inframe->status, inframe->resend_request_number); - conn->last_seqno_read = SUCCESSOR(conn->last_seqno_read); // manage the packet out of sequence minder + conn->last_seqno_read = + SUCCESSOR(conn->last_seqno_read); // manage the packet out of sequence minder void *silence = malloc(conn->output_bytes_per_frame * conn->max_frames_per_packet * conn->output_sample_ratio); @@ -2112,8 +2134,7 @@ void *player_thread_func(void *arg) { die("Shairport Sync only supports 16 bit input"); } - - at_least_one_frame_seen = 1; + at_least_one_frame_seen = 1; // We have a frame of data. We need to see if we want to add or remove a frame from it to // keep in sync. @@ -2166,8 +2187,7 @@ void *player_thread_func(void *arg) { } } - conn->buffer_occupancy = - seq_diff(conn->ab_write, conn->ab_read); // int32_t from int16_t + conn->buffer_occupancy = seq_diff(conn->ab_write, conn->ab_read); // int32_t from int16_t if (conn->buffer_occupancy < minimum_buffer_occupancy) minimum_buffer_occupancy = conn->buffer_occupancy; @@ -2175,9 +2195,6 @@ void *player_thread_func(void *arg) { if (conn->buffer_occupancy > maximum_buffer_occupancy) maximum_buffer_occupancy = conn->buffer_occupancy; - - - // here, we want to check (a) if we are meant to do synchronisation, // (b) if we have a delay procedure, (c) if we can get the delay. @@ -2191,7 +2208,7 @@ void *player_thread_func(void *arg) { if (resp == 0) { // no error current_delay = l_delay; if (l_delay >= 0) - current_delay = l_delay; + current_delay = l_delay; else { debug(2, "Underrun of %ld frames reported, but ignored.", l_delay); current_delay = @@ -2229,15 +2246,16 @@ void *player_thread_func(void *arg) { local_time_to_frame(local_time_now, &should_be_frame_32, conn); // int64_t should_be_frame = ((int64_t)should_be_frame_32) * conn->output_sample_ratio; - int64_t delay = int64_mod_difference(should_be_frame_32 * conn->output_sample_ratio, nt - current_delay, UINT32_MAX * conn->output_sample_ratio); + int64_t delay = + int64_mod_difference(should_be_frame_32 * conn->output_sample_ratio, + nt - current_delay, UINT32_MAX * conn->output_sample_ratio); - //int64_t delay = should_be_frame - (nt - current_delay); // all int64_t + // int64_t delay = should_be_frame - (nt - current_delay); // all int64_t // the original frame numbers are unsigned 32-bit integers that roll over modulo 2^32 - // hence these delay figures will be unsigned numbers that roll over modulo 2^32 * conn->output_sample_ratio; - // therefore, calculating the delay must be done in the light of possible rollover - - + // hence these delay figures will be unsigned numbers that roll over modulo 2^32 * + // conn->output_sample_ratio; therefore, calculating the delay must be done in the light + // of possible rollover sync_error = delay - ((int64_t)conn->latency * conn->output_sample_ratio + @@ -2245,38 +2263,51 @@ void *player_thread_func(void *arg) { config.output_rate)); // int64_t from int64_t - int32_t, so okay if (at_least_one_frame_seen_this_session == 0) { - at_least_one_frame_seen_this_session = 1; + at_least_one_frame_seen_this_session = 1; - // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", sync_error); + // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", + // sync_error); - // this is a sneaky attempt to make a final adjustment to the timing of the first packet + // this is a sneaky attempt to make a final adjustment to the timing of the first + // packet - // the very first packet generally has a first_frame_early_bias subtracted from its timing - // to make it more likely that it will be early than late, - // making it possible to compensate for it be adding a few frames of silence. + // the very first packet generally has a first_frame_early_bias subtracted from its + // timing to make it more likely that it will be early than late, making it possible + // to compensate for it be adding a few frames of silence. - // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", sync_error); + // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", + // sync_error); - // remove the bias when reporting the error to make it the true error + // remove the bias when reporting the error to make it the true error - debug(2,"first frame sync error (positive --> late): %" PRId64 " frames, %.3f mS at %d frames per second output.", sync_error+first_frame_early_bias, (1000.0*(sync_error+first_frame_early_bias))/config.output_rate, config.output_rate); - - // if the packet is early, add the frames needed to put it in sync. - if (sync_error < 0) { - size_t final_adjustment_length_sized = -sync_error; - char *final_adjustment_silence = malloc(conn->output_bytes_per_frame * final_adjustment_length_sized); + debug(2, + "first frame sync error (positive --> late): %" PRId64 + " frames, %.3f mS at %d frames per second output.", + sync_error + first_frame_early_bias, + (1000.0 * (sync_error + first_frame_early_bias)) / config.output_rate, + config.output_rate); + + // if the packet is early, add the frames needed to put it in sync. + if (sync_error < 0) { + size_t final_adjustment_length_sized = -sync_error; + char *final_adjustment_silence = + malloc(conn->output_bytes_per_frame * final_adjustment_length_sized); if (final_adjustment_silence) { - conn->previous_random_number = - generate_zero_frames(final_adjustment_silence, final_adjustment_length_sized, config.output_format, - conn->enable_dither, conn->previous_random_number); + conn->previous_random_number = generate_zero_frames( + final_adjustment_silence, final_adjustment_length_sized, config.output_format, + conn->enable_dither, conn->previous_random_number); int final_adjustment = -sync_error; final_adjustment = final_adjustment - first_frame_early_bias; - debug(2, "final sync adjustment: %" PRId64 " silent frames added with a bias of %" PRId64 " frames.", -sync_error, first_frame_early_bias); + debug(2, + "final sync adjustment: %" PRId64 + " silent frames added with a bias of %" PRId64 " frames.", + -sync_error, first_frame_early_bias); config.output->play(final_adjustment_silence, final_adjustment_length_sized); free(final_adjustment_silence); } else { - warn("Failed to allocate memory for a final_adjustment_silence buffer of %d frames for a " + warn("Failed to allocate memory for a final_adjustment_silence buffer of %d " + "frames for a " "sync error of %d frames.", final_adjustment_length_sized, sync_error); } @@ -2560,30 +2591,30 @@ void *player_thread_func(void *arg) { // if there is no delay procedure, then we should be sending the packet // to the output at the time determined by // the packet's time to play + requested latency + requested offset. -/* - // This is just for checking during development + /* + // This is just for checking during + development - uint32_t should_be_frame_32; - local_time_to_frame(local_time_now, &should_be_frame_32, conn); - // int64_t should_be_frame = ((int64_t)should_be_frame_32) * conn->output_sample_ratio; + uint32_t should_be_frame_32; + local_time_to_frame(local_time_now, &should_be_frame_32, conn); + // int64_t should_be_frame = ((int64_t)should_be_frame_32) * + conn->output_sample_ratio; - int32_t ilatency = (int32_t)((config.audio_backend_latency_offset - config.audio_backend_buffer_desired_length) * conn->input_rate) + conn->latency; - if (ilatency < 0) - debug(1,"incorrect latency %d.", ilatency); + int32_t ilatency = (int32_t)((config.audio_backend_latency_offset - + config.audio_backend_buffer_desired_length) * conn->input_rate) + conn->latency; if + (ilatency < 0) debug(1,"incorrect latency %d.", ilatency); - int32_t idelay = (int32_t)(should_be_frame_32 - inframe->given_timestamp); + int32_t idelay = (int32_t)(should_be_frame_32 - inframe->given_timestamp); - idelay = idelay - ilatency; + idelay = idelay - ilatency; - debug(2,"delay is %d input frames.", idelay); -*/ + debug(2,"delay is %d input frames.", idelay); + */ // if this is the first frame, see if it's close to when it's supposed to be // release, which will be its time plus latency and any offset_time if (at_least_one_frame_seen_this_session == 0) { - at_least_one_frame_seen_this_session = 1; - - + at_least_one_frame_seen_this_session = 1; } play_samples = @@ -2779,11 +2810,10 @@ void *player_thread_func(void *arg) { "%*.2f," /* source actual (average) frame rate */ "%*.2f," /* source clock drift */ "%*d", /* source clock drift sample count */ - 12, play_number, - 7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, - 7, conn->resend_requests, 5, minimum_buffer_occupancy, 5, - maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11, - conn->input_frame_rate, 10, + 12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7, + conn->too_late_packets, 7, conn->resend_requests, 5, + minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11, + conn->remote_frame_rate, 11, conn->input_frame_rate, 10, (conn->local_to_remote_time_gradient - 1.0) * 1000000, 6, conn->local_to_remote_time_gradient_sample_count); } @@ -2791,7 +2821,7 @@ void *player_thread_func(void *arg) { inform("No frames received in the last sampling interval."); } } - minimum_dac_queue_size = UINT64_MAX; // hack reset + minimum_dac_queue_size = UINT64_MAX; // hack reset maximum_buffer_occupancy = INT32_MIN; // can't be less than this minimum_buffer_occupancy = INT32_MAX; // can't be more than this at_least_one_frame_seen = 0; @@ -2997,17 +3027,17 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c // 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 (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); - } - send_ssnc_metadata('pvol', dv, strlen(dv), 1); + memset(dv, 0, 128); + 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); + } + send_ssnc_metadata('pvol', dv, strlen(dv), 1); #endif if (config.output->mute) @@ -3026,10 +3056,10 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c else { // 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); - snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0); - send_ssnc_metadata('pvol', dv, strlen(dv), 1); + char dv[128]; + memset(dv, 0, 128); + snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0); + send_ssnc_metadata('pvol', dv, strlen(dv), 1); } #endif @@ -3057,10 +3087,10 @@ void player_flush(uint32_t timestamp, rtsp_conn_info *conn) { debug(3, "player_flush"); do_flush(timestamp, conn); #ifdef CONFIG_METADATA - debug(2, "pfls"); - char numbuf[32]; - snprintf(numbuf, sizeof(numbuf),"%u",timestamp); - send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points + debug(2, "pfls"); + char numbuf[32]; + snprintf(numbuf, sizeof(numbuf), "%u", timestamp); + send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points #endif } diff --git a/player.h b/player.h index f6b86cd..2b0c2c2 100644 --- a/player.h +++ b/player.h @@ -25,7 +25,9 @@ #include "audio.h" #define time_ping_history_power_of_two 7 -#define time_ping_history (1 << time_ping_history_power_of_two) // 2^7 is 128. At 1 per three seconds, approximately six minutes of records +#define time_ping_history \ + (1 << time_ping_history_power_of_two) // 2^7 is 128. At 1 per three seconds, approximately six + // minutes of records typedef struct time_ping_record { uint64_t dispersion; diff --git a/rtp.c b/rtp.c index e56c545..c558645 100644 --- a/rtp.c +++ b/rtp.c @@ -46,9 +46,9 @@ #include struct Nvll { - char* name; - double value; - struct Nvll *next; + char *name; + double value; + struct Nvll *next; }; typedef struct Nvll nvll; @@ -263,8 +263,8 @@ void *rtp_control_receiver(void *arg) { obfp += 2; }; *obfp = 0; - - + + // get raw timestamp information // I think that a good way to understand these timestamps is that // (1) the rtlt below is the timestamp of the frame that should be playing at the @@ -275,19 +275,19 @@ void *rtp_control_receiver(void *arg) { // Thus, (3) the latency can be calculated by subtracting the second from the // first. // There must be more to it -- there something missing. - + // In addition, it seems that if the value of the short represented by the second // pair of bytes in the packet is 7 // then an extra time lag is expected to be added, presumably by // the AirPort Express. - + // Best guess is that this delay is 11,025 frames. - + uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency uint32_t rt = nctohl(&packet[16]); // raw timestamp - + uint32_t fl = nctohs(&packet[2]); // - + debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and %u, giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt); //debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 " @@ -527,22 +527,25 @@ void rtp_timing_receiver_cleanup_handler(void *arg) { // walk down the list of DACP / gradient pairs, if any nvll *gradients = config.gradients; if (conn->dacp_id) - while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string,gradients->name) != 0)) - gradients = gradients->next; + while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string, gradients->name) != 0)) + gradients = gradients->next; - // if gradients comes out of this non-null, it is pointing to the DACP and it's last-known gradient + // if gradients comes out of this non-null, it is pointing to the DACP and it's last-known + // gradient if (gradients) { - gradients->value = conn->local_to_remote_time_gradient; - // debug(1,"Updating a drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, gradients->name); + gradients->value = conn->local_to_remote_time_gradient; + // debug(1,"Updating a drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient + // - 1.0)*1000000, gradients->name); } else { - nvll *new_entry = (nvll*)malloc(sizeof(nvll)); - if (new_entry) { - new_entry->name = strdup((const char *)&conn->client_ip_string); - new_entry->value = conn->local_to_remote_time_gradient; - new_entry->next = config.gradients; - config.gradients = new_entry; - // debug(1,"Setting a new drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, new_entry->name); - } + nvll *new_entry = (nvll *)malloc(sizeof(nvll)); + if (new_entry) { + new_entry->name = strdup((const char *)&conn->client_ip_string); + new_entry->value = conn->local_to_remote_time_gradient; + new_entry->next = config.gradients; + config.gradients = new_entry; + // debug(1,"Setting a new drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient + // - 1.0)*1000000, new_entry->name); + } } debug(3, "Cancel Timing Requester."); @@ -574,13 +577,14 @@ void *rtp_timing_receiver(void *arg) { conn->local_to_remote_time_gradient = 1.0; // initial value. // walk down the list of DACP / gradient pairs, if any nvll *gradients = config.gradients; - while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string,gradients->name) != 0)) - gradients = gradients->next; + while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string, gradients->name) != 0)) + gradients = gradients->next; // if gradients comes out of this non-null, it is pointing to the IP and it's last-known gradient if (gradients) { - conn->local_to_remote_time_gradient = gradients->value; - // debug(1,"Using a stored drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, gradients->name); + conn->local_to_remote_time_gradient = gradients->value; + // debug(1,"Using a stored drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient + // - 1.0)*1000000, gradients->name); } // calculate diffusion factor @@ -591,13 +595,12 @@ void *rtp_timing_receiver(void *arg) { // be the nth root of diffusion_expansion_factor // where n is the number of elements in the array - const double diffusion_expansion_factor = 10; - double log_of_multiplier = log10(diffusion_expansion_factor)/time_ping_history; - double multiplier = pow(10,log_of_multiplier); + const double diffusion_expansion_factor = 10; + double log_of_multiplier = log10(diffusion_expansion_factor) / time_ping_history; + double multiplier = pow(10, log_of_multiplier); uint64_t dispersion_factor = (uint64_t)(multiplier * 100); // debug(1,"dispersion factor is %" PRIu64 ".", dispersion_factor); - // uint64_t first_local_to_remote_time_difference_time; // uint64_t l2rtd = 0; int sequence_number = 0; @@ -621,7 +624,8 @@ void *rtp_timing_receiver(void *arg) { if (packet[1] == 0xd3) { // timing reply return_time = arrival_time - conn->departure_time; - debug(3,"clock synchronisation request: return time is %8.3f milliseconds.",0.000001*return_time); + debug(3, "clock synchronisation request: return time is %8.3f milliseconds.", + 0.000001 * return_time); if (return_time < 200000000) { // must be less than 0.2 seconds // distant_receive_time = @@ -671,11 +675,11 @@ void *rtp_timing_receiver(void *arg) { // conn->time_pings[cc].dispersion * pow(2.14, // 1.0/conn->time_ping_count); if (conn->time_pings[cc].dispersion > UINT64_MAX / dispersion_factor) - debug(1,"dispersion factor is too large at %" PRIu64 "."); + debug(1, "dispersion factor is too large at %" PRIu64 "."); else - conn->time_pings[cc].dispersion = - (conn->time_pings[cc].dispersion * dispersion_factor) / - 100; // make the dispersions 'age' by this rational factor + conn->time_pings[cc].dispersion = + (conn->time_pings[cc].dispersion * dispersion_factor) / + 100; // make the dispersions 'age' by this rational factor } // these are used for doing a least squares calculation to get the drift conn->time_pings[0].local_time = arrival_time; @@ -752,7 +756,8 @@ void *rtp_timing_receiver(void *arg) { if ((conn->time_pings[cc].chosen) && (conn->time_pings[cc].sequence_number > (settling_time / 3))) { // wait for a approximate settling time - // have to scale them down so that the sum, possibly over every term in the array, doesn't overflow + // have to scale them down so that the sum, possibly over + // every term in the array, doesn't overflow y_bar += (conn->time_pings[cc].remote_time >> time_ping_history_power_of_two); x_bar += (conn->time_pings[cc].local_time >> time_ping_history_power_of_two); sample_count++; @@ -762,8 +767,6 @@ void *rtp_timing_receiver(void *arg) { y_bar = y_bar / sample_count; x_bar = x_bar / sample_count; - - int64_t xid, yid; double mtl, mbl; mtl = 0; @@ -791,19 +794,21 @@ void *rtp_timing_receiver(void *arg) { conn->local_to_remote_time_gradient = mtl / mbl; else { // conn->local_to_remote_time_gradient = 1.0; - debug(1,"mbl is zero. Drift remains at %.2f ppm.", (conn->local_to_remote_time_gradient - 1.0)*1000000); + debug(1, "mbl is zero. Drift remains at %.2f ppm.", + (conn->local_to_remote_time_gradient - 1.0) * 1000000); } - // scale the numbers back up - uint64_t ybf = y_bar << time_ping_history_power_of_two; - uint64_t xbf = x_bar << time_ping_history_power_of_two; + // scale the numbers back up + uint64_t ybf = y_bar << time_ping_history_power_of_two; + uint64_t xbf = x_bar << time_ping_history_power_of_two; - conn->local_to_remote_time_difference = - ybf - xbf; // make this the new local-to-remote-time-difference - conn->local_to_remote_time_difference_measurement_time = xbf; + conn->local_to_remote_time_difference = + ybf - xbf; // make this the new local-to-remote-time-difference + conn->local_to_remote_time_difference_measurement_time = xbf; } else { - debug(3,"not enough samples to estimate drift -- remaining at %.2f ppm.", (conn->local_to_remote_time_gradient - 1.0)*1000000); + debug(3, "not enough samples to estimate drift -- remaining at %.2f ppm.", + (conn->local_to_remote_time_gradient - 1.0) * 1000000); // conn->local_to_remote_time_gradient = 1.0; } // debug(1,"local to remote time gradient is %12.2f ppm, based on %d diff --git a/rtsp.c b/rtsp.c index 1f1cb9f..af9e970 100644 --- a/rtsp.c +++ b/rtsp.c @@ -115,7 +115,7 @@ typedef struct { pthread_cond_t pc_queue_item_added_signal; pthread_cond_t pc_queue_item_removed_signal; char *name; - size_t item_size; // number of bytes in each item + size_t item_size; // number of bytes in each item uint32_t count; // number of items in the queue uint32_t capacity; // maximum number of items uint32_t toq; // first item to take @@ -152,11 +152,12 @@ typedef struct { rtsp_message *carrier; } metadata_package; -void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items, const char* name) { - if (name) - debug(2, "Creating metadata queue \"%s\".", name); - else - debug(1, "Creating an unnamed metadata queue."); +void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items, + const char *name) { + if (name) + debug(2, "Creating metadata queue \"%s\".", name); + else + debug(1, "Creating an unnamed metadata queue."); pthread_mutex_init(&the_queue->pc_queue_lock, NULL); pthread_cond_init(&the_queue->pc_queue_item_added_signal, NULL); pthread_cond_init(&the_queue->pc_queue_item_removed_signal, NULL); @@ -167,25 +168,25 @@ void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t the_queue->toq = 0; the_queue->eoq = 0; if (name == NULL) - the_queue->name = NULL; + the_queue->name = NULL; else - the_queue->name = strdup(name); + the_queue->name = strdup(name); } void pc_queue_delete(pc_queue *the_queue) { - if (the_queue->name) - debug(2, "Deleting metadata queue \"%s\".", the_queue->name); - else - debug(1, "Deleting an unnamed metadata queue."); - if (the_queue->name != NULL) - free(the_queue->name); - // debug(2, "destroying pc_queue_item_removed_signal"); + if (the_queue->name) + debug(2, "Deleting metadata queue \"%s\".", the_queue->name); + else + debug(1, "Deleting an unnamed metadata queue."); + if (the_queue->name != NULL) + free(the_queue->name); + // debug(2, "destroying pc_queue_item_removed_signal"); pthread_cond_destroy(&the_queue->pc_queue_item_removed_signal); - // debug(2, "destroying pc_queue_item_added_signal"); + // debug(2, "destroying pc_queue_item_added_signal"); pthread_cond_destroy(&the_queue->pc_queue_item_added_signal); - // debug(2, "destroying pc_queue_lock"); + // debug(2, "destroying pc_queue_lock"); pthread_mutex_destroy(&the_queue->pc_queue_lock); - // debug(2, "destroying signals and locks done"); + // debug(2, "destroying signals and locks done"); } int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, @@ -204,7 +205,7 @@ void pc_queue_cleanup_handler(void *arg) { } int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { - int response = 0; + int response = 0; int rc; if (the_queue) { if (block == 0) { @@ -219,34 +220,39 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { // leave this out if you want this to return if the queue is already full // irrespective of the block flag. /* - while (the_queue->count == the_queue->capacity) { - rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal, &the_queue->pc_queue_lock); - if (rc) - debug(1, "Error waiting for item to be removed"); - } - */ + while (the_queue->count == the_queue->capacity) { + rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal, + &the_queue->pc_queue_lock); if (rc) debug(1, "Error waiting for item to be removed"); + } + */ if (the_queue->count < the_queue->capacity) { - uint32_t i = the_queue->eoq; - void *p = the_queue->items + the_queue->item_size * i; - // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq; - memcpy(p, the_stuff, the_queue->item_size); - - // update the pointer - i++; - if (i == the_queue->capacity) - // fold pointer if necessary - i = 0; - the_queue->eoq = i; - the_queue->count++; - //debug(2,"metadata queue+ \"%s\" %d/%d.", the_queue->name, the_queue->count, the_queue->capacity); - if (the_queue->count == the_queue->capacity) - debug(3, "metadata queue \"%s\": is now full with %d items in it!", the_queue->name, the_queue->count); - rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal); - if (rc) - debug(1, "metadata queue \"%s\": error signalling after pc_queue_add_item", the_queue->name); + uint32_t i = the_queue->eoq; + void *p = the_queue->items + the_queue->item_size * i; + // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq; + memcpy(p, the_stuff, the_queue->item_size); + + // update the pointer + i++; + if (i == the_queue->capacity) + // fold pointer if necessary + i = 0; + the_queue->eoq = i; + the_queue->count++; + // debug(2,"metadata queue+ \"%s\" %d/%d.", the_queue->name, the_queue->count, + // the_queue->capacity); + if (the_queue->count == the_queue->capacity) + debug(3, "metadata queue \"%s\": is now full with %d items in it!", the_queue->name, + the_queue->count); + rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal); + if (rc) + debug(1, "metadata queue \"%s\": error signalling after pc_queue_add_item", + the_queue->name); } else { - response = EWOULDBLOCK; // a bit arbitrary, this. - debug(3,"metadata queue \"%s\": is already full with %d items in it. Not adding this item to the queue.", the_queue->name, the_queue->count); + response = EWOULDBLOCK; // a bit arbitrary, this. + debug(3, + "metadata queue \"%s\": is already full with %d items in it. Not adding this item to " + "the queue.", + the_queue->name, the_queue->count); } pthread_cleanup_pop(1); // unlock the queue lock. } else { @@ -279,7 +285,8 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { i = 0; the_queue->toq = i; the_queue->count--; - debug(3,"metadata queue- \"%s\" %d/%d.", the_queue->name, the_queue->count, the_queue->capacity); + debug(3, "metadata queue- \"%s\" %d/%d.", the_queue->name, the_queue->count, + the_queue->capacity); rc = pthread_cond_signal(&the_queue->pc_queue_item_removed_signal); if (rc) debug(1, "metadata queue \"%s\": error signalling after pc_queue_get_item", the_queue->name); @@ -317,15 +324,17 @@ void *player_watchdog_thread_code(void *arg) { uint64_t last_watchdog_bark_time = conn->watchdog_bark_time; debug_mutex_unlock(&conn->watchdog_mutex, 0); if (last_watchdog_bark_time != 0) { - uint64_t time_since_last_bark = (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000; + uint64_t time_since_last_bark = + (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000; uint64_t ct = config.timeout; // go from int to 64-bit int if (time_since_last_bark >= ct) { conn->watchdog_barks++; if (conn->watchdog_barks == 1) { // debuglev = 3; // tell us everything. - debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone " - "of the heart\".", + debug(1, + "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone " + "of the heart\".", conn->connection_number); conn->stop = 1; pthread_cancel(conn->thread); @@ -446,7 +455,8 @@ void msg_retain(rtsp_message *msg) { debug(1, "Error %d locking reference counter lock"); if (msg > (rtsp_message *)0x00010000) { msg->referenceCount++; - debug(3,"msg_free increment reference counter message %d to %d.", msg->index_number, msg->referenceCount); + debug(3, "msg_free increment reference counter message %d to %d.", msg->index_number, + msg->referenceCount); // debug(1,"msg_retain -- item %d reference count %d.", msg->index_number, msg->referenceCount); rc = pthread_mutex_unlock(&reference_counter_lock); if (rc) @@ -462,7 +472,7 @@ rtsp_message *msg_init(void) { memset(msg, 0, sizeof(rtsp_message)); msg->referenceCount = 1; // from now on, any access to this must be protected with the lock msg->index_number = msg_indexes++; - debug(3,"msg_init message %d", msg->index_number); + debug(3, "msg_init message %d", msg->index_number); } else { die("msg_init -- can not allocate memory for rtsp_message %d.", msg_indexes); } @@ -526,8 +536,9 @@ void msg_free(rtsp_message **msgh) { if (*msgh > (rtsp_message *)0x00010000) { rtsp_message *msg = *msgh; msg->referenceCount--; - if (msg->referenceCount) - debug(3,"msg_free decrement reference counter message %d to %d", msg->index_number, msg->referenceCount); + if (msg->referenceCount) + debug(3, "msg_free decrement reference counter message %d to %d", msg->index_number, + msg->referenceCount); if (msg->referenceCount == 0) { unsigned int i; for (i = 0; i < msg->nheaders; i++) { @@ -542,7 +553,7 @@ void msg_free(rtsp_message **msgh) { index = 0x10000; // ensure it doesn't fold to zero. *msgh = (rtsp_message *)(index); // put a version of the index number of the freed message in here - debug(3,"msg_free freed message %d", msg->index_number); + debug(3, "msg_free freed message %d", msg->index_number); free(msg); } else { // debug(1,"msg_free item %d -- decrement reference to @@ -607,7 +618,7 @@ int msg_handle_line(rtsp_message **pmsg, char *line) { } fail: - debug(3,"msg_handle_line fail"); + debug(3, "msg_handle_line fail"); msg_free(pmsg); *pmsg = NULL; return 0; @@ -650,7 +661,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes if (errno == EINTR) continue; if (errno == EAGAIN) { - debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", conn->connection_number); + debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", + conn->connection_number); continue; } if (errno != ECONNRESET) { @@ -663,15 +675,15 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes goto shutdown; } -/* // this outputs the message received - { - void *pt = malloc(nread+1); - memset(pt, 0, nread+1); - memcpy(pt, buf + inbuf, nread); - debug(1, "Incoming string on port: \"%s\"",pt); - free(pt); - } -*/ + /* // this outputs the message received + { + void *pt = malloc(nread+1); + memset(pt, 0, nread+1); + memcpy(pt, buf + inbuf, nread); + debug(1, "Incoming string on port: \"%s\"",pt); + free(pt); + } + */ inbuf += nread; @@ -680,7 +692,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes msg_size = msg_handle_line(the_packet, buf); if (!(*the_packet)) { - debug(1,"Connection %d: rtsp_read_request can't find an RTSP header.", conn->connection_number); + debug(1, "Connection %d: rtsp_read_request can't find an RTSP header.", + conn->connection_number); reply = rtsp_read_request_response_bad_packet; goto shutdown; } @@ -887,9 +900,10 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message * rtsp_message *resp) { debug(3, "Connection %d: OPTIONS", conn->connection_number); resp->respcode = 200; - msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, " - "PAUSE, FLUSH, TEARDOWN, " - "OPTIONS, GET_PARAMETER, SET_PARAMETER"); + msg_add_header(resp, "Public", + "ANNOUNCE, SETUP, RECORD, " + "PAUSE, FLUSH, TEARDOWN, " + "OPTIONS, GET_PARAMETER, SET_PARAMETER"); } void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req, @@ -929,17 +943,17 @@ void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1 } } -// debug(1,"RTSP Flush Requested: %u.",rtptime); + // debug(1,"RTSP Flush Requested: %u.",rtptime); -// the following is now done better by the player_flush routine as a 'pfls' -/* -#ifdef CONFIG_METADATA - if (p) - send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1); - else - send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0); -#endif -*/ + // the following is now done better by the player_flush routine as a 'pfls' + /* + #ifdef CONFIG_METADATA + if (p) + send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1); + else + send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0); + #endif + */ player_flush(rtptime, conn); // will not crash even it there is no player thread. resp->respcode = 200; @@ -1035,8 +1049,9 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { msg_add_header(resp, "Session", "1"); resp->respcode = 200; // it all worked out okay - debug(1, "Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: " - "%d, Timing: %d and Audio: %d.", + debug(1, + "Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: " + "%d, Timing: %d and Audio: %d.", conn->connection_number, conn->dacp_id, &conn->client_ip_string, &conn->self_ip_string, conn->local_control_port, conn->local_timing_port, conn->local_audio_port); @@ -1265,8 +1280,6 @@ char *base64_encode_so(const unsigned char *data, size_t input_length, char *enc static int fd = -1; // static int dirty = 0; - - pc_queue metadata_queue; #define metadata_queue_size 500 metadata_package metadata_queue_items[metadata_queue_size]; @@ -1294,7 +1307,6 @@ pc_queue metadata_multicast_queue; metadata_package metadata_multicast_queue_items[metadata_queue_size]; pthread_t metadata_multicast_thread; - void metadata_create_multicast_socket(void) { if (config.metadata_enabled == 0) return; @@ -1332,7 +1344,6 @@ void metadata_delete_multicast_socket(void) { free(metadata_sockmsg); } - void metadata_open(void) { if (config.metadata_enabled == 0) return; @@ -1354,7 +1365,8 @@ static void metadata_close(void) { } void metadata_multicast_process(uint32_t type, uint32_t code, char *data, uint32_t length) { - // debug(1, "Process multicast metadata with type %x, code %x and length %u.", type, code, length); + // debug(1, "Process multicast metadata with type %x, code %x and length %u.", type, code, + // length); if (metadata_sock >= 0 && length < config.metadata_sockmsglength - 8) { char *ptr = metadata_sockmsg; uint32_t v; @@ -1455,7 +1467,7 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) debug(1, "Error encoding base64 data."); // debug(1,"Remaining count: %d ret: %d, outbuf_size: // %d.",remaining_count,ret,outbuf_size); - //ret = non_blocking_write(fd, outbuf, outbuf_size); + // ret = non_blocking_write(fd, outbuf, outbuf_size); ret = write(fd, outbuf, outbuf_size); if (ret < 0) { // debug(1,"metadata_process error %d exit 3",ret); @@ -1473,7 +1485,7 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) } } snprintf(thestring, 1024, "\n"); - //ret = non_blocking_write(fd, thestring, strlen(thestring)); + // ret = non_blocking_write(fd, thestring, strlen(thestring)); ret = write(fd, thestring, strlen(thestring)); if (ret < 0) { // debug(1,"metadata_process error %d exit 5",ret); @@ -1508,11 +1520,12 @@ void *metadata_thread_function(__attribute__((unused)) void *ignore) { pc_queue_get_item(&metadata_queue, &pack); pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack); if (config.metadata_enabled) { - if (pack.carrier) { - debug(3, " pipe: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number); - } else { - debug(3, " pipe: type %x, code %x, length %u.", pack.type, pack.code, pack.length); - } + if (pack.carrier) { + debug(3, " pipe: type %x, code %x, length %u, message %d.", pack.type, pack.code, + pack.length, pack.carrier->index_number); + } else { + debug(3, " pipe: type %x, code %x, length %u.", pack.type, pack.code, pack.length); + } metadata_process(pack.type, pack.code, pack.data, pack.length); debug(3, " pipe: done."); } @@ -1530,8 +1543,8 @@ void metadata_multicast_thread_cleanup_function(__attribute__((unused)) void *ar void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) { // create a pc_queue for passing information to a threaded metadata handler - pc_queue_init(&metadata_multicast_queue, (char *)&metadata_multicast_queue_items, sizeof(metadata_package), - metadata_multicast_queue_size, "multicast"); + pc_queue_init(&metadata_multicast_queue, (char *)&metadata_multicast_queue_items, + sizeof(metadata_package), metadata_multicast_queue_size, "multicast"); metadata_create_multicast_socket(); metadata_package pack; pthread_cleanup_push(metadata_multicast_thread_cleanup_function, NULL); @@ -1539,13 +1552,20 @@ void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) { pc_queue_get_item(&metadata_multicast_queue, &pack); pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack); if (config.metadata_enabled) { - if (pack.carrier) { - debug(3, " multicast: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number); - } else { - debug(3, " multicast: type %x, code %x, length %u.", pack.type, pack.code, pack.length); - } + if (pack.carrier) { + debug(3, + " multicast: type " + "%x, code %x, length %u, message %d.", + pack.type, pack.code, pack.length, pack.carrier->index_number); + } else { + debug(3, + " multicast: type " + "%x, code %x, length %u.", + pack.type, pack.code, pack.length); + } metadata_multicast_process(pack.type, pack.code, pack.data, pack.length); - debug(3, " multicast: done."); + debug(3, + " multicast: done."); } pthread_cleanup_pop(1); } @@ -1553,10 +1573,8 @@ void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) { pthread_exit(NULL); } - #ifdef CONFIG_METADATA_HUB -void metadata_hub_close(void) { -} +void metadata_hub_close(void) {} void metadata_hub_thread_cleanup_function(__attribute__((unused)) void *arg) { // debug(2, "metadata_hub_thread_cleanup_function called"); @@ -1582,14 +1600,14 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { // we check that it's not a "real" error. From the "man 2 open" page: // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - } + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } free(path); // create a pc_queue for passing information to a threaded metadata handler @@ -1600,13 +1618,15 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { while (1) { pc_queue_get_item(&metadata_hub_queue, &pack); pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack); - if (pack.carrier) { - debug(3, " hub: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number); - } else { - debug(3, " hub: type %x, code %x, length %u.", pack.type, pack.code, pack.length); - } - metadata_hub_process_metadata(pack.type, pack.code, pack.data, pack.length); - debug(3, " hub: done."); + if (pack.carrier) { + debug(3, " hub: type %x, code %x, length %u, message %d.", pack.type, + pack.code, pack.length, pack.carrier->index_number); + } else { + debug(3, " hub: type %x, code %x, length %u.", pack.type, pack.code, + pack.length); + } + metadata_hub_process_metadata(pack.type, pack.code, pack.data, pack.length); + debug(3, " hub: done."); pthread_cleanup_pop(1); } pthread_cleanup_pop(1); // will never happen @@ -1615,8 +1635,7 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { #endif #ifdef CONFIG_MQTT -void metadata_mqtt_close(void) { -} +void metadata_mqtt_close(void) {} void metadata_mqtt_thread_cleanup_function(__attribute__((unused)) void *arg) { // debug(2, "metadata_mqtt_thread_cleanup_function called"); @@ -1634,15 +1653,19 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { while (1) { pc_queue_get_item(&metadata_mqtt_queue, &pack); pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack); - if (config.mqtt_enabled) { - if (pack.carrier) { - debug(3, " mqtt: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number); - } else { - debug(3, " mqtt: type %x, code %x, length %u.", pack.type, pack.code, pack.length); - } - mqtt_process_metadata(pack.type, pack.code, pack.data, pack.length); - debug(3, " mqtt: done."); - } + if (config.mqtt_enabled) { + if (pack.carrier) { + debug(3, + " mqtt: type %x, code %x, length %u, message " + "%d.", + pack.type, pack.code, pack.length, pack.carrier->index_number); + } else { + debug(3, " mqtt: type %x, code %x, length %u.", + pack.type, pack.code, pack.length); + } + mqtt_process_metadata(pack.type, pack.code, pack.data, pack.length); + debug(3, " mqtt: done."); + } pthread_cleanup_pop(1); } @@ -1700,8 +1723,8 @@ void metadata_stop(void) { } } -int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, - int block) { +int send_metadata_to_queue(pc_queue *queue, uint32_t type, uint32_t code, char *data, + uint32_t length, rtsp_message *carrier, int block) { // parameters: type, code, pointer to data or NULL, length of data or NULL, // the rtsp_message or @@ -1737,20 +1760,27 @@ int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char * if (pack.carrier) { msg_retain(pack.carrier); } else { - if (data) - pack.data = memdup(data,length); // only if it's not a null + if (data) + pack.data = memdup(data, length); // only if it's not a null } int rc = pc_queue_add_item(queue, &pack, block); if (rc != 0) { if (pack.carrier) { - if (rc == EWOULDBLOCK) - debug(2, "metadata queue \"%s\" full, dropping message item: type %x, code %x, data %x, length %u, message %d.", queue->name, pack.type, pack.code, pack.data, pack.length, pack.carrier->index_number); + if (rc == EWOULDBLOCK) + debug(2, + "metadata queue \"%s\" full, dropping message item: type %x, code %x, data %x, " + "length %u, message %d.", + queue->name, pack.type, pack.code, pack.data, pack.length, + pack.carrier->index_number); msg_free(&pack.carrier); } else { - if (rc == EWOULDBLOCK) - debug(2, "metadata queue \"%s\" full, dropping data item: type %x, code %x, data %x, length %u.", queue->name, pack.type, pack.code, pack.data, pack.length); - if (pack.data) - free(pack.data); + if (rc == EWOULDBLOCK) + debug( + 2, + "metadata queue \"%s\" full, dropping data item: type %x, code %x, data %x, length %u.", + queue->name, pack.type, pack.code, pack.data, pack.length); + if (pack.data) + free(pack.data); } } return rc; @@ -1758,23 +1788,20 @@ int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char * int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, int block) { - int rc; - rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + int rc; + rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); #ifdef CONFIG_METADATA_HUB - rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block); + rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block); #endif #ifdef CONFIG_MQTT - rc = send_metadata_to_queue(&metadata_mqtt_queue, type, code, data, length, carrier, block); + rc = send_metadata_to_queue(&metadata_mqtt_queue, type, code, data, length, carrier, block); #endif - return rc; + return rc; } - - - static void handle_set_parameter_metadata(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *req, __attribute__((unused)) rtsp_message *resp) { @@ -1838,7 +1865,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m char *ct = msg_get_header(req, "Content-Type"); if (ct) { -// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); + // debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); #ifdef CONFIG_METADATA // It seems that the rtptime of the message is used as a kind of an ID that @@ -2136,7 +2163,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag unsigned int i = 0; unsigned int max_param = sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); - char* found; + char *found; while ((found = strsep(&pfmtp, " \t")) != NULL && i < max_param) { conn->stream.fmtp[i++] = atoi(found); } @@ -2617,8 +2644,9 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (strcmp(req->method, "OPTIONS") != 0) // the options message is very common, so don't log it until level 3 debug_level = 2; - debug(debug_level, "Connection %d: Received an RTSP Packet of type \"%s\":", - conn->connection_number, req->method), + debug(debug_level, + "Connection %d: Received an RTSP Packet of type \"%s\":", conn->connection_number, + req->method), debug_print_msg_headers(debug_level, req); apple_challenge(conn->fd, req, resp); @@ -2667,8 +2695,9 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (conn->stop == 0) { int err = msg_write_response(conn->fd, resp); if (err) { - debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the " - "connection.", + debug(1, + "Connection %d: Unable to write an RTSP message response. Terminating the " + "connection.", conn->connection_number); struct linger so_linger; so_linger.l_onoff = 1; // "true" @@ -2721,10 +2750,11 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (reply == -1) { char errorstring[1024]; strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, (char *)errorstring); + debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, + (char *)errorstring); } else if (reply != (ssize_t)strlen(response_text)) { - debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", strlen(response_text), - reply); + debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", + strlen(response_text), reply); } } else { debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.", @@ -2937,7 +2967,7 @@ void rtsp_listen_loop(void) { debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.", conn->connection_number, remote_ip4, rport, ip4, tport); } - #ifdef AF_INET6 +#ifdef AF_INET6 if (local_info->SAFAMILY == AF_INET6) { // IPv6: @@ -2954,7 +2984,7 @@ void rtsp_listen_loop(void) { debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.", conn->connection_number, remote_ip6, rport, ip6, tport); } - #endif +#endif } else { debug(1, "Error figuring out Shairport Sync's own IP number."); diff --git a/shairport.c b/shairport.c index 975b71f..4ee45bc 100644 --- a/shairport.c +++ b/shairport.c @@ -61,6 +61,7 @@ #endif #include "activity_monitor.h" +#include "audio.h" #include "common.h" #include "rtp.h" #include "rtsp.h" @@ -117,6 +118,8 @@ int daemonisewithout = 0; char configuration_file_path[4096 + 1]; char actual_configuration_file_path[4096 + 1]; +char first_backend_name[256]; + void print_version(void) { char *version_string = get_version_string(); if (version_string) { @@ -126,7 +129,6 @@ void print_version(void) { debug(1, "Can't print version string!"); } } - #ifdef CONFIG_SOXR pthread_t soxr_time_check_thread; void *soxr_time_check(__attribute__((unused)) void *arg) { @@ -419,15 +421,15 @@ int parse_options(int argc, char **argv) { // for unexpected circumstances #ifdef CONFIG_METADATA - /* Get the metadata setting. */ - config.metadata_enabled = 1; // if metadata support is included, then enable it by default - config.get_coverart = 1; // if metadata support is included, then enable it by default + /* Get the metadata setting. */ + config.metadata_enabled = 1; // if metadata support is included, then enable it by default + config.get_coverart = 1; // if metadata support is included, then enable it by default #endif #ifdef CONFIG_CONVOLUTION - config.convolution_max_length = 8192; + config.convolution_max_length = 8192; #endif - config.loudness_reference_volume_db = -20; + config.loudness_reference_volume_db = -20; #ifdef CONFIG_METADATA_HUB config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart"; @@ -682,9 +684,9 @@ int parse_options(int argc, char **argv) { } else if (strcasecmp(str, "stderr") == 0) { log_to_stderr(); } else { - config.log_file_path = (char *)str; - config.log_fd = -1; - log_to_file(); + config.log_file_path = (char *)str; + config.log_fd = -1; + log_to_file(); } } /* Get the ignore_volume_control setting. */ @@ -1313,113 +1315,114 @@ const char *pid_file_proc(void) { void exit_function() { - if (emergency_exit == 0) { - // the following is to ensure that if libdaemon has been included - // that most of this code will be skipped when the parent process is exiting - // exec + if (emergency_exit == 0) { + // the following is to ensure that if libdaemon has been included + // that most of this code will be skipped when the parent process is exiting + // exec #ifdef CONFIG_LIBDAEMON - if ((this_is_the_daemon_process) || (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not actually deamonised at all -#endif - debug(2, "exit function called..."); - /* - Actually, there is no terminate_mqtt() function. - #ifdef CONFIG_MQTT - if (config.mqtt_enabled) { - terminate_mqtt(); - } - #endif - */ + if ((this_is_the_daemon_process) || + (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not + // actually deamonised at all +#endif + debug(2, "exit function called..."); + /* + Actually, there is no terminate_mqtt() function. + #ifdef CONFIG_MQTT + if (config.mqtt_enabled) { + terminate_mqtt(); + } + #endif + */ #if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE) - /* - Actually, there is no stop_mpris_service() function. - #ifdef CONFIG_MPRIS_INTERFACE - stop_mpris_service(); - #endif - */ + /* + Actually, there is no stop_mpris_service() function. + #ifdef CONFIG_MPRIS_INTERFACE + stop_mpris_service(); + #endif + */ #ifdef CONFIG_DBUS_INTERFACE - stop_dbus_service(); + stop_dbus_service(); #endif - if (g_main_loop) { - debug(2, "Stopping DBUS Loop Thread"); - g_main_loop_quit(g_main_loop); - pthread_join(dbus_thread, NULL); - } + if (g_main_loop) { + debug(2, "Stopping DBUS Loop Thread"); + g_main_loop_quit(g_main_loop); + pthread_join(dbus_thread, NULL); + } #endif #ifdef CONFIG_DACP_CLIENT - debug(2, "Stopping DACP Monitor"); - dacp_monitor_stop(); + debug(2, "Stopping DACP Monitor"); + dacp_monitor_stop(); #endif #ifdef CONFIG_METADATA_HUB - debug(2, "Stopping metadata hub"); - metadata_hub_stop(); + debug(2, "Stopping metadata hub"); + metadata_hub_stop(); #endif #ifdef CONFIG_METADATA - metadata_stop(); // close down the metadata pipe + metadata_stop(); // close down the metadata pipe #endif - activity_monitor_stop(0); + activity_monitor_stop(0); - if ((config.output) && (config.output->deinit)) { - debug(2, "Deinitialise the audio backend."); - config.output->deinit(); - } + if ((config.output) && (config.output->deinit)) { + debug(2, "Deinitialise the audio backend."); + config.output->deinit(); + } #ifdef CONFIG_SOXR - // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down - pthread_join(soxr_time_check_thread, NULL); + // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down + pthread_join(soxr_time_check_thread, NULL); #endif - if (conns) - free(conns); // make sure the connections have been deleted first + if (conns) + free(conns); // make sure the connections have been deleted first - if (config.service_name) - free(config.service_name); + if (config.service_name) + free(config.service_name); #ifdef CONFIG_CONVOLUTION - if (config.convolution_ir_file) - free(config.convolution_ir_file); + if (config.convolution_ir_file) + free(config.convolution_ir_file); #endif - if (config.regtype) - free(config.regtype); + if (config.regtype) + free(config.regtype); #ifdef CONFIG_LIBDAEMON - if (this_is_the_daemon_process) { - daemon_retval_send(0); - daemon_pid_file_remove(); - daemon_signal_done(); - if (config.computed_piddir) - free(config.computed_piddir); - } - } + if (this_is_the_daemon_process) { + daemon_retval_send(0); + daemon_pid_file_remove(); + daemon_signal_done(); + if (config.computed_piddir) + free(config.computed_piddir); + } + } #endif - if (config.cfg) - config_destroy(config.cfg); - if (config.appName) - free(config.appName); - // probably should be freeing malloc'ed memory here, including strdup-created strings... - + if (config.cfg) + config_destroy(config.cfg); + if (config.appName) + free(config.appName); + // probably should be freeing malloc'ed memory here, including strdup-created strings... #ifdef CONFIG_LIBDAEMON - if (this_is_the_daemon_process) { // this is the daemon that is exiting - debug(1,"libdaemon daemon exit"); - } else { - if (config.daemonise) - debug(1,"libdaemon parent exit"); - else - debug(1,"exit"); - } + if (this_is_the_daemon_process) { // this is the daemon that is exiting + debug(1, "libdaemon daemon exit"); + } else { + if (config.daemonise) + debug(1, "libdaemon parent exit"); + else + debug(1, "exit"); + } #else - debug(1,"exit"); + debug(1, "exit"); #endif - } else { - debug(1,"emergency exit"); - } + } else { + debug(1, "emergency exit"); + } } // for removing zombie script processes @@ -1434,11 +1437,10 @@ void handle_sigchld(__attribute__((unused)) int sig) { } void main_thread_cleanup_handler(__attribute__((unused)) void *arg) { - debug(2,"main thread cleanup handler called"); + debug(2, "main thread cleanup handler called"); exit(EXIT_SUCCESS); } - int main(int argc, char **argv) { /* Check if we are called with -V or --version parameter */ if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) { @@ -1455,7 +1457,7 @@ int main(int argc, char **argv) { #ifdef CONFIG_LIBDAEMON pid = getpid(); #endif - config.log_fd = -1; + config.log_fd = -1; conns = NULL; // no connections active memset((void *)&main_thread_id, 0, sizeof(main_thread_id)); memset(&config, 0, sizeof(config)); // also clears all strings, BTW @@ -1476,7 +1478,7 @@ int main(int argc, char **argv) { setlogmask(LOG_UPTO(LOG_DEBUG)); openlog(NULL, 0, LOG_DAEMON); #endif - emergency_exit = 0; // what to do or skip in the exit_function + emergency_exit = 0; // what to do or skip in the exit_function atexit(exit_function); // set defaults @@ -1504,6 +1506,15 @@ int main(int argc, char **argv) { // set non-zero / non-NULL default values here // but note that audio back ends also have a chance to set defaults + // get the first output backend in the list and make it the default + audio_output *first_backend = audio_get_output(NULL); + if (first_backend == NULL) { + die("No audio backend found! Check your build of Shairport Sync."); + } else { + strncpy(first_backend_name, first_backend->name, sizeof(first_backend_name) - 1); + config.output_name = first_backend_name; + } + strcpy(configuration_file_path, SYSCONFDIR); // strcat(configuration_file_path, "/shairport-sync"); // thinking about adding a special // shairport-sync directory @@ -1598,13 +1609,16 @@ int main(int argc, char **argv) { if (errno == ENOENT) daemon_log(LOG_WARNING, "Failed to kill %s daemon: PID file not found.", config.appName); else - daemon_log(LOG_WARNING, "Failed to kill %s daemon: \"%s\", errno %u.", config.appName, strerror(errno), errno); + daemon_log(LOG_WARNING, "Failed to kill %s daemon: \"%s\", errno %u.", config.appName, + strerror(errno), errno); } else { - // debug(1,"Successfully killed the %s daemon.", config.appName); + // debug(1,"Successfully killed the %s daemon.", config.appName); if (daemon_pid_file_remove() == 0) debug(2, "killed the %s daemon.", config.appName); else - daemon_log(LOG_WARNING, "killed the %s deamon, but cannot remove old PID file: \"%s\", errno %u.", config.appName, strerror(errno), errno); + daemon_log(LOG_WARNING, + "killed the %s deamon, but cannot remove old PID file: \"%s\", errno %u.", + config.appName, strerror(errno), errno); } return ret < 0 ? 1 : 0; #else @@ -1651,14 +1665,19 @@ int main(int argc, char **argv) { case 0: break; case 1: - daemon_log(LOG_ERR, - "the %s daemon failed to launch: could not close open file descriptors after forking.", config.appName); + daemon_log( + LOG_ERR, + "the %s daemon failed to launch: could not close open file descriptors after forking.", + config.appName); break; case 2: - daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create PID file.", config.appName); + daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create PID file.", + config.appName); break; case 3: - daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create or access PID directory.", config.appName); + daemon_log(LOG_ERR, + "the %s daemon failed to launch: could not create or access PID directory.", + config.appName); break; default: daemon_log(LOG_ERR, "the %s daemon failed to launch, error %i.", config.appName, ret); @@ -1709,8 +1728,8 @@ int main(int argc, char **argv) { #endif debug(1, "Started!"); - // stop a pipe signal from killing the program - signal(SIGPIPE, SIG_IGN); + // stop a pipe signal from killing the program + signal(SIGPIPE, SIG_IGN); // install a zombie process reaper // see: http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html -- cgit v1.2.3 From 5ee2cf1b562947c2a16a6efbce27049aaa469c62 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 3 Sep 2020 10:01:42 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 8abde68..356874e 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -2,6 +2,7 @@ Version 3.3.7d17 ==== **Bug Fix** * Include the word `jack` in the version string if support for [Jack Audio](https://jackaudio.org) is included. +* Make the first output backend in the list of backends the default, and make its name the default output_name. Clang-format everything. Thanks to [kiwi-ed](https://github.com/kiwi-ed) for bringing these issues to light. Version 3.3.7d16 ==== -- cgit v1.2.3 From b54fb78cb4bf2a705e2eee3a79a75a56c422ce10 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 3 Sep 2020 10:35:52 +0100 Subject: Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9f64310..81b6297 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], [3.3.7d17], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d18], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From d3c4107a44f3c078e8bdf0e8212a16498631ffbd Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 3 Sep 2020 10:37:20 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 356874e..4f84ce8 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,8 +1,12 @@ +Version 3.3.7d18 +==== +**Bug Fix** +* Make the first output backend in the list of backends the default, and make its name the default output_name. Clang-format everything. Thanks again to [kiwi-ed](https://github.com/kiwi-ed) for bringing this issue to light. + Version 3.3.7d17 ==== **Bug Fix** -* Include the word `jack` in the version string if support for [Jack Audio](https://jackaudio.org) is included. -* Make the first output backend in the list of backends the default, and make its name the default output_name. Clang-format everything. Thanks to [kiwi-ed](https://github.com/kiwi-ed) for bringing these issues to light. +* Include the word `jack` in the version string if support for [Jack Audio](https://jackaudio.org) is included. Thanks to [kiwi-ed](https://github.com/kiwi-ed) for bringing this issue to light. Version 3.3.7d16 ==== -- cgit v1.2.3 From 46c47c01a6cda645bc60569cf528a7940fa9e64e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 11 Sep 2020 16:54:34 +0100 Subject: Ensure the metadata pipe is created, if necessary, when the --with-metadata option is chosen without dbus or mpris --- common.c | 13 ++++++++----- configure.ac | 2 +- rtsp.c | 54 +++++++++++++++++++++++++++--------------------------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/common.c b/common.c index c1deea3..cdd4d03 100644 --- a/common.c +++ b/common.c @@ -170,15 +170,18 @@ int create_log_file(const char *path) { int flags = fcntl(fd, F_GETFL); if (flags == -1) { // strerror_r(errno, (char - //*)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d (\"%s\") - //getting flags of pipe: \"%s\".", errno, (char *)errorstring, pathname); + //*)errorstring, sizeof(errorstring)); + //debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", + // errno, + // (char *)errorstring, pathname); } else { flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); // if (flags == -1) { // strerror_r(errno, - //(char *)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d - //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, (char *)errorstring, - //pathname); + //(char *)errorstring, sizeof(errorstring)); + //debug(1, "create_log_file -- error %d + //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, + //(char *)errorstring, pathname); } } } diff --git a/configure.ac b/configure.ac index 81b6297..97fc87d 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], [3.3.7d18], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d19], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/rtsp.c b/rtsp.c index af9e970..a1eeff1 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1320,7 +1320,6 @@ void metadata_create_multicast_socket(void) { } else { int buffer_size = METADATA_SNDBUF; setsockopt(metadata_sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size)); - fcntl(fd, F_SETFL, O_NONBLOCK); bzero((char *)&metadata_sockaddr, sizeof(metadata_sockaddr)); metadata_sockaddr.sin_family = AF_INET; metadata_sockaddr.sin_addr.s_addr = inet_addr(config.metadata_sockaddr); @@ -1584,32 +1583,6 @@ void metadata_hub_thread_cleanup_function(__attribute__((unused)) void *arg) { void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { - // create the fifo, if necessary - size_t pl = strlen(config.metadata_pipename) + 1; - char *path = malloc(pl + 1); - snprintf(path, pl + 1, "%s", config.metadata_pipename); - - mode_t oldumask = umask(000); - if (mkfifo(path, 0666) && errno != EEXIST) - die("Could not create metadata pipe \"%s\".", path); - umask(oldumask); - debug(1, "metadata pipe name is \"%s\".", path); - - // try to open it - fd = try_to_open_pipe_for_writing(path); - // we check that it's not a "real" error. From the "man 2 open" page: - // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO - // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - } - free(path); - // create a pc_queue for passing information to a threaded metadata handler pc_queue_init(&metadata_hub_queue, (char *)&metadata_hub_queue_items, sizeof(metadata_package), metadata_hub_queue_size, "hub"); @@ -1675,6 +1648,33 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { #endif void metadata_init(void) { + + // create the metadata pipe, if necessary + size_t pl = strlen(config.metadata_pipename) + 1; + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); + + mode_t oldumask = umask(000); + if (mkfifo(path, 0666) && errno != EEXIST) + die("Could not create metadata pipe \"%s\".", path); + umask(oldumask); + debug(1, "metadata pipe name is \"%s\".", path); + + // try to open it + fd = try_to_open_pipe_for_writing(path); + // we check that it's not a "real" error. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO + // open for reading." Which is okay. + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } + free(path); + int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); if (ret) debug(1, "Failed to create metadata thread!"); -- cgit v1.2.3 From 485e18db1786b672c2949ee6b67ce64435e76a9e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 11 Sep 2020 17:31:55 +0100 Subject: Add a default name for the pipe backend: /tmp/shairport-sync-audio --- audio_pipe.c | 16 ++++++++++------ scripts/shairport-sync.conf | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/audio_pipe.c b/audio_pipe.c index 05c9862..5e82496 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -42,6 +42,7 @@ static int fd = -1; char *pipename = NULL; +char *default_pipe_name = "/tmp/shairport-sync-audio"; static void start(__attribute__((unused)) int sample_rate, __attribute__((unused)) int sample_format) { @@ -108,16 +109,19 @@ static int init(int argc, char **argv) { if (config_lookup_string(config.cfg, "pipe.name", &str)) { pipename = (char *)str; } - - if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) - die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); } - if ((pipename == NULL) && (argc != 1)) - die("bad or missing argument(s) to pipe"); + if (argc > 1) + die("too many command-line arguments to pipe"); if (argc == 1) - pipename = strdup(argv[0]); + pipename = argv[0]; // command line argument has priority + + if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) + die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); + + if (pipename == NULL) + pipename = default_pipe_name; // if none specified // here, create the pipe mode_t oldumask = umask(000); diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index efa3499..6444b8a 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -159,7 +159,7 @@ jack = // --with-pipe pipe = { -// name = "/path/to/pipe"; // there is no default pipe name for the output +// name = "/tmp/shairport-sync-audio"; // this is the default }; // There are no configuration file parameters for the "stdout" audio back end. No interpolation is done. -- cgit v1.2.3 From 5d3b0fad527f5009ac3cc1f4d3aa99a966140b72 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 11 Sep 2020 17:40:25 +0100 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 4f84ce8..6eca81f 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +Version 3.3.7d19 +==== +**Bug Fix** +* Ensure the metadata pipe is created, if necessary, when the `--with-metadata` configuration option is chosen without the `--with-dbus-interface`, `--with-mpris-interface` or the `--with-mqtt-client` options. Super thanks to [Philip Howard](https://github.com/Gadgetoid) for finding this and for developing a possible solution. + +**Enhancements** +* Add a default name for the `pipe` backend. If you don't specify a name for the `pipe` backend's named pipe, it will be `/tmp/shairport-sync-audio`. Note that Shairport Sync will need to have write permission to `/tmp` to create the pipe. + Version 3.3.7d18 ==== **Bug Fix** -- cgit v1.2.3 From c5b47427f26dded1587f9bc2e8591f0b8e4dfc80 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 20 Sep 2020 16:51:07 +0100 Subject: Tighten up memory allocatons in DACP.c but shorten the wait time for a response. --- dacp.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/dacp.c b/dacp.c index a25bf9e..28faece 100644 --- a/dacp.c +++ b/dacp.c @@ -77,23 +77,25 @@ void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size void *t = realloc(ptr, size); if ((t == NULL) && (size != 0)) debug(1, "Response realloc of size %d failed!", size); + if (t != ptr) + debug(3, "response_realloc pointer changed for size %d", size); return t; } void response_body(void *opaque, const char *data, int size) { struct HttpResponse *response = (struct HttpResponse *)opaque; - ssize_t space_available = response->malloced_size - response->size; + ssize_t space_available = response->malloced_size - response->size; //must be greater than or equal to 0 if (space_available < size) { - // debug(1,"Getting more space for the response -- need %d bytes but only %ld bytes left.\n", - // size, - // size - space_available); - ssize_t size_requested = size - space_available + response->malloced_size + 16384; + debug(3,"Getting more space for the response -- %d allocated, need %d bytes but only %d bytes left.\n", response->malloced_size, size, space_available); + ssize_t size_requested = size - space_available + response->malloced_size; void *t = realloc(response->body, size_requested); response->malloced_size = size_requested; - if (t) + if (t) { + if (response->body != t) + debug(3, "response_body pointer changed for size %u.", size_requested); response->body = t; - else { + } else { die("dacp: can't allocate any more space for parser."); } } @@ -226,7 +228,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { struct timeval tv; tv.tv_sec = 0; - tv.tv_usec = 500000; + tv.tv_usec = 100000; if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1) debug(1, "dacp_send_command: error %d setting receive timeout.", errno); if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1) @@ -268,8 +270,10 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { } else { - response.body = malloc(2048); // it can resize this if necessary - response.malloced_size = 2048; + response.body = malloc(0); // it will resize this if necessary + if (response.body == NULL) + die("Can not allocatr space for a DACP response."); + response.malloced_size = 0; pthread_cleanup_push(malloc_cleanup, response.body); struct http_roundtripper rt; @@ -286,6 +290,8 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { debug(1, "dacp_send_command: error %d setting receive timeout.", errno); ssize_t ndata = recv(sockfd, buffer, sizeof(buffer), 0); // debug(3, "Received %d bytes: \"%s\".", ndata, buffer); + if ((ndata >=0) && ((size_t)ndata > sizeof(buffer)/2)) + debug(1, "Received %u bytes: \"%s\".", ndata, buffer); if (ndata <= 0) { if (ndata == -1) { char errorstring[1024]; -- cgit v1.2.3 From 6857d06bd63374377f12a9f7a99868098f27897c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 27 Sep 2020 10:44:12 +0100 Subject: Handle active_remote_id as a string rather than an unsigned 32-bit number. It seems ROON uses a longer character sequence. Tidy up some error messages aboout which backend is selected. --- dacp.c | 72 ++++++++++++++++++++++++++++++++----------------------------- player.h | 3 +-- rtsp.c | 17 +++++++++------ shairport.c | 3 +-- 4 files changed, 51 insertions(+), 44 deletions(-) diff --git a/dacp.c b/dacp.c index 28faece..77313f7 100644 --- a/dacp.c +++ b/dacp.c @@ -56,7 +56,7 @@ typedef struct { int always_use_revision_number_1; // for dealing with forked-daapd; uint32_t scope_id; // if it's an ipv6 connection, this will be its scope id char ip_string[INET6_ADDRSTRLEN]; // the ip string pointing to the client - uint32_t active_remote_id; // send this when you want to send remote control commands + char *active_remote_id; // send this when you want to send remote control commands void *port_monitor_private_storage; } dacp_server_record; @@ -77,25 +77,23 @@ void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size void *t = realloc(ptr, size); if ((t == NULL) && (size != 0)) debug(1, "Response realloc of size %d failed!", size); - if (t != ptr) - debug(3, "response_realloc pointer changed for size %d", size); return t; } void response_body(void *opaque, const char *data, int size) { struct HttpResponse *response = (struct HttpResponse *)opaque; - ssize_t space_available = response->malloced_size - response->size; //must be greater than or equal to 0 + ssize_t space_available = response->malloced_size - response->size; if (space_available < size) { - debug(3,"Getting more space for the response -- %d allocated, need %d bytes but only %d bytes left.\n", response->malloced_size, size, space_available); - ssize_t size_requested = size - space_available + response->malloced_size; + // debug(1,"Getting more space for the response -- need %d bytes but only %ld bytes left.\n", + // size, + // size - space_available); + ssize_t size_requested = size - space_available + response->malloced_size + 16384; void *t = realloc(response->body, size_requested); response->malloced_size = size_requested; - if (t) { - if (response->body != t) - debug(3, "response_body pointer changed for size %u.", size_requested); + if (t) response->body = t; - } else { + else { die("dacp: can't allocate any more space for parser."); } } @@ -228,7 +226,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { struct timeval tv; tv.tv_sec = 0; - tv.tv_usec = 100000; + tv.tv_usec = 500000; if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1) debug(1, "dacp_send_command: error %d setting receive timeout.", errno); if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1) @@ -246,7 +244,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // debug(1,"DACP connect succeeded."); snprintf(message, sizeof(message), - "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %u\r\n\r\n", + "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %s\r\n\r\n", command, dacp_server.ip_string, dacp_server.port, dacp_server.active_remote_id); @@ -270,10 +268,8 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { } else { - response.body = malloc(0); // it will resize this if necessary - if (response.body == NULL) - die("Can not allocatr space for a DACP response."); - response.malloced_size = 0; + response.body = malloc(2048); // it can resize this if necessary + response.malloced_size = 2048; pthread_cleanup_push(malloc_cleanup, response.body); struct http_roundtripper rt; @@ -290,8 +286,6 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { debug(1, "dacp_send_command: error %d setting receive timeout.", errno); ssize_t ndata = recv(sockfd, buffer, sizeof(buffer), 0); // debug(3, "Received %d bytes: \"%s\".", ndata, buffer); - if ((ndata >=0) && ((size_t)ndata > sizeof(buffer)/2)) - debug(1, "Received %u bytes: \"%s\".", ndata, buffer); if (ndata <= 0) { if (ndata == -1) { char errorstring[1024]; @@ -449,9 +443,11 @@ void set_dacp_server_information(rtsp_conn_info *conn) { // metadata_hub_modify_epilog(ch); } } - dacp_server.active_remote_id = conn->dacp_active_remote; // even if the dacp_id remains the same, - // the active remote will change. - debug(3, "set_dacp_server_information set active-remote id to %" PRIu32 ".", + if (dacp_server.active_remote_id) + free(dacp_server.active_remote_id); + dacp_server.active_remote_id = strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, + // the active remote will change. + debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); @@ -463,20 +459,24 @@ void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) { "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port " "number %d.", dacp_id, dacp_server.dacp_id, port); - if (strcmp(dacp_id, dacp_server.dacp_id) == 0) { - dacp_server.port = port; - if (port == 0) - dacp_server.scan_enable = 0; - else { - dacp_server.scan_enable = 1; - // debug(2, "dacp_monitor_port_update_callback enables scan"); - } - // metadata_hub_modify_prolog(); - // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable; - // metadata_store.dacp_server_active = dacp_server.scan_enable; - // metadata_hub_modify_epilog(ch); + if ((dacp_id == NULL) || (dacp_server.dacp_id == NULL)) { + warn("dacp_id or dacp_server.dacp_id NULL detected"); } else { - debug(1, "dacp port monitor reporting on an out-of-use remote."); + if (strcmp(dacp_id, dacp_server.dacp_id) == 0) { + dacp_server.port = port; + if (port == 0) + dacp_server.scan_enable = 0; + else { + dacp_server.scan_enable = 1; + // debug(2, "dacp_monitor_port_update_callback enables scan"); + } + // metadata_hub_modify_prolog(); + // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable; + // metadata_store.dacp_server_active = dacp_server.scan_enable; + // metadata_hub_modify_epilog(ch); + } else { + debug(1, "dacp port monitor reporting on an out-of-use remote."); + } } pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); @@ -1005,6 +1005,10 @@ void dacp_monitor_stop() { pthread_mutex_destroy(&dacp_conversation_lock); pthread_cond_destroy(&dacp_server_information_cv); debug(3, "DACP Server Information Condition Variable destroyed."); + if (dacp_server.active_remote_id) { + free(dacp_server.active_remote_id); + dacp_server.active_remote_id = NULL; + } } } diff --git a/player.h b/player.h index 2b0c2c2..013dfb6 100644 --- a/player.h +++ b/player.h @@ -197,7 +197,6 @@ typedef struct { // could be one of many, so we need to know it uint32_t self_scope_id; // if it's an ipv6 connection, this will be its scope short connection_ip_family; // AF_INET / AF_INET6 - uint32_t client_active_remote; // used when you want to control the client... SOCKADDR rtp_client_control_socket; // a socket pointing to the control port of the client SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client @@ -265,7 +264,7 @@ typedef struct { char *dacp_id; // id of the client -- used to find the port to be used // uint16_t dacp_port; // port on the client to send remote control messages to, else // zero - uint32_t dacp_active_remote; // key to send to the remote controller + char *dacp_active_remote; // key to send to the remote controller void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled. int enable_dither; // needed for filling silences before play actually starts diff --git a/rtsp.c b/rtsp.c index a1eeff1..974c8a4 100644 --- a/rtsp.c +++ b/rtsp.c @@ -3,7 +3,7 @@ * Copyright (c) James Laird 2013 * Modifications associated with audio synchronization, mutithreading and - * metadata handling copyright (c) Mike Brady 2014-2019 + * metadata handling copyright (c) Mike Brady 2014-2020 * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -975,15 +975,19 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".", conn->connection_number, ar); // get the active remote - char *p; - conn->dacp_active_remote = strtoul(ar, &p, 10); + if (conn->dacp_active_remote) // this is in case SETUP was previously called + free(conn->dacp_active_remote); + conn->dacp_active_remote = strdup(ar); #ifdef CONFIG_METADATA send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1); #endif } else { debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.", conn->connection_number); - conn->dacp_active_remote = 0; + if (conn->dacp_active_remote) {// this is in case SETUP was previously called + free(conn->dacp_active_remote); + conn->dacp_active_remote = NULL; + } } ar = msg_get_header(req, "DACP-ID"); @@ -998,9 +1002,10 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { } else { debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.", conn->connection_number); - if (conn->dacp_id) // this is in case SETUP was previously called + if (conn->dacp_id) {// this is in case SETUP was previously called free(conn->dacp_id); - conn->dacp_id = NULL; + conn->dacp_id = NULL; + } } char *hdr = msg_get_header(req, "Transport"); diff --git a/shairport.c b/shairport.c index 4ee45bc..127b23f 100644 --- a/shairport.c +++ b/shairport.c @@ -1763,8 +1763,7 @@ int main(int argc, char **argv) { config.output = audio_get_output(config.output_name); if (!config.output) { - audio_ls_outputs(); - die("Invalid audio output specified!"); + die("Invalid audio backend \"%s\" selected!", config.output_name == NULL ? "" : config.output_name); } config.output->init(argc - audio_arg, argv + audio_arg); -- cgit v1.2.3 From 3fc95704899324784e654cf5edf92cd162d0ddee Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 27 Sep 2020 10:45:22 +0100 Subject: Clang Format --- audio_pipe.c | 8 ++++---- common.c | 4 ++-- dacp.c | 46 +++++++++++++++++++++++----------------------- player.h | 4 ++-- rtsp.c | 6 +++--- shairport.c | 3 ++- 6 files changed, 36 insertions(+), 35 deletions(-) diff --git a/audio_pipe.c b/audio_pipe.c index 5e82496..a7925fb 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -117,11 +117,11 @@ static int init(int argc, char **argv) { if (argc == 1) pipename = argv[0]; // command line argument has priority - if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) - die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); + if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) + die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); - if (pipename == NULL) - pipename = default_pipe_name; // if none specified + if (pipename == NULL) + pipename = default_pipe_name; // if none specified // here, create the pipe mode_t oldumask = umask(000); diff --git a/common.c b/common.c index cdd4d03..b48adad 100644 --- a/common.c +++ b/common.c @@ -171,7 +171,7 @@ int create_log_file(const char *path) { if (flags == -1) { // strerror_r(errno, (char //*)errorstring, sizeof(errorstring)); - //debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", + // debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", // errno, // (char *)errorstring, pathname); } else { @@ -179,7 +179,7 @@ int create_log_file(const char *path) { // if (flags == -1) { // strerror_r(errno, //(char *)errorstring, sizeof(errorstring)); - //debug(1, "create_log_file -- error %d + // debug(1, "create_log_file -- error %d //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, //(char *)errorstring, pathname); } diff --git a/dacp.c b/dacp.c index 77313f7..acb963b 100644 --- a/dacp.c +++ b/dacp.c @@ -444,11 +444,11 @@ void set_dacp_server_information(rtsp_conn_info *conn) { } } if (dacp_server.active_remote_id) - free(dacp_server.active_remote_id); - dacp_server.active_remote_id = strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, - // the active remote will change. - debug(3, "set_dacp_server_information set active-remote id to %s.", - dacp_server.active_remote_id); + free(dacp_server.active_remote_id); + dacp_server.active_remote_id = + strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, + // the active remote will change. + debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); } @@ -460,23 +460,23 @@ void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) { "number %d.", dacp_id, dacp_server.dacp_id, port); if ((dacp_id == NULL) || (dacp_server.dacp_id == NULL)) { - warn("dacp_id or dacp_server.dacp_id NULL detected"); + warn("dacp_id or dacp_server.dacp_id NULL detected"); } else { - if (strcmp(dacp_id, dacp_server.dacp_id) == 0) { - dacp_server.port = port; - if (port == 0) - dacp_server.scan_enable = 0; - else { - dacp_server.scan_enable = 1; - // debug(2, "dacp_monitor_port_update_callback enables scan"); - } - // metadata_hub_modify_prolog(); - // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable; - // metadata_store.dacp_server_active = dacp_server.scan_enable; - // metadata_hub_modify_epilog(ch); - } else { - debug(1, "dacp port monitor reporting on an out-of-use remote."); - } + if (strcmp(dacp_id, dacp_server.dacp_id) == 0) { + dacp_server.port = port; + if (port == 0) + dacp_server.scan_enable = 0; + else { + dacp_server.scan_enable = 1; + // debug(2, "dacp_monitor_port_update_callback enables scan"); + } + // metadata_hub_modify_prolog(); + // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable; + // metadata_store.dacp_server_active = dacp_server.scan_enable; + // metadata_hub_modify_epilog(ch); + } else { + debug(1, "dacp port monitor reporting on an out-of-use remote."); + } } pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); @@ -1006,8 +1006,8 @@ void dacp_monitor_stop() { pthread_cond_destroy(&dacp_server_information_cv); debug(3, "DACP Server Information Condition Variable destroyed."); if (dacp_server.active_remote_id) { - free(dacp_server.active_remote_id); - dacp_server.active_remote_id = NULL; + free(dacp_server.active_remote_id); + dacp_server.active_remote_id = NULL; } } } diff --git a/player.h b/player.h index 013dfb6..105ce86 100644 --- a/player.h +++ b/player.h @@ -264,8 +264,8 @@ typedef struct { char *dacp_id; // id of the client -- used to find the port to be used // uint16_t dacp_port; // port on the client to send remote control messages to, else // zero - char *dacp_active_remote; // key to send to the remote controller - void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled. + char *dacp_active_remote; // key to send to the remote controller + void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled. int enable_dither; // needed for filling silences before play actually starts uint64_t dac_buffer_queue_minimum_length; diff --git a/rtsp.c b/rtsp.c index 974c8a4..8616a61 100644 --- a/rtsp.c +++ b/rtsp.c @@ -984,7 +984,7 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { } else { debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.", conn->connection_number); - if (conn->dacp_active_remote) {// this is in case SETUP was previously called + if (conn->dacp_active_remote) { // this is in case SETUP was previously called free(conn->dacp_active_remote); conn->dacp_active_remote = NULL; } @@ -1002,9 +1002,9 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { } else { debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.", conn->connection_number); - if (conn->dacp_id) {// this is in case SETUP was previously called + if (conn->dacp_id) { // this is in case SETUP was previously called free(conn->dacp_id); - conn->dacp_id = NULL; + conn->dacp_id = NULL; } } diff --git a/shairport.c b/shairport.c index 127b23f..ba99523 100644 --- a/shairport.c +++ b/shairport.c @@ -1763,7 +1763,8 @@ int main(int argc, char **argv) { config.output = audio_get_output(config.output_name); if (!config.output) { - die("Invalid audio backend \"%s\" selected!", config.output_name == NULL ? "" : config.output_name); + die("Invalid audio backend \"%s\" selected!", + config.output_name == NULL ? "" : config.output_name); } config.output->init(argc - audio_arg, argv + audio_arg); -- cgit v1.2.3 From cf5a9f764dea01bcd86ba2800ef9cd7c1d1f8d59 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 27 Sep 2020 10:48:27 +0100 Subject: Handle active_remote_id as a string rather than an unsigned 32-bit number. It seems ROON uses a longer character sequence. Tidy up some error messages aboout which backend is selected. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 97fc87d..589de5a 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], [3.3.7d19], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.7d20], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 5146efd768a00b7d084cf1e80f3de51c584cadd3 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 5 Dec 2020 18:00:47 +0000 Subject: Bugfix -- don't try to create the metadata pipe or output metadata to it when metadata has not been enabled. --- rtsp.c | 3 ++- shairport.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rtsp.c b/rtsp.c index abfabc4..184b411 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1793,7 +1793,8 @@ int send_metadata_to_queue(pc_queue *queue, uint32_t type, uint32_t code, char * int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, int block) { int rc; - rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + if (config.metadata_enabled) + rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); #ifdef CONFIG_METADATA_HUB rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block); diff --git a/shairport.c b/shairport.c index ba99523..9cedc72 100644 --- a/shairport.c +++ b/shairport.c @@ -1362,7 +1362,8 @@ void exit_function() { #endif #ifdef CONFIG_METADATA - metadata_stop(); // close down the metadata pipe + if (config.metadata_enabled) + metadata_stop(); // close down the metadata pipe #endif activity_monitor_stop(0); @@ -1952,7 +1953,8 @@ int main(int argc, char **argv) { #endif memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr)); #ifdef CONFIG_METADATA - metadata_init(); // create the metadata pipe if necessary + if (config.metadata_enabled) + metadata_init(); // create the metadata pipe if necessary #endif #ifdef CONFIG_METADATA_HUB -- cgit v1.2.3 From fc1a32a18dbf543dc97bfa1a7a606816119e7fc8 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 5 Dec 2020 18:01:04 +0000 Subject: Bugfix -- don't try to create the metadata pipe or output metadata to it when metadata has not been enabled. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ed0dcda..d8ffc08 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], [3.3.7], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8d0], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 7b716cf41eaa18ae1d73e08e5e7d2e4202535f67 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 5 Dec 2020 18:02:03 +0000 Subject: Bugfix -- don't try to create the metadata pipe or output metadata to it when metadata has not been enabled. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d8ffc08..693b224 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], [3.3.8d0], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8rc0], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 83c9e3610dcba99d6f542c920a3143390c27afc4 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 5 Dec 2020 18:06:47 +0000 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 273b666..71cb219 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,10 @@ Please see the [Release Notes for 3.3](https://github.com/mikebrady/shairport-sync/releases/tag/3.3). +Version 3.3.8 +==== +**Bug Fix** +* Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) and [Ircama](https://github.com/Ircama) for their reports. + Version 3.3.7 ==== **Docker Integration** -- cgit v1.2.3 From b8f348d459e326997498c8895ae8d24dda620ddd Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 7 Dec 2020 14:04:22 +0000 Subject: Restore compatibility with AirPower on Android. --- dacp.c | 10 +++++-- rtsp.c | 95 +++++++++++++++++++++++++++++++++---------------------------- shairport.c | 4 +-- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/dacp.c b/dacp.c index acb963b..effbe5e 100644 --- a/dacp.c +++ b/dacp.c @@ -445,9 +445,13 @@ void set_dacp_server_information(rtsp_conn_info *conn) { } if (dacp_server.active_remote_id) free(dacp_server.active_remote_id); - dacp_server.active_remote_id = - strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, - // the active remote will change. + if (conn->dacp_active_remote) + dacp_server.active_remote_id = + strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, + // the active remote will change. + else + dacp_server.active_remote_id = NULL; + debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); diff --git a/rtsp.c b/rtsp.c index 184b411..f3524ae 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1652,41 +1652,41 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { #endif void metadata_init(void) { - - // create the metadata pipe, if necessary - size_t pl = strlen(config.metadata_pipename) + 1; - char *path = malloc(pl + 1); - snprintf(path, pl + 1, "%s", config.metadata_pipename); - - mode_t oldumask = umask(000); - if (mkfifo(path, 0666) && errno != EEXIST) - die("Could not create metadata pipe \"%s\".", path); - umask(oldumask); - debug(1, "metadata pipe name is \"%s\".", path); - - // try to open it - fd = try_to_open_pipe_for_writing(path); - // we check that it's not a "real" error. From the "man 2 open" page: - // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO - // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); + int ret; + if (config.metadata_enabled) { + // create the metadata pipe, if necessary + size_t pl = strlen(config.metadata_pipename) + 1; + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); + mode_t oldumask = umask(000); + if (mkfifo(path, 0666) && errno != EEXIST) + die("Could not create metadata pipe \"%s\".", path); + umask(oldumask); + debug(1, "metadata pipe name is \"%s\".", path); + + // try to open it + fd = try_to_open_pipe_for_writing(path); + // we check that it's not a "real" error. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO + // open for reading." Which is okay. + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } + free(path); + int ret; + ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata thread!"); + + ret = pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata multicast thread!"); } - free(path); - - int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata thread!"); - - ret = pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata multicast thread!"); - #ifdef CONFIG_METADATA_HUB ret = pthread_create(&metadata_hub_thread, NULL, metadata_hub_thread_function, NULL); if (ret) @@ -1715,15 +1715,20 @@ void metadata_stop(void) { pthread_join(metadata_hub_thread, NULL); // debug(2, "metadata stop hub done."); #endif - // debug(2, "metadata stop multicast thread."); - pthread_cancel(metadata_multicast_thread); - pthread_join(metadata_multicast_thread, NULL); - // debug(2, "metadata stop multicast done."); - - // debug(2, "metadata stop metadata_thread thread."); - pthread_cancel(metadata_thread); - pthread_join(metadata_thread, NULL); - // debug(2, "metadata_stop finished successfully."); + if (config.metadata_enabled) { + // debug(2, "metadata stop multicast thread."); + if (metadata_multicast_thread) { + pthread_cancel(metadata_multicast_thread); + pthread_join(metadata_multicast_thread, NULL); + // debug(2, "metadata stop multicast done."); + } + if (metadata_thread) { + // debug(2, "metadata stop metadata_thread thread."); + pthread_cancel(metadata_thread); + pthread_join(metadata_thread, NULL); + // debug(2, "metadata_stop finished successfully."); + } + } } } @@ -1793,8 +1798,10 @@ int send_metadata_to_queue(pc_queue *queue, uint32_t type, uint32_t code, char * int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, int block) { int rc; - if (config.metadata_enabled) + if (config.metadata_enabled) { rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + rc = send_metadata_to_queue(&metadata_multicast_queue, type, code, data, length, carrier, block); + } #ifdef CONFIG_METADATA_HUB rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block); diff --git a/shairport.c b/shairport.c index 9cedc72..db2c753 100644 --- a/shairport.c +++ b/shairport.c @@ -1362,7 +1362,6 @@ void exit_function() { #endif #ifdef CONFIG_METADATA - if (config.metadata_enabled) metadata_stop(); // close down the metadata pipe #endif @@ -1953,8 +1952,7 @@ int main(int argc, char **argv) { #endif memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr)); #ifdef CONFIG_METADATA - if (config.metadata_enabled) - metadata_init(); // create the metadata pipe if necessary + metadata_init(); // create the metadata pipe if necessary #endif #ifdef CONFIG_METADATA_HUB -- cgit v1.2.3 From b291b951d71f42d7770d8357af6499455f17f78d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 7 Dec 2020 14:04:53 +0000 Subject: Restore compatibility with AirPower on Android. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 693b224..d745ac7 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], [3.3.8rc0], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8rc1], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From dabaa32aa5824f51e3c27bb7ebafb0dce3bd633d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 7 Dec 2020 14:08:43 +0000 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 71cb219..dda9160 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -3,7 +3,8 @@ Please see the [Release Notes for 3.3](https://github.com/mikebrady/shairport-sy Version 3.3.8 ==== **Bug Fix** -* Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) and [Ircama](https://github.com/Ircama) for their reports. +* Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. +* Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. Version 3.3.7 ==== -- cgit v1.2.3 From 9729fa950b9031e2bbe1de8bdbf35a5bc68b6117 Mon Sep 17 00:00:00 2001 From: minix1234 <48591487+minix1234@users.noreply.github.com> Date: Tue, 15 Dec 2020 16:32:57 -0500 Subject: Create MQTT.md first crack at MQTT markdown informational data --- MQTT.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 MQTT.md diff --git a/MQTT.md b/MQTT.md new file mode 100644 index 0000000..7f14f6e --- /dev/null +++ b/MQTT.md @@ -0,0 +1,132 @@ +Setting Up MQTT Publishing +==== + +This is a rough guide on the setup of MQTT publishing in ShairPort-Sync. + + +The MQTT listens for and publishes metadata generated by shairport-sync + +MetaData Parsing +---- + + +Additional details regarding the metadata can be found at [https://github.com/mikebrady/shairport-sync-metadata-reader] + +Meta data is generated by both the stream source (iOS, itunes, etc) and by shairport-sync itself. This data is coded as two 4-character codes to identify each piece of data, the `type` and the `code`. + +The first 4-character code, called the `type`, is either: + * `core` for all the regular metadadata coming from iTunes, etc., or + * `ssnc` (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as start/end delimiters, etc. + +* For `core` metadata, the second 4-character code is the 4-character metadata code that comes from iTunes etc. See, for example, https://code.google.com/p/ytrack/wiki/DMAP for information about the significance of the codes. The original data supplied by the source, if any, follows, and is encoded in base64 format. The length of the data is also provided. +* For `ssnc` metadata, the second 4-character code is used to distinguish the messages. Cover art, coming from the source, is not tagged in the same way as other metadata, it seems, so is sent as an `ssnc` type metadata message with the code `PICT`. Progress information, similarly, is not tagged like other source-originated metadata, so it is sent as an `ssnc` type with the code `prgr`. + +Here are some of the `core` codes commonly passed from the source: + * `asar` -- "artist" + * `asal` -- "album" + * `minm` -- "title" + * `asgn` -- "genre" + * `asfm` -- "format" + * `asal` -- "songalbum' + * `pvol` -- "volume" + * `clip` -- "client_ip" + +Here are the 'ssnc' codes defined so far: + * `PICT` -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see which. + * `clip` -- the payload is the IP number of the client, i.e. the sender of audio. Can be an IPv4 or an IPv6 number. + * `pbeg` -- play stream begin. No arguments + * `pend` -- play stream end. No arguments + * `pfls` -- play stream flush. No arguments + * `prsm` -- play stream resume. No arguments + * `pvol` -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. The "airplay_volume" is what's sent by the source (e.g. iTunes) to the player, and is from 0.00 down to -30.00, with -144.00 meaning "mute". This is linear on the volume control slider of iTunes or iOS AirPlay. If the volume setting is being ignored by Shairport Sync itself, the volume, lowest_volume and highest_volume values are zero. + * `prgr` -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start of the current play sequence, the current play point and the end of the play sequence. + * `mdst` -- a sequence of metadata is about to start. The RTP timestamp associated with the metadata sequence is included as data, if available. + * `mden` -- a sequence of metadata has ended. The RTP timestamp associated with the metadata sequence is included as data, if available. + * `pcst` -- a picture is about to be sent. The RTP timestamp associated with it is included as data, if available. + * `pcen` -- a picture has been sent. The RTP timestamp associated with it is included as data, if available. + * `snam` -- a device e.g. "Joe's iPhone" has started a play session. Specifically, it's the "X-Apple-Client-Name" string. + * `snua` -- a "user agent" e.g. "iTunes/12..." has started a play session. Specifically, it's the "User-Agent" string. + * `stal` -- this is an error message meaning that reception of a large piece of metadata, usually a large picture, has stalled; bad things may happen. + +The next two two tokens are to facilitiate remote control of the source. There is some information at http://nto.github.io/AirPlay.html about remote control of the source. + * `daid` -- this is the source's DACP-ID (if it has one -- it's not guaranteed), useful if you want to remotely control the source. Use this string to identify the source's remote control on the network. + * `acre` -- this is the source's Active-Remote token, necessary if you want to send commands to the source's remote control (if it has one). + * `dapo` -- the payload is the port number (as text) of the source's remote control, to which commands should be sent. It is 3689 for iTunes but varies for iOS devices. + + + +Parsed Messages +---- + +The MQTT service can parse the above raw messages into a subset of human-readable topics that include, + +* `artist` -- text of artist name +* `album` -- text of album name +* `title` -- text of song title +* `genre` -- text of genre +* `format` -- ?? +* `songalbum` -- +* `volume` -- The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. (see above) +* `client_ip` -- IP address of the connected client + +and empty messages at the following topics are published. + +* `play_start` -- fired at the begining of every song +* `play_end` -- fired at the end of every song +* `play_flush` -- fired when song is skipped or on positional change +* `play_resume` -- fired when song play resumes from pause +* `active_start` -- fired when a new active airplay session begins +* `active_end` -- fired after a configured timeout period after the stream ends (unless a new stream begins) + +Import Notes +---- + +One needs to set either `publish_raw`, `publish_parsed` or `publish_cover` in the MQTT setup or no messages will be published. + +`active_start` and `active_end` represent a stable on/off flag for the current airplay session. + +`active_start` is plublished when any new airplay session begins + +`active_end` will fire after a configured timeout period unless the airplay stream is resumed. + +```xml +sessioncontrol = +{ +// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed. + active_state_timeout = 30.0; +}; +``` + + + +Example Configuration +---- + +Shairport-sync is configured below to published parsed metadata information and album-art to a MQTT server. + +```xml + +metadata = +{ + enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe + include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes". + cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems + pipe_name = "/tmp/shairport-sync-metadata"; + pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up +}; + + +mqtt = +{ + enabled = "yes"; // set this to yes to enable the mqtt-metadata-service + hostname = "192.168.1.111"; // Hostname of the MQTT Broker + port = 1883; // Port on the MQTT Broker to connect to + username = "username"; //set this to a string to your username in order to enable username authentication + password = "password"; //set this to a string you your password in order to enable username & password authentication + topic = "shairport"; //MQTT topic where this instance of shairport-sync should publish. If not set, the general.name value is used. +// publish_raw = "no"; //whether to publish all available metadata under the codes given in the 'metadata' docs. + publish_parsed = "yes"; //whether to publish a small (but useful) subset of metadata under human-understandable topics + publish_cover = "yes"; //whether to publish the cover over mqtt in binary form. This may lead to a bit of load on the broker +// enable_remote = "no"; //whether to remote control via MQTT. RC is available under `topic`/remote. +}; +``` -- cgit v1.2.3 From f40e70222d24f0429c026a0bcc532863badb30dd Mon Sep 17 00:00:00 2001 From: minix1234 <48591487+minix1234@users.noreply.github.com> Date: Tue, 15 Dec 2020 17:42:40 -0500 Subject: Update README.md added link to MQTT info pages --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 29acc4c..5f92027 100644 --- a/README.md +++ b/README.md @@ -627,3 +627,8 @@ If you are using WiFi, you should ensure that WiFi power management is off. See Troubleshooting --------------- Please refer to [TROUBLESHOOTING](https://github.com/mikebrady/shairport-sync/blob/master/TROUBLESHOOTING.md) for a few hints, contributed by users. + +MQTT +--------------- +Please refer to the [MQTT INFO](https://github.com/mikebrady/shairport-sync/blob/master/MQTT.md) page for additional info on building, configuring and using MQTT to interface shairport-sync with common home automation systems (contributed by users). + -- cgit v1.2.3 From ad429d075b002ec12e304e071da162f3f4ca2752 Mon Sep 17 00:00:00 2001 From: minix1234 <48591487+minix1234@users.noreply.github.com> Date: Tue, 15 Dec 2020 21:38:23 -0500 Subject: Update MQTT.md touch up on initial proposal --- MQTT.md | 160 ++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 50 deletions(-) diff --git a/MQTT.md b/MQTT.md index 7f14f6e..da21a3f 100644 --- a/MQTT.md +++ b/MQTT.md @@ -1,10 +1,65 @@ Setting Up MQTT Publishing ==== -This is a rough guide on the setup of MQTT publishing in ShairPort-Sync. +This is a rough guide on the setup of MQTT publishing in ShairPort-Sync. The MQTT service listens for and publishes metadata generated by the airplay source and shairport-sync. +Below is a simple example of configuring shairport-sync to send parsed metadata to a MQTT server under the topic "shairport" -The MQTT listens for and publishes metadata generated by shairport-sync +Example Configuration +---- + +Shairport-sync is configured below to published parsed metadata information and album-art to a MQTT server. + +```xml + +metadata = +{ + enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe + include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes". + cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems + pipe_name = "/tmp/shairport-sync-metadata"; + pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up +}; + + +mqtt = +{ + enabled = "yes"; // set this to yes to enable the mqtt-metadata-service + hostname = "192.168.1.111"; // Hostname of the MQTT Broker + port = 1883; // Port on the MQTT Broker to connect to + username = "username"; //set this to a string to your username in order to enable username authentication + password = "password"; //set this to a string you your password in order to enable username & password authentication + topic = "shairport"; //MQTT topic where this instance of shairport-sync should publish. If not set, the general.name value is used. +// publish_raw = "no"; //whether to publish all available metadata under the codes given in the 'metadata' docs. + publish_parsed = "yes"; //whether to publish a small (but useful) subset of metadata under human-understandable topics + publish_cover = "yes"; //whether to publish the cover over mqtt in binary form. This may lead to a bit of load on the broker +// enable_remote = "no"; //whether to remote control via MQTT. RC is available under `topic`/remote. +}; +``` + +Import Notes +---- + +Publish Options + +One needs to set either `publish_raw`, `publish_parsed` or `publish_cover` in the MQTT setup or no messages will be published. + + +Overall Active State of Stream + +`active_start` and `active_end` represent a stable on/off flag for the current airplay session. + +`active_start` is plublished when any new airplay session begins + +`active_end` will fire after a configured timeout period unless the airplay stream is resumed. + +```xml +sessioncontrol = +{ +// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed. + active_state_timeout = 30.0; +}; +``` MetaData Parsing ---- @@ -48,12 +103,6 @@ Here are the 'ssnc' codes defined so far: * `snua` -- a "user agent" e.g. "iTunes/12..." has started a play session. Specifically, it's the "User-Agent" string. * `stal` -- this is an error message meaning that reception of a large piece of metadata, usually a large picture, has stalled; bad things may happen. -The next two two tokens are to facilitiate remote control of the source. There is some information at http://nto.github.io/AirPlay.html about remote control of the source. - * `daid` -- this is the source's DACP-ID (if it has one -- it's not guaranteed), useful if you want to remotely control the source. Use this string to identify the source's remote control on the network. - * `acre` -- this is the source's Active-Remote token, necessary if you want to send commands to the source's remote control (if it has one). - * `dapo` -- the payload is the port number (as text) of the source's remote control, to which commands should be sent. It is 3689 for iTunes but varies for iOS devices. - - Parsed Messages ---- @@ -78,55 +127,66 @@ and empty messages at the following topics are published. * `active_start` -- fired when a new active airplay session begins * `active_end` -- fired after a configured timeout period after the stream ends (unless a new stream begins) -Import Notes ----- -One needs to set either `publish_raw`, `publish_parsed` or `publish_cover` in the MQTT setup or no messages will be published. -`active_start` and `active_end` represent a stable on/off flag for the current airplay session. +## Consuming MQTT Data -`active_start` is plublished when any new airplay session begins +Users will find examples on how to consume the MQTT data in various home automation projects. If a user has an interesting use please raise a new issue to suggest adding it to the guide, or simply fork the development branch and create a pull-request. -`active_end` will fire after a configured timeout period unless the airplay stream is resumed. +### Home Assistant Variable Templates -```xml -sessioncontrol = -{ -// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed. - active_state_timeout = 30.0; -}; -``` +Examples of consuming "parsed" MQTT data in [Home Assistant](https://www.home-assistant.io/) +The `active_start` and `active_end` have good potential use as triggers to turn on and off various connect receivers/zones. The messages published are empty and therefor no "payload_on" is set, "payload_off" however is set to prevent accidental triggering. +```yml +binary_sensor: + - platform: mqtt + name: "shairport active start" + state_topic: "shairport/active_start" + payload_on: "" + payload_off: "OFF" + off_delay: 300 -Example Configuration ----- - -Shairport-sync is configured below to published parsed metadata information and album-art to a MQTT server. - -```xml - -metadata = -{ - enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe - include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes". - cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems - pipe_name = "/tmp/shairport-sync-metadata"; - pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up -}; - + - platform: mqtt + name: "shairport active end" + state_topic: "shairport/active_end" + payload_on: "" + payload_off: "OFF" + off_delay: 300 +``` -mqtt = -{ - enabled = "yes"; // set this to yes to enable the mqtt-metadata-service - hostname = "192.168.1.111"; // Hostname of the MQTT Broker - port = 1883; // Port on the MQTT Broker to connect to - username = "username"; //set this to a string to your username in order to enable username authentication - password = "password"; //set this to a string you your password in order to enable username & password authentication - topic = "shairport"; //MQTT topic where this instance of shairport-sync should publish. If not set, the general.name value is used. -// publish_raw = "no"; //whether to publish all available metadata under the codes given in the 'metadata' docs. - publish_parsed = "yes"; //whether to publish a small (but useful) subset of metadata under human-understandable topics - publish_cover = "yes"; //whether to publish the cover over mqtt in binary form. This may lead to a bit of load on the broker -// enable_remote = "no"; //whether to remote control via MQTT. RC is available under `topic`/remote. -}; +Below parsed data is saved into the Home Assistant database as sensor data. Please not the conversion of the volume from dB to percentage. + +```yml +sensor: + - platform: mqtt + name: "shairport album" + state_topic: "shairport/album" + expire_after: 600 + + - platform: mqtt + name: "shairport artist" + state_topic: "shairport/artist" + expire_after: 600 + + - platform: mqtt + name: "shairport title" + state_topic: "shairport/title" + expire_after: 600 + + - platform: mqtt + name: "shairport genre" + state_topic: "shairport/genre" + expire_after: 600 + + - platform: mqtt + name: "shairport volume (dB)" + state_topic: "shairport/volume" + + - platform: mqtt + name: "shairport volume (PCT)" + state_topic: "shairport/volume" + value_template: "{{ value | regex_findall_index(find='^(.+?),', index=0, ignorecase=False) | float / 30 + 1 }}" + unit_of_measurement: 'percent' ``` -- cgit v1.2.3 From c27fb1c9b56521f9f8e0a6605212c12307f9220f Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 16:11:12 +0000 Subject: Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Fix a crash that occurred playing from AirPower on Android. --- dacp.c | 10 ++++--- rtsp.c | 97 +++++++++++++++++++++++++++++++++++------------------------------- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/dacp.c b/dacp.c index acb963b..effbe5e 100644 --- a/dacp.c +++ b/dacp.c @@ -445,9 +445,13 @@ void set_dacp_server_information(rtsp_conn_info *conn) { } if (dacp_server.active_remote_id) free(dacp_server.active_remote_id); - dacp_server.active_remote_id = - strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, - // the active remote will change. + if (conn->dacp_active_remote) + dacp_server.active_remote_id = + strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, + // the active remote will change. + else + dacp_server.active_remote_id = NULL; + debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); pthread_cond_signal(&dacp_server_information_cv); debug_mutex_unlock(&dacp_server_information_lock, 3); diff --git a/rtsp.c b/rtsp.c index 8616a61..f3524ae 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1587,7 +1587,6 @@ void metadata_hub_thread_cleanup_function(__attribute__((unused)) void *arg) { } void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { - // create a pc_queue for passing information to a threaded metadata handler pc_queue_init(&metadata_hub_queue, (char *)&metadata_hub_queue_items, sizeof(metadata_package), metadata_hub_queue_size, "hub"); @@ -1653,41 +1652,41 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { #endif void metadata_init(void) { - - // create the metadata pipe, if necessary - size_t pl = strlen(config.metadata_pipename) + 1; - char *path = malloc(pl + 1); - snprintf(path, pl + 1, "%s", config.metadata_pipename); - - mode_t oldumask = umask(000); - if (mkfifo(path, 0666) && errno != EEXIST) - die("Could not create metadata pipe \"%s\".", path); - umask(oldumask); - debug(1, "metadata pipe name is \"%s\".", path); - - // try to open it - fd = try_to_open_pipe_for_writing(path); - // we check that it's not a "real" error. From the "man 2 open" page: - // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO - // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); + int ret; + if (config.metadata_enabled) { + // create the metadata pipe, if necessary + size_t pl = strlen(config.metadata_pipename) + 1; + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); + mode_t oldumask = umask(000); + if (mkfifo(path, 0666) && errno != EEXIST) + die("Could not create metadata pipe \"%s\".", path); + umask(oldumask); + debug(1, "metadata pipe name is \"%s\".", path); + + // try to open it + fd = try_to_open_pipe_for_writing(path); + // we check that it's not a "real" error. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO + // open for reading." Which is okay. + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } + free(path); + int ret; + ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata thread!"); + + ret = pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata multicast thread!"); } - free(path); - - int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata thread!"); - - ret = pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata multicast thread!"); - #ifdef CONFIG_METADATA_HUB ret = pthread_create(&metadata_hub_thread, NULL, metadata_hub_thread_function, NULL); if (ret) @@ -1716,15 +1715,20 @@ void metadata_stop(void) { pthread_join(metadata_hub_thread, NULL); // debug(2, "metadata stop hub done."); #endif - // debug(2, "metadata stop multicast thread."); - pthread_cancel(metadata_multicast_thread); - pthread_join(metadata_multicast_thread, NULL); - // debug(2, "metadata stop multicast done."); - - // debug(2, "metadata stop metadata_thread thread."); - pthread_cancel(metadata_thread); - pthread_join(metadata_thread, NULL); - // debug(2, "metadata_stop finished successfully."); + if (config.metadata_enabled) { + // debug(2, "metadata stop multicast thread."); + if (metadata_multicast_thread) { + pthread_cancel(metadata_multicast_thread); + pthread_join(metadata_multicast_thread, NULL); + // debug(2, "metadata stop multicast done."); + } + if (metadata_thread) { + // debug(2, "metadata stop metadata_thread thread."); + pthread_cancel(metadata_thread); + pthread_join(metadata_thread, NULL); + // debug(2, "metadata_stop finished successfully."); + } + } } } @@ -1794,7 +1798,10 @@ int send_metadata_to_queue(pc_queue *queue, uint32_t type, uint32_t code, char * int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, int block) { int rc; - rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + if (config.metadata_enabled) { + rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + rc = send_metadata_to_queue(&metadata_multicast_queue, type, code, data, length, carrier, block); + } #ifdef CONFIG_METADATA_HUB rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block); -- cgit v1.2.3 From 5b95a4276f33d182dcafa50eca83c9a078d25b04 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 16:13:44 +0000 Subject: Fix a hang if an on-*** script didn't launch. --- common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common.c b/common.c index b48adad..8520c71 100644 --- a/common.c +++ b/common.c @@ -831,10 +831,10 @@ void command_set_volume(double volume) { execv(argV[0], argV); warn("Execution of on-set-volume command \"%s\" failed to start", config.cmd_set_volume); // debug(1, "Error executing on-set-volume command %s", config.cmd_set_volume); - exit(EXIT_FAILURE); /* only if execv fails */ + _exit(EXIT_FAILURE); /* only if execv fails */ } } - + _exit(EXIT_SUCCESS); } else { if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */ @@ -886,7 +886,7 @@ void command_start(void) { execv(argV[0], argV); warn("Execution of on-start command failed to start"); debug(1, "Error executing on-start command %s", config.cmd_start); - exit(EXIT_FAILURE); /* only if execv fails */ + _exit(EXIT_FAILURE); /* only if execv fails */ } } else { if (config.cmd_blocking || config.cmd_start_returns_output) { /* pid!=0 means parent process @@ -942,7 +942,7 @@ void command_execute(const char *command, const char *extra_argument, const int execv(argV[0], argV); warn("Execution of command \"%s\" failed to start", full_command); debug(1, "Error executing command \"%s\".", full_command); - exit(EXIT_FAILURE); /* only if execv fails */ + _exit(EXIT_FAILURE); /* only if execv fails */ } } else { if (block) { /* pid!=0 means parent process and if blocking is true, wait for -- cgit v1.2.3 From a1810786faead4a89983e05d17ac6ef42217155c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 16:14:01 +0000 Subject: Bug fixes --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 589de5a..adedf3f 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], [3.3.7d20], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8d3], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 45b740ca26a63639480a519a887280659bb5bf17 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 16:17:11 +0000 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 6eca81f..7cb3afc 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,12 @@ +Version 3.3.8d3 + +==== +**Bug Fixes** +* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. +* Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. +* Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. + + Version 3.3.7d19 ==== **Bug Fix** -- cgit v1.2.3 From 88239b242b91f1a7d55bd89e62d4a9c0c9465ff1 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 16:19:17 +0000 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 7cb3afc..064cc2c 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,11 +1,14 @@ Version 3.3.8d3 - ==== + **Bug Fixes** * Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. * Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. * Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. +Version 3.3.8d2 -- 3.3.8d0 +==== +There are absent from the repository -- see `3.3.8rc0` and `rc1` in the `master` branch. Version 3.3.7d19 ==== -- cgit v1.2.3 From 18776f529d65ba3ab1abd05c2a0620fb3b39b6b0 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 17:40:18 +0000 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 064cc2c..63422e8 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,5 +1,7 @@ Version 3.3.8d3 ==== +**Enhancements** +* Documentation for the MQTT interface. Many thanks to [minix1234](https://github.com/minix1234)! **Bug Fixes** * Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. -- cgit v1.2.3 From 272b3db5ffdc7be6d21216c0b602fe02f0cdf3b6 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 23 Dec 2020 17:41:39 +0000 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index dda9160..0554aee 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,7 +2,11 @@ Please see the [Release Notes for 3.3](https://github.com/mikebrady/shairport-sy Version 3.3.8 ==== -**Bug Fix** +**Enhancements** +* Documentation for the MQTT interface. Many thanks to [minix1234](https://github.com/minix1234)! + +**Bug Fixes** +* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. * Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. * Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. -- cgit v1.2.3 From 72176a95a4ef8d043a06800243e09f3db103aed4 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 26 Dec 2020 14:35:18 +0000 Subject: Fix up AC_ARG_WITH logic to work with --without-* properly. Also do some tidying up. Fixed --with-apple-alac, --with-pipe, --with-stdout, --with-dummy. More to do. --- configure.ac | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index adedf3f..cde2ca7 100644 --- a/configure.ac +++ b/configure.ac @@ -41,8 +41,7 @@ AM_CONDITIONAL([BUILD_FOR_OPENBSD], [test "x${with_os}" = xopenbsd ]) ##### Some build systems are not fully using pkg-config, so we can use the flag ${with_pkg_config} on a case-by-case basis ##### to control how to deal with them -AC_ARG_WITH([pkg_config], -[ --with-pkg-config = use pkg-config to find libraries], ,[with_pkg_config=yes]) +AC_ARG_WITH([pkg_config],[AS_HELP_STRING([--with-pkg-config],[use pkg-config to find libraries])],[],[with_pkg_config=yes]) ##### The following check for the pthreads library doesn't put the compiler into the correct pthread mode ##### so we add the -pthread compilation flag in AMFLAGS in the Makefile.am as well. @@ -50,24 +49,35 @@ AC_ARG_WITH([pkg_config], AC_CHECK_LIB([pthread],[pthread_create], , AC_MSG_ERROR(pthread library needed)) AC_CHECK_LIB([m],[exp], , AC_MSG_ERROR(maths library needed)) -AC_MSG_RESULT(>>Including libpopt) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( - [POPT], [popt], - [CFLAGS="${POPT_CFLAGS} ${CFLAGS}" - LIBS="${POPT_LIBS} ${LIBS}"]) + [popt], [popt], + [CFLAGS="${popt_CFLAGS} ${CFLAGS}" + LIBS="${popt_LIBS} ${LIBS}"], AC_MSG_ERROR(libpopt needed)) else AC_CHECK_LIB([popt],[poptGetContext], , AC_MSG_ERROR(libpopt needed)) fi -AC_ARG_WITH([dummy],[ --with-dummy = include the dummy audio back end ],[AC_MSG_RESULT(>>Including the dummy audio back end) AC_DEFINE([CONFIG_DUMMY], 1, [Needed by the compiler.]) ], ) +AC_ARG_WITH([dummy],[AS_HELP_STRING([--with-dummy],[include the dummy audio back end])],[],[]) +if test "x$with_dummy" = "xyes" ; then + AC_MSG_RESULT(>>Including the dummy audio back end) + AC_DEFINE([CONFIG_DUMMY], 1, [Needed by the compiler.]) +fi AM_CONDITIONAL([USE_DUMMY], [test "x$with_dummy" = "xyes" ]) -AC_ARG_WITH([stdout],[ --with-stdout = include the stdout audio back end ],[ AC_MSG_RESULT(>>Including the stdout audio back end) AC_DEFINE([CONFIG_STDOUT], 1, [Needed by the compiler.]) ], ) -AM_CONDITIONAL([USE_STDOUT], [test "x$with_stdout" = "xyes" ]) +AC_ARG_WITH([stdout],[AS_HELP_STRING([--with-stdout],[include the stdout audio back end])],[],[]) +if test "x$with_stdout" = "xyes" ; then + AC_MSG_RESULT(>>Including the stdout audio back end) + AC_DEFINE([CONFIG_STDOUT], 1, [Needed by the compiler.]) +fi +AM_CONDITIONAL([USE_STDOUT], [test "x$with_stdout" = "xyes"]) -AC_ARG_WITH([pipe],[ --with-pipe = include the pipe audio back end ],[ AC_MSG_RESULT(>>Including the pipe audio back end) AC_DEFINE([CONFIG_PIPE], 1, [Needed by the compiler.]) ], ) -AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ]) +AC_ARG_WITH([pipe],[AS_HELP_STRING([--with-pipe],[include the pipe audio back end])],[],[]) +if test "x$with_pipe" = "xyes" ; then + AC_MSG_RESULT(>>Including the pipe audio back end) + AC_DEFINE([CONFIG_PIPE], 1, [Needed by the compiler.]) +fi +AM_CONDITIONAL([USE_PIPE], [test "x$with_dummy" = "xyes" ]) # Check to see if we should include the System V initscript @@ -106,18 +116,17 @@ AC_ARG_WITH([configfiles], AM_CONDITIONAL([INSTALL_CONFIG_FILES], [test "x$with_configfiles" = "xyes"]) # Look for Apple ALAC flag -AC_ARG_WITH(apple-alac, [ --with-apple-alac = include support for the Apple ALAC decoder], - [AC_MSG_RESULT(>>Including the Apple ALAC Decoder) +AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for the Apple ALAC decoder])], []) +if test "x${with_apple_alac}" = "xyes" ; then +AC_MSG_RESULT(including the Apple ALAC Decoder) AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Include support for using the Apple ALAC Decoder]) - REQUESTED_APPLE_ALAC=1 if test "x${with_pkg_config}" = xyes ; then - PKG_CHECK_MODULES( - [ALAC], [alac], - [LIBS="${ALAC_LIBS} ${LIBS}"]) + PKG_CHECK_MODULES([ALAC], [alac], [LIBS="${ALAC_LIBS} ${LIBS}"], AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library.)) else - AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library!)) - fi ]) -AM_CONDITIONAL([USE_APPLE_ALAC], [test "x$REQUESTED_APPLE_ALAC" = "x1"]) + AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library.)) + fi +fi +AM_CONDITIONAL([USE_APPLE_ALAC], [test "x${with_apple_alac}" = "xyes"]) # Look for piddir flag AC_ARG_WITH(piddir, [ --with-piddir= Specify a pathname to a directory in which to write the PID file.], [ @@ -156,7 +165,7 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=p AC_MSG_ERROR(choose "openssl", "mbedtls" or "polarssl" encryption) fi if test "x${with_ssl}" = xopenssl ; then - AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding]) + AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [SSL], [libssl,libcrypto], @@ -166,7 +175,7 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=p AC_CHECK_LIB([ssl], [main], , AC_MSG_ERROR(libssl selected but the library cannot be found!)) fi elif test "x${with_ssl}" = xmbedtls ; then - AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding]) + AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding]) AC_CHECK_LIB([mbedtls],[mbedtls_ssl_init],, [AC_MSG_ERROR([mbed tls support requires the mbedtls library -- libmbedtls-dev suggested],1)]) AC_CHECK_LIB([mbedcrypto], [mbedtls_entropy_func],, @@ -174,7 +183,7 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=p AC_CHECK_LIB([mbedx509], [mbedtls_pk_init],, [AC_MSG_ERROR([mbed tls support requires the mbedx509 library -- libmbedx509-0 suggested],1)]) elif test "x${with_ssl}" = xpolarssl ; then - AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding]) + AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding]) AC_CHECK_LIB([polarssl],[ssl_init], , AC_MSG_ERROR(PolarSSL is selected but the library cannot be found and is deprecated. Consider selecting mbed TLS instead using --with-ssl=mbedtls.)) else AC_MSG_ERROR(unknown option "${with_ssl}"." Please choose with "openssl", "mbedtls" or "polarssl") -- cgit v1.2.3 From 2538e46abe733360a0bb39ec66c3968f138f647e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 26 Dec 2020 15:45:13 +0000 Subject: Further changes, use AS_HELP_STRING macros, remove most include messages, make many tokens lowercase. --- configure.ac | 114 +++++++++++++++++++++++------------------------------------ 1 file changed, 44 insertions(+), 70 deletions(-) diff --git a/configure.ac b/configure.ac index cde2ca7..46b3e4e 100644 --- a/configure.ac +++ b/configure.ac @@ -58,67 +58,61 @@ else AC_CHECK_LIB([popt],[poptGetContext], , AC_MSG_ERROR(libpopt needed)) fi -AC_ARG_WITH([dummy],[AS_HELP_STRING([--with-dummy],[include the dummy audio back end])],[],[]) +AC_ARG_WITH([dummy],[AS_HELP_STRING([--with-dummy],[include the dummy audio back end])]) if test "x$with_dummy" = "xyes" ; then - AC_MSG_RESULT(>>Including the dummy audio back end) + AC_MSG_RESULT(include the dummy audio back end) AC_DEFINE([CONFIG_DUMMY], 1, [Needed by the compiler.]) fi AM_CONDITIONAL([USE_DUMMY], [test "x$with_dummy" = "xyes" ]) -AC_ARG_WITH([stdout],[AS_HELP_STRING([--with-stdout],[include the stdout audio back end])],[],[]) +AC_ARG_WITH([stdout],[AS_HELP_STRING([--with-stdout],[include the stdout audio back end])]) if test "x$with_stdout" = "xyes" ; then - AC_MSG_RESULT(>>Including the stdout audio back end) + AC_MSG_RESULT(include the stdout audio back end) AC_DEFINE([CONFIG_STDOUT], 1, [Needed by the compiler.]) fi AM_CONDITIONAL([USE_STDOUT], [test "x$with_stdout" = "xyes"]) -AC_ARG_WITH([pipe],[AS_HELP_STRING([--with-pipe],[include the pipe audio back end])],[],[]) +AC_ARG_WITH([pipe],[AS_HELP_STRING([--with-pipe],[include the pipe audio back end])]) if test "x$with_pipe" = "xyes" ; then - AC_MSG_RESULT(>>Including the pipe audio back end) + AC_MSG_RESULT(include the pipe audio back end) AC_DEFINE([CONFIG_PIPE], 1, [Needed by the compiler.]) fi AM_CONDITIONAL([USE_PIPE], [test "x$with_dummy" = "xyes" ]) # Check to see if we should include the System V initscript -AC_ARG_WITH([systemv], -[ --with-systemv = install a System V startup script during a make install], , ) +AC_ARG_WITH([systemv],[AS_HELP_STRING([--with-systemv],[install a System V startup script during a make install])]) AM_CONDITIONAL([INSTALL_SYSTEMV], [test "x$with_systemv" = "xyes"]) # Check to see if we should include the systemd stuff to define it as a service -AC_ARG_WITH([systemd], -[ --with-systemd = install a systemd startup script during a make install], , ) +AC_ARG_WITH([systemd],[AS_HELP_STRING([--with-systemd],[install a systemd startup script during a make install])]) AM_CONDITIONAL([INSTALL_SYSTEMD], [test "x$with_systemd" = "xyes"]) -AC_ARG_WITH([freebsd-service], -[ --with-freebsd-service = install a FreeBSD startup script during a make install], , ) +AC_ARG_WITH([freebsd-service],[AS_HELP_STRING([--with-freebsd-service],[install a FreeBSD startup script during a make install])]) AM_CONDITIONAL([INSTALL_FREEBSD_SERVICE], [test "x$with_freebsd_service" = "xyes"]) -AC_ARG_WITH([cygwin-service], -[ --with-cygwin-service = install a CYGWIN config script during a make install], , ) +AC_ARG_WITH([cygwin-service],[AS_HELP_STRING([--with-cygwin-service],[install a CYGWIN config script during a make install])]) AM_CONDITIONAL([INSTALL_CYGWIN_SERVICE], [test "x$with_cygwin_service" = "xyes"]) -AC_ARG_WITH([external-mdns],[ --with-external-mdns = support the use of 'avahi-publish-service' or 'mDNSPublish' to advertise the service on Bonjour/ZeroConf ],[ AC_MSG_RESULT(>>Including external mdns support) AC_DEFINE([CONFIG_EXTERNAL_MDNS], 1, [Needed by the compiler.]) ], ) +AC_ARG_WITH([external-mdns],[AS_HELP_STRING([--with-external-mdns],[support the use of 'avahi-publish-service' or 'mDNSPublish' to advertise the service on Bonjour/ZeroConf])],[ AC_MSG_RESULT(include external mdns support) AC_DEFINE([CONFIG_EXTERNAL_MDNS], 1, [Needed by the compiler.]) ], ) AM_CONDITIONAL([USE_EXTERNAL_MDNS], [test "x$with_external_mdns" = "xyes" ]) # Add the libconfig package if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( - [LIBCONFIG], [libconfig], - [LIBS="${LIBCONFIG_LIBS} ${LIBS}"]) + [libconfig], [libconfig], + [LIBS="${libconfig_LIBS} ${LIBS}"], AC_MSG_ERROR([libconfig library needed])) else AC_CHECK_LIB([config],[config_init], , AC_MSG_ERROR([libconfig library needed])) fi -AC_ARG_WITH([configfiles], -[ --with-configfiles = install configuration files during a make install ], ,[with_configfiles=yes]) +AC_ARG_WITH([configfiles],[AS_HELP_STRING([--with-configfiles],[install configuration files during a make install])], ,[with_configfiles=yes]) AM_CONDITIONAL([INSTALL_CONFIG_FILES], [test "x$with_configfiles" = "xyes"]) # Look for Apple ALAC flag -AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for the Apple ALAC decoder])], []) +AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for the Apple ALAC decoder])]) if test "x${with_apple_alac}" = "xyes" ; then -AC_MSG_RESULT(including the Apple ALAC Decoder) AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Include support for using the Apple ALAC Decoder]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES([ALAC], [alac], [LIBS="${ALAC_LIBS} ${LIBS}"], AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library.)) @@ -129,7 +123,7 @@ fi AM_CONDITIONAL([USE_APPLE_ALAC], [test "x${with_apple_alac}" = "xyes"]) # Look for piddir flag -AC_ARG_WITH(piddir, [ --with-piddir= Specify a pathname to a directory in which to write the PID file.], [ +AC_ARG_WITH(piddir, [AS_HELP_STRING([--with-piddir=],[Specify a pathname to a directory in which to write the PID file.])], [ AC_MSG_CHECKING(--with-piddir argument) if test "x${with_piddir}" = x -o "x${with_piddir}" = xyes ; then AC_MSG_RESULT(not found) @@ -143,9 +137,8 @@ AM_CONDITIONAL([USE_CUSTOMPIDDIR], [test "x$REQUESTED_CUSTOMPIDDIR" = "x1"]) # Look for libdaemon -AC_ARG_WITH(libdaemon, [--with-libdaemon = include support for daemonising in non-systemd systems], REQUESTED_LIBDAEMON=1, ) +AC_ARG_WITH(libdaemon,[AS_HELP_STRING([--with-libdaemon],[include support for daemonising in non-systemd systems])], REQUESTED_LIBDAEMON=1, ) if test "x$REQUESTED_LIBDAEMON" = "x1"; then - AC_MSG_RESULT(>>Including libdaemon support for older systems) AC_DEFINE([CONFIG_LIBDAEMON], 1, [Include libdaemon]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -158,7 +151,7 @@ fi AM_CONDITIONAL([USE_LIBDAEMON], [test "x$REQUESTED_LIBDAEMON" = "x1"]) # Check --with-ssl=argument -AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=polarssl (deprecated) for encryption services ], [ +AC_ARG_WITH(ssl, [AS_HELP_STRING([--with-ssl=],[choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=polarssl (deprecated) for encryption services ])], [ AC_MSG_CHECKING(encryption libraries chosen) if test "x${with_ssl}" = x -o "x${with_ssl}" = xyes ; then AC_MSG_RESULT(not found) @@ -191,13 +184,12 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=p ], ) # Look for soxr flag -AC_ARG_WITH(soxr, [ --with-soxr = choose libsoxr for high-quality interpolation], [ - AC_MSG_RESULT(>>Including support for soxr-based interpolation) +AC_ARG_WITH(soxr, [AS_HELP_STRING([--with-soxr],[choose libsoxr for high-quality interpolation])], [ AC_DEFINE([CONFIG_SOXR], 1, [Include support for using the SoX Resampler library for interpolation]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( - [SOXR], [soxr], - [LIBS="${SOXR_LIBS} ${LIBS}"], + [soxr], [soxr], + [LIBS="${soxr_LIBS} ${LIBS}"], [AC_MSG_ERROR(soxr support requires the libsoxr library -- libsoxr-dev suggested!)]) else AC_CHECK_LIB([avutil],[av_get_cpu_flags]) @@ -211,51 +203,46 @@ AC_ARG_WITH(soxr, [ --with-soxr = choose libsoxr for high-quality interpolation ], ) # Look for metadata flag and resolve it further down the script -AC_ARG_WITH(metadata, [ --with-metadata = include support for a metadata feed], [ - REQUESTED_METADATA=1], ) +AC_ARG_WITH(metadata,[AS_HELP_STRING([--with-metadata],[include support for a metadata feed])], [REQUESTED_METADATA=1], ) # What follows is a bit messy, because if the relevant library is requested, a compiler flag is defined, a file is included in the compilation # and the relevant link files are added. # Look for avahi flag -AC_ARG_WITH(avahi, [ --with-avahi = choose Avahi-based mDNS support], [ - AC_MSG_RESULT(>>Including Avahi mDNS support) +AC_ARG_WITH(avahi, [AS_HELP_STRING([--with-avahi],[choose Avahi-based mDNS support])], [ REQUESTED_AVAHI=1 AC_DEFINE([CONFIG_AVAHI], 1, [Needed by the compiler.]) AC_CHECK_LIB([avahi-client], [avahi_client_new], , AC_MSG_ERROR(Avahi support requires the avahi-client library!)) - AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!))], ) + AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!))]) AM_CONDITIONAL([USE_AVAHI], [test "x$REQUESTED_AVAHI" = "x1"]) # Look for tinysvcmdns flag -AC_ARG_WITH(tinysvcmdns, [ --with-tinysvcmdns = choose tinysvcmdns-based mDNS support], [ - AC_MSG_RESULT(>>Including tinysvcmdns mDNS support) +AC_ARG_WITH(tinysvcmdns, [AS_HELP_STRING([--with-tinysvcmdns],[choose tinysvcmdns-based mDNS support])], [ REQUESTED_TINYSVCMDNS=1 AC_DEFINE([CONFIG_TINYSVCMDNS], 1, [Needed by the compiler.])], ) AM_CONDITIONAL([USE_TINYSVCMDNS], [test "x$REQUESTED_TINYSVCMDNS" = "x1"]) # Look for ALSA flag -AC_ARG_WITH(alsa, [ --with-alsa = choose ALSA API support (GNU/Linux only)], - [AC_MSG_RESULT(>>Including an ALSA back end) +AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa],[choose ALSA API support (GNU/Linux only)])], [ REQUESTED_ALSA=1 AC_DEFINE([CONFIG_ALSA], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( - [ALSA], [alsa], - [LIBS="${ALSA_LIBS} ${LIBS}"]) + [alsa], [alsa], + [LIBS="${alsa_LIBS} ${LIBS}"]) else AC_CHECK_LIB([asound], [snd_pcm_open], , AC_MSG_ERROR(ALSA support requires the asound library!)) fi ]) AM_CONDITIONAL([USE_ALSA], [test "x$REQUESTED_ALSA" = "x1"]) # Look for jack flag -AC_ARG_WITH(jack, [ --with-jack = include a Jack Audio Connection Kit (jack) backend], [ - AC_MSG_RESULT(>>Including a Jack Audio Connection Kit (jack) back end) +AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack],[include a Jack Audio Connection Kit (jack) backend])], [ REQUESTED_JACK=1 AC_DEFINE([CONFIG_JACK], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( - [JACK], [jack], - [LIBS="${JACK_LIBS} ${LIBS}"], + [jack], [jack], + [LIBS="${jack_LIBS} ${LIBS}"], [AC_MSG_ERROR(Jack Audio Connection Kit support requires the jack library -- libjack-dev suggested!)]) else AC_CHECK_LIB([jack], [jack_client_open], , AC_MSG_ERROR(Jack Audio Connection Kit support requires the jack library -- libjack-dev suggested!)) @@ -263,32 +250,28 @@ AC_ARG_WITH(jack, [ --with-jack = include a Jack Audio Connection Kit (jack) ba AM_CONDITIONAL([USE_JACK], [test "x$REQUESTED_JACK" = "x1"]) # Look for SNDIO flag -AC_ARG_WITH(sndio, [ --with-sndio = choose SNDIO API support], [ - AC_MSG_RESULT(>>Including a SNDIO back end) +AC_ARG_WITH(sndio, [AS_HELP_STRING([--with-sndio],[choose SNDIO API support])], [ REQUESTED_SNDIO=1 AC_DEFINE([CONFIG_SNDIO], 1, [Needed by the compiler.]) AC_CHECK_LIB([sndio], [sio_open], , AC_MSG_ERROR(SNDIO support requires the sndio library -- libsndio-dev suggested))], ) AM_CONDITIONAL([USE_SNDIO], [test "x$REQUESTED_SNDIO" = "x1"]) # Look for AO flag -AC_ARG_WITH(ao, [ --with-ao = choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!], [ - AC_MSG_RESULT(>>Including an AO back end. N.B. no synchronisation -- so underflow or overflow is inevitable!) +AC_ARG_WITH(ao, [AS_HELP_STRING([--with-ao],[choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!])], [ REQUESTED_AO=1 AC_DEFINE([CONFIG_AO], 1, [Needed by the compiler.]) AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested))], ) AM_CONDITIONAL([USE_AO], [test "x$REQUESTED_AO" = "x1"]) # Look for Soundio flag -AC_ARG_WITH(soundio, [ --with-soundio = choose soundio API support.], [ - AC_MSG_RESULT(>>Including an soundio back end) +AC_ARG_WITH(soundio, [AS_HELP_STRING([--with-soundio],[choose soundio API support.])], [ REQUESTED_SOUNDIO=1 AC_DEFINE([CONFIG_SOUNDIO], 1, [Needed by the compiler.]) AC_CHECK_LIB([soundio], [soundio_create], , AC_MSG_ERROR(soundio support requires the soundio library -- libsoundio-dev suggested))], ) AM_CONDITIONAL([USE_SOUNDIO], [test "x$REQUESTED_SOUNDIO" = "x1"]) # Look for pulseaudio flag -AC_ARG_WITH(pa, [ --with-pa = choose PulseAudio support.], [ - AC_MSG_RESULT(>>Including a PulseAudio back end.) +AC_ARG_WITH(pa, [AS_HELP_STRING([--with-pa],[choose PulseAudio support.])], [ REQUESTED_PA=1 AC_DEFINE([CONFIG_PA], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then @@ -302,8 +285,7 @@ AC_ARG_WITH(pa, [ --with-pa = choose PulseAudio support.], [ AM_CONDITIONAL([USE_PA], [test "x$REQUESTED_PA" = "x1"]) # Look for Convolution flag -AC_ARG_WITH(convolution, [ --with-convolution = choose audio DSP convolution support], [ - AC_MSG_RESULT(>>Including convolution support) +AC_ARG_WITH(convolution, [AS_HELP_STRING([--with-convolution],[choose audio DSP convolution support])], [ REQUESTED_CONVOLUTION=1 AM_INIT_AUTOMAKE([subdir-objects]) AC_DEFINE([CONFIG_CONVOLUTION], 1, [Needed by the compiler.]) @@ -311,16 +293,14 @@ AC_ARG_WITH(convolution, [ --with-convolution = choose audio DSP convolution su AM_CONDITIONAL([USE_CONVOLUTION], [test "x$REQUESTED_CONVOLUTION" = "x1"]) # Look for dns_sd flag -AC_ARG_WITH(dns_sd, [ --with-dns_sd = choose dns_sd mDNS support], [ - AC_MSG_RESULT(>>Including dns_sd for mDNS support) +AC_ARG_WITH(dns_sd, [AS_HELP_STRING([--with-dns_sd],[choose dns_sd mDNS support])], [ REQUESTED_DNS_SD=1 AC_DEFINE([CONFIG_DNS_SD], 1, [Needed by the compiler.]) AC_SEARCH_LIBS([DNSServiceRefDeallocate], [dns_sd], , AC_MSG_ERROR(dns_sd support requires the dns_sd library!))], ) AM_CONDITIONAL([USE_DNS_SD], [test "x$REQUESTED_DNS_SD" = "x1"]) # Look for dbus flag -AC_ARG_WITH(dbus-interface, [ --with-dbus-interface = include support for the native Shairport Sync D-Bus interface], [ - AC_MSG_RESULT(>>Including dbus support) +AC_ARG_WITH(dbus-interface, [AS_HELP_STRING([--with-dbus-interface],[include support for the native Shairport Sync D-Bus interface])], [ AC_DEFINE([CONFIG_DBUS_INTERFACE], 1, [Include support for the native Shairport Sync D-Bus interface]) REQUESTED_DBUS=1 PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus messaging support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) @@ -328,16 +308,14 @@ AC_ARG_WITH(dbus-interface, [ --with-dbus-interface = include support for the n AM_CONDITIONAL([USE_DBUS], [test "x$REQUESTED_DBUS" = "x1"]) # Look for dbus test client flag -AC_ARG_WITH(dbus-test-client, [ --with-dbus-test-client = compile dbus test client], [ - AC_MSG_RESULT(>>Including dbus test client) +AC_ARG_WITH(dbus-test-client, [AS_HELP_STRING([--with-dbus-test-client],[compile a separare D-Bus test client])], [ REQUESTED_DBUS_CLIENT=1 PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_DBUS_CLIENT], [test "x$REQUESTED_DBUS_CLIENT" = "x1"]) # Look for mpris flag -AC_ARG_WITH(mpris-interface, [ --with-mpris-interface = include support for a D-Bus interface conforming to the MPRIS standard], [ - AC_MSG_RESULT(>>Including the MPRIS D-Bus Interface) +AC_ARG_WITH(mpris-interface, [AS_HELP_STRING([--with-mpris-interface],[include support for a D-Bus interface conforming to the MPRIS standard])], [ AC_DEFINE([CONFIG_MPRIS_INTERFACE], 1, [Include support for a D-Bus interface conforming to the MPRIS standard]) REQUESTED_MPRIS=1 PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus messaging support for mpris requires the glib 2.0 library -- libglib2.0-dev suggested!)]) @@ -345,28 +323,25 @@ AC_ARG_WITH(mpris-interface, [ --with-mpris-interface = include support for a D AM_CONDITIONAL([USE_MPRIS], [test "x$REQUESTED_MPRIS" = "x1"]) # Look for mpris test client flag -AC_ARG_WITH(mpris-test-client, [ --with-mpris-test-client = compile mpris test client], [ - AC_MSG_RESULT(>>Including mpris test client) +AC_ARG_WITH(mpris-test-client, [AS_HELP_STRING([--with-mpris-test-client],[compile an separate MPRIS test client])], [ REQUESTED_MPRIS_CLIENT=1 PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(mpris client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_MPRIS_CLIENT], [test "x$REQUESTED_MPRIS_CLIENT" = "x1"]) # Look for mqtt flag -AC_ARG_WITH(mqtt-client, [ --with-mqtt-client = include a client for MQTT -- the Message Queuing Telemetry Transport protocol], [ +AC_ARG_WITH(mqtt-client, [AS_HELP_STRING([--with-mqtt-client],[include a client for MQTT -- the Message Queuing Telemetry Transport protocol])], [ AC_DEFINE([CONFIG_MQTT], 1, [Include a client for MQTT, the Message Queuing Telemetry Transport protocol]) - AC_MSG_RESULT(>>Including MQTT support) REQUESTED_MQTT=1 AC_CHECK_LIB([mosquitto], [mosquitto_lib_init], , AC_MSG_ERROR(MQTT support requires the mosquitto library -- libmosquitto-dev suggested!)) ],) AM_CONDITIONAL([USE_MQTT], [test "x$REQUESTED_MQTT" = "x1"]) if test "x$REQUESTED_MQTT" = "x1" && test "x$REQUESTED_AVAHI" != "x1"; then - AC_MSG_WARN([>>MQTT needs Avahi to allow remote control functionality. Only Metadata publishing will be supported]) + AC_MSG_WARN([MQTT needs Avahi to allow remote control functionality. At present, only metadata publishing will be supported]) fi if test "x$REQUESTED_MPRIS" = "x1" || test "x$REQUESTED_DBUS" = "x1" || test "x$REQUESTED_MQTT" = "x1"; then - AC_MSG_RESULT(>>Including extended metadata and DACP client support) REQUESTED_EXTENDED_METADATA_SUPPORT=1 AC_DEFINE([CONFIG_METADATA_HUB], 1, [Needed by the compiler.]) AC_DEFINE([CONFIG_DACP_CLIENT], 1, [Needed by the compiler.]) @@ -375,7 +350,6 @@ AM_CONDITIONAL([USE_METADATA_HUB], [test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" AM_CONDITIONAL([USE_DACP_CLIENT], [test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1"]) if test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1" || test "x$REQUESTED_METADATA" = "x1"; then - AC_MSG_RESULT(>>Including metadata support) AC_DEFINE([CONFIG_METADATA], 1, [Needed by the compiler.]) fi AM_CONDITIONAL([USE_METADATA], [test "x$REQUESTED_METADATA" = "x1"]) @@ -400,7 +374,7 @@ fi # Look for xmltoman AC_CHECK_PROGS([XMLTOMAN], [xmltoman]) if test -z "$XMLTOMAN"; then - AC_MSG_WARN([>>xmltoman not found - not rebuilding man pages]) + AC_MSG_WARN([xmltoman not found - not rebuilding man pages]) fi AM_CONDITIONAL([HAVE_XMLTOMAN], [test -n "$XMLTOMAN"]) -- cgit v1.2.3 From 3612fa374fc2de5d1e3e111216e512bf20da8309 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 26 Dec 2020 16:10:04 +0000 Subject: Further changes, make ALAC and GIO lowecase. --- configure.ac | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 46b3e4e..3a86e3b 100644 --- a/configure.ac +++ b/configure.ac @@ -115,9 +115,9 @@ AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for if test "x${with_apple_alac}" = "xyes" ; then AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Include support for using the Apple ALAC Decoder]) if test "x${with_pkg_config}" = xyes ; then - PKG_CHECK_MODULES([ALAC], [alac], [LIBS="${ALAC_LIBS} ${LIBS}"], AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library.)) + PKG_CHECK_MODULES([alac], [alac], [LIBS="${alac_LIBS} ${LIBS}"], AC_MSG_ERROR(Apple ALAC Decoder support requires the ALAC library.)) else - AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library.)) + AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the ALAC library.)) fi fi AM_CONDITIONAL([USE_APPLE_ALAC], [test "x${with_apple_alac}" = "xyes"]) @@ -303,14 +303,14 @@ AM_CONDITIONAL([USE_DNS_SD], [test "x$REQUESTED_DNS_SD" = "x1"]) AC_ARG_WITH(dbus-interface, [AS_HELP_STRING([--with-dbus-interface],[include support for the native Shairport Sync D-Bus interface])], [ AC_DEFINE([CONFIG_DBUS_INTERFACE], 1, [Include support for the native Shairport Sync D-Bus interface]) REQUESTED_DBUS=1 - PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus messaging support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) + PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(D-Bus messaging support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_DBUS], [test "x$REQUESTED_DBUS" = "x1"]) # Look for dbus test client flag AC_ARG_WITH(dbus-test-client, [AS_HELP_STRING([--with-dbus-test-client],[compile a separare D-Bus test client])], [ REQUESTED_DBUS_CLIENT=1 - PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) + PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(D-Bus client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_DBUS_CLIENT], [test "x$REQUESTED_DBUS_CLIENT" = "x1"]) @@ -318,14 +318,14 @@ AM_CONDITIONAL([USE_DBUS_CLIENT], [test "x$REQUESTED_DBUS_CLIENT" = "x1"]) AC_ARG_WITH(mpris-interface, [AS_HELP_STRING([--with-mpris-interface],[include support for a D-Bus interface conforming to the MPRIS standard])], [ AC_DEFINE([CONFIG_MPRIS_INTERFACE], 1, [Include support for a D-Bus interface conforming to the MPRIS standard]) REQUESTED_MPRIS=1 - PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus messaging support for mpris requires the glib 2.0 library -- libglib2.0-dev suggested!)]) + PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(MPRIS messaging support for mpris requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_MPRIS], [test "x$REQUESTED_MPRIS" = "x1"]) # Look for mpris test client flag AC_ARG_WITH(mpris-test-client, [AS_HELP_STRING([--with-mpris-test-client],[compile an separate MPRIS test client])], [ REQUESTED_MPRIS_CLIENT=1 - PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(mpris client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) + PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(MPRIS client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) ], ) AM_CONDITIONAL([USE_MPRIS_CLIENT], [test "x$REQUESTED_MPRIS_CLIENT" = "x1"]) -- cgit v1.2.3 From 9af9dafb97ac227c3185a9ad6db43305a45c7b1e Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 26 Dec 2020 16:11:53 +0000 Subject: Partially fix the --with-* and --without-* issues and clean up the script. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3a86e3b..acaebe2 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], [3.3.8d3], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8d4], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) -- cgit v1.2.3 From 26ff0c2095fd6686320fa93b6dac17a0a02fe863 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 30 Dec 2020 19:32:46 +0000 Subject: More of the configure.ac fixes done. Still incomplete. --- configure.ac | 174 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/configure.ac b/configure.ac index acaebe2..dd59c22 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,10 @@ if test "z$with_os" = "z"; then fi with_os=`echo ${with_os} | tr '[[:upper:]]' '[[:lower:]]' ` +if test "x${with_os}" != xlinux && "x${with_os}" != xfreebsd && "x${with_os}" != xdarwin ; then + AC_MSG_ERROR(--with-os= argument must linux, freebsd, openbsd or darwin) +fi + # Checks for programs. AC_PROG_CC AC_PROG_CXX @@ -40,6 +44,7 @@ AM_CONDITIONAL([BUILD_FOR_OPENBSD], [test "x${with_os}" = xopenbsd ]) ##### Some build systems are not fully using pkg-config, so we can use the flag ${with_pkg_config} on a case-by-case basis ##### to control how to deal with them +##### Note -- this flag is sometimes ignored, especially for newer packages AC_ARG_WITH([pkg_config],[AS_HELP_STRING([--with-pkg-config],[use pkg-config to find libraries])],[],[with_pkg_config=yes]) @@ -77,7 +82,7 @@ if test "x$with_pipe" = "xyes" ; then AC_MSG_RESULT(include the pipe audio back end) AC_DEFINE([CONFIG_PIPE], 1, [Needed by the compiler.]) fi -AM_CONDITIONAL([USE_PIPE], [test "x$with_dummy" = "xyes" ]) +AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ]) # Check to see if we should include the System V initscript @@ -95,7 +100,11 @@ AM_CONDITIONAL([INSTALL_FREEBSD_SERVICE], [test "x$with_freebsd_service" = "xyes AC_ARG_WITH([cygwin-service],[AS_HELP_STRING([--with-cygwin-service],[install a CYGWIN config script during a make install])]) AM_CONDITIONAL([INSTALL_CYGWIN_SERVICE], [test "x$with_cygwin_service" = "xyes"]) -AC_ARG_WITH([external-mdns],[AS_HELP_STRING([--with-external-mdns],[support the use of 'avahi-publish-service' or 'mDNSPublish' to advertise the service on Bonjour/ZeroConf])],[ AC_MSG_RESULT(include external mdns support) AC_DEFINE([CONFIG_EXTERNAL_MDNS], 1, [Needed by the compiler.]) ], ) +AC_ARG_WITH([external-mdns],[AS_HELP_STRING([--with-external-mdns],[support the use of 'avahi-publish-service' or 'mDNSPublish' to advertise the service on Bonjour/ZeroConf])]) +if test "x$with_external_mdns" = xyes ; then + AC_MSG_RESULT(include external mdns support) + AC_DEFINE([CONFIG_EXTERNAL_MDNS], 1, [Use 'avahi-publish-service' or 'mDNSPublish' to advertise.]) +fi AM_CONDITIONAL([USE_EXTERNAL_MDNS], [test "x$with_external_mdns" = "xyes" ]) # Add the libconfig package @@ -123,22 +132,22 @@ fi AM_CONDITIONAL([USE_APPLE_ALAC], [test "x${with_apple_alac}" = "xyes"]) # Look for piddir flag -AC_ARG_WITH(piddir, [AS_HELP_STRING([--with-piddir=],[Specify a pathname to a directory in which to write the PID file.])], [ +AC_ARG_WITH(piddir, [AS_HELP_STRING([--with-piddir=],[Specify a pathname to a directory in which to write the PID file.])]) +if test "x${with_piddir}" != "x" ; then AC_MSG_CHECKING(--with-piddir argument) if test "x${with_piddir}" = x -o "x${with_piddir}" = xyes ; then AC_MSG_RESULT(not found) AC_MSG_ERROR(when you use the --with-piddir directive you must specify the pathname of the directory into which the PID file will be written) fi AC_MSG_RESULT(${with_piddir}) - REQUESTED_CUSTOMPIDDIR=1 - AC_SUBST(CUSTOM_PID_DIR,["${with_piddir}"]) - AC_DEFINE([DEFINED_CUSTOM_PID_DIR],1,[Hook up special proc to libdaemon to point to this directory])], ) -AM_CONDITIONAL([USE_CUSTOMPIDDIR], [test "x$REQUESTED_CUSTOMPIDDIR" = "x1"]) - + AC_SUBST(CUSTOM_PID_DIR,["${with_piddir}"]) + AC_DEFINE([DEFINED_CUSTOM_PID_DIR],1,[Hook up special proc to libdaemon to point to this directory]) +fi +AM_CONDITIONAL([USE_CUSTOMPIDDIR], [ test "x${with_piddir}" != "x" ]) # Look for libdaemon -AC_ARG_WITH(libdaemon,[AS_HELP_STRING([--with-libdaemon],[include support for daemonising in non-systemd systems])], REQUESTED_LIBDAEMON=1, ) -if test "x$REQUESTED_LIBDAEMON" = "x1"; then +AC_ARG_WITH(libdaemon,[AS_HELP_STRING([--with-libdaemon],[include support for daemonising in non-systemd systems])]) +if test "x$with_libdaemon" = "x1"; then AC_DEFINE([CONFIG_LIBDAEMON], 1, [Include libdaemon]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -148,43 +157,40 @@ if test "x$REQUESTED_LIBDAEMON" = "x1"; then AC_CHECK_LIB([daemon],[daemon_fork], , AC_MSG_ERROR(libdaemon needed)) fi fi -AM_CONDITIONAL([USE_LIBDAEMON], [test "x$REQUESTED_LIBDAEMON" = "x1"]) +AM_CONDITIONAL([USE_LIBDAEMON], [test "x$with_libdaemon" = "x1"]) # Check --with-ssl=argument -AC_ARG_WITH(ssl, [AS_HELP_STRING([--with-ssl=],[choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=polarssl (deprecated) for encryption services ])], [ - AC_MSG_CHECKING(encryption libraries chosen) - if test "x${with_ssl}" = x -o "x${with_ssl}" = xyes ; then - AC_MSG_RESULT(not found) - AC_MSG_ERROR(choose "openssl", "mbedtls" or "polarssl" encryption) - fi - if test "x${with_ssl}" = xopenssl ; then - AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding]) - if test "x${with_pkg_config}" = xyes ; then - PKG_CHECK_MODULES( - [SSL], [libssl,libcrypto], - [LIBS="${SSL_LIBS} ${LIBS}"]) - else - AC_CHECK_LIB([crypto], [main], , AC_MSG_ERROR(libcrypto selected but the library cannot be found!)) - AC_CHECK_LIB([ssl], [main], , AC_MSG_ERROR(libssl selected but the library cannot be found!)) - fi - elif test "x${with_ssl}" = xmbedtls ; then - AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding]) - AC_CHECK_LIB([mbedtls],[mbedtls_ssl_init],, +AC_ARG_WITH(ssl, [AS_HELP_STRING([--with-ssl=],[choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=polarssl (deprecated) for encryption services ])]) + +if test "x${with_ssl}" = xopenssl ; then + AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding]) + if test "x${with_pkg_config}" = xyes ; then + PKG_CHECK_MODULES( + [SSL], [libssl,libcrypto], + [LIBS="${SSL_LIBS} ${LIBS}"]) + else + AC_CHECK_LIB([crypto], [main], , AC_MSG_ERROR(libcrypto selected but the library cannot be found!)) + AC_CHECK_LIB([ssl], [main], , AC_MSG_ERROR(libssl selected but the library cannot be found!)) + fi +elif test "x${with_ssl}" = xmbedtls ; then + AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding]) + AC_CHECK_LIB([mbedtls],[mbedtls_ssl_init],, [AC_MSG_ERROR([mbed tls support requires the mbedtls library -- libmbedtls-dev suggested],1)]) - AC_CHECK_LIB([mbedcrypto], [mbedtls_entropy_func],, - [AC_MSG_ERROR([mbed tls support requires the mbedcrypto library -- libmbedcrypto0 suggested],1)]) - AC_CHECK_LIB([mbedx509], [mbedtls_pk_init],, - [AC_MSG_ERROR([mbed tls support requires the mbedx509 library -- libmbedx509-0 suggested],1)]) - elif test "x${with_ssl}" = xpolarssl ; then - AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding]) - AC_CHECK_LIB([polarssl],[ssl_init], , AC_MSG_ERROR(PolarSSL is selected but the library cannot be found and is deprecated. Consider selecting mbed TLS instead using --with-ssl=mbedtls.)) - else - AC_MSG_ERROR(unknown option "${with_ssl}"." Please choose with "openssl", "mbedtls" or "polarssl") - fi -], ) + AC_CHECK_LIB([mbedcrypto], [mbedtls_entropy_func],, + [AC_MSG_ERROR([mbed tls support requires the mbedcrypto library -- libmbedcrypto0 suggested],1)]) + AC_CHECK_LIB([mbedx509], [mbedtls_pk_init],, + [AC_MSG_ERROR([mbed tls support requires the mbedx509 library -- libmbedx509-0 suggested],1)]) +elif test "x${with_ssl}" = xpolarssl ; then + AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding]) + AC_CHECK_LIB([polarssl],[ssl_init], , AC_MSG_ERROR(PolarSSL is selected but the library cannot be found and is deprecated. Consider selecting mbed TLS instead using --with-ssl=mbedtls.)) +else + AC_MSG_ERROR(specify one of --with-ssl=openssl or --with-ssl=mbedtls or --with-ssl=polarssl) +fi # Look for soxr flag -AC_ARG_WITH(soxr, [AS_HELP_STRING([--with-soxr],[choose libsoxr for high-quality interpolation])], [ + +AC_ARG_WITH(soxr, [AS_HELP_STRING([--with-soxr],[choose libsoxr for high-quality interpolation])]) +if test "x$with_soxr" = "xyes" ; then AC_DEFINE([CONFIG_SOXR], 1, [Include support for using the SoX Resampler library for interpolation]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -200,31 +206,33 @@ AC_ARG_WITH(soxr, [AS_HELP_STRING([--with-soxr],[choose libsoxr for high-quality AC_CHECK_LIB([soxr],[soxr_create], , AC_MSG_ERROR(soxr support requires the libsoxr library -- libsoxr-dev suggested!)) fi fi -], ) +fi # Look for metadata flag and resolve it further down the script -AC_ARG_WITH(metadata,[AS_HELP_STRING([--with-metadata],[include support for a metadata feed])], [REQUESTED_METADATA=1], ) +AC_ARG_WITH(metadata,[AS_HELP_STRING([--with-metadata],[include support for a metadata feed])]) # What follows is a bit messy, because if the relevant library is requested, a compiler flag is defined, a file is included in the compilation # and the relevant link files are added. # Look for avahi flag -AC_ARG_WITH(avahi, [AS_HELP_STRING([--with-avahi],[choose Avahi-based mDNS support])], [ - REQUESTED_AVAHI=1 +AC_ARG_WITH(avahi, [AS_HELP_STRING([--with-avahi],[choose Avahi-based mDNS support])]) +if test "x$with_avahi" = "xyes" ; then AC_DEFINE([CONFIG_AVAHI], 1, [Needed by the compiler.]) AC_CHECK_LIB([avahi-client], [avahi_client_new], , AC_MSG_ERROR(Avahi support requires the avahi-client library!)) - AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!))]) -AM_CONDITIONAL([USE_AVAHI], [test "x$REQUESTED_AVAHI" = "x1"]) + AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!)) +fi +AM_CONDITIONAL([USE_AVAHI], [test "x$with_avahi" = "xyes"]) # Look for tinysvcmdns flag -AC_ARG_WITH(tinysvcmdns, [AS_HELP_STRING([--with-tinysvcmdns],[choose tinysvcmdns-based mDNS support])], [ - REQUESTED_TINYSVCMDNS=1 - AC_DEFINE([CONFIG_TINYSVCMDNS], 1, [Needed by the compiler.])], ) -AM_CONDITIONAL([USE_TINYSVCMDNS], [test "x$REQUESTED_TINYSVCMDNS" = "x1"]) +AC_ARG_WITH(tinysvcmdns, [AS_HELP_STRING([--with-tinysvcmdns],[choose tinysvcmdns-based mDNS support])]) +if test "x$with_tinysvcmdns" = "xyes" ; then + AC_DEFINE([CONFIG_TINYSVCMDNS], 1, [Include TinySVCmDNS-based mDNS support]) +fi +AM_CONDITIONAL([USE_TINYSVCMDNS], [test "x$with_tinysvcmdns" = "xyes"]) # Look for ALSA flag -AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa],[choose ALSA API support (GNU/Linux only)])], [ - REQUESTED_ALSA=1 +AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa],[choose ALSA API support (GNU/Linux only)])]) +if test "x$with_alsa" = "xyes" ; then AC_DEFINE([CONFIG_ALSA], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -232,12 +240,13 @@ AC_ARG_WITH(alsa, [AS_HELP_STRING([--with-alsa],[choose ALSA API support (GNU/Li [LIBS="${alsa_LIBS} ${LIBS}"]) else AC_CHECK_LIB([asound], [snd_pcm_open], , AC_MSG_ERROR(ALSA support requires the asound library!)) - fi ]) -AM_CONDITIONAL([USE_ALSA], [test "x$REQUESTED_ALSA" = "x1"]) + fi +fi +AM_CONDITIONAL([USE_ALSA], [test "x$with_alsa" = "xyes"]) # Look for jack flag -AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack],[include a Jack Audio Connection Kit (jack) backend])], [ - REQUESTED_JACK=1 +AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack],[include a Jack Audio Connection Kit (jack) backend])]) +if test "x$with_jack" = "xyes" ; then AC_DEFINE([CONFIG_JACK], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -246,33 +255,37 @@ AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack],[include a Jack Audio Connection [AC_MSG_ERROR(Jack Audio Connection Kit support requires the jack library -- libjack-dev suggested!)]) else AC_CHECK_LIB([jack], [jack_client_open], , AC_MSG_ERROR(Jack Audio Connection Kit support requires the jack library -- libjack-dev suggested!)) - fi ]) -AM_CONDITIONAL([USE_JACK], [test "x$REQUESTED_JACK" = "x1"]) + fi +fi +AM_CONDITIONAL([USE_JACK], [test "x$with_jack" = "xyes"]) # Look for SNDIO flag -AC_ARG_WITH(sndio, [AS_HELP_STRING([--with-sndio],[choose SNDIO API support])], [ - REQUESTED_SNDIO=1 +AC_ARG_WITH(sndio, [AS_HELP_STRING([--with-sndio],[choose SNDIO API support])]) +if test "x$with_sndio" = "xyes" ; then AC_DEFINE([CONFIG_SNDIO], 1, [Needed by the compiler.]) - AC_CHECK_LIB([sndio], [sio_open], , AC_MSG_ERROR(SNDIO support requires the sndio library -- libsndio-dev suggested))], ) -AM_CONDITIONAL([USE_SNDIO], [test "x$REQUESTED_SNDIO" = "x1"]) + AC_CHECK_LIB([sndio], [sio_open], , AC_MSG_ERROR(SNDIO support requires the sndio library -- libsndio-dev suggested)) +fi +AM_CONDITIONAL([USE_SNDIO], [test "x$with_sndio" = "xyes"]) # Look for AO flag -AC_ARG_WITH(ao, [AS_HELP_STRING([--with-ao],[choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!])], [ - REQUESTED_AO=1 +AC_ARG_WITH(ao, [AS_HELP_STRING([--with-ao],[choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!])]) +if test "x$with_ao" = "xyes" ; then AC_DEFINE([CONFIG_AO], 1, [Needed by the compiler.]) - AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested))], ) -AM_CONDITIONAL([USE_AO], [test "x$REQUESTED_AO" = "x1"]) + AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested)) +fi +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.])], [ - REQUESTED_SOUNDIO=1 - AC_DEFINE([CONFIG_SOUNDIO], 1, [Needed by the compiler.]) - AC_CHECK_LIB([soundio], [soundio_create], , AC_MSG_ERROR(soundio support requires the soundio library -- libsoundio-dev suggested))], ) -AM_CONDITIONAL([USE_SOUNDIO], [test "x$REQUESTED_SOUNDIO" = "x1"]) +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)) +fi +AM_CONDITIONAL([USE_SOUNDIO], [test "x$with_soundio" = "xyes"]) # Look for pulseaudio flag -AC_ARG_WITH(pa, [AS_HELP_STRING([--with-pa],[choose PulseAudio support.])], [ - REQUESTED_PA=1 +AC_ARG_WITH(pa, [AS_HELP_STRING([--with-pa],[choose PulseAudio support.])]) +if test "x$with_pa" = "xyes" ; then AC_DEFINE([CONFIG_PA], 1, [Needed by the compiler.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( @@ -281,8 +294,9 @@ AC_ARG_WITH(pa, [AS_HELP_STRING([--with-pa],[choose PulseAudio support.])], [ else AC_CHECK_LIB([pulse-simple], [pa_simple_new], , AC_MSG_ERROR(PulseAudio support requires the libpulse library!)) AC_CHECK_LIB([pulse], [pa_stream_peek], , AC_MSG_ERROR(PulseAudio support requires the libpulse-dev library.)) - fi ]) -AM_CONDITIONAL([USE_PA], [test "x$REQUESTED_PA" = "x1"]) + fi +fi +AM_CONDITIONAL([USE_PA], [test "x$with_pa" = "xyes"]) # Look for Convolution flag AC_ARG_WITH(convolution, [AS_HELP_STRING([--with-convolution],[choose audio DSP convolution support])], [ @@ -337,7 +351,7 @@ AC_ARG_WITH(mqtt-client, [AS_HELP_STRING([--with-mqtt-client],[include a client ],) AM_CONDITIONAL([USE_MQTT], [test "x$REQUESTED_MQTT" = "x1"]) -if test "x$REQUESTED_MQTT" = "x1" && test "x$REQUESTED_AVAHI" != "x1"; then +if test "x$REQUESTED_MQTT" = "x1" && test "x$with_avahi" = "xyes" ; then AC_MSG_WARN([MQTT needs Avahi to allow remote control functionality. At present, only metadata publishing will be supported]) fi @@ -349,10 +363,10 @@ fi AM_CONDITIONAL([USE_METADATA_HUB], [test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1"]) AM_CONDITIONAL([USE_DACP_CLIENT], [test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1"]) -if test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1" || test "x$REQUESTED_METADATA" = "x1"; then +if test "x$REQUESTED_EXTENDED_METADATA_SUPPORT" = "x1" || test "x$with_metadata" = "xyes" ; then AC_DEFINE([CONFIG_METADATA], 1, [Needed by the compiler.]) fi -AM_CONDITIONAL([USE_METADATA], [test "x$REQUESTED_METADATA" = "x1"]) +AM_CONDITIONAL([USE_METADATA], [test "x$with_metadata" = "xyes"]) if test "x${with_systemd}" = xyes ; then # Find systemd unit dir -- cgit v1.2.3 From ca40780a76996774f4a3a5588b0db4bcf6a89329 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 1 Jan 2021 19:16:27 +0000 Subject: Add some simple configuration tests. --- .gitignore | 1 + configure.ac | 96 ++++++++++++----------- tests/configure_test.sh | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 47 deletions(-) create mode 100644 tests/configure_test.sh diff --git a/.gitignore b/.gitignore index 6f76aec..9d13084 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /shairport-sync-mpris-test-client *.o +*.log /*~ *.xml~ /config.mk diff --git a/configure.ac b/configure.ac index dd59c22..341bb38 100644 --- a/configure.ac +++ b/configure.ac @@ -66,21 +66,21 @@ fi AC_ARG_WITH([dummy],[AS_HELP_STRING([--with-dummy],[include the dummy audio back end])]) if test "x$with_dummy" = "xyes" ; then AC_MSG_RESULT(include the dummy audio back end) - AC_DEFINE([CONFIG_DUMMY], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_DUMMY], 1, [Include a fake audio backend.]) fi AM_CONDITIONAL([USE_DUMMY], [test "x$with_dummy" = "xyes" ]) AC_ARG_WITH([stdout],[AS_HELP_STRING([--with-stdout],[include the stdout audio back end])]) if test "x$with_stdout" = "xyes" ; then AC_MSG_RESULT(include the stdout audio back end) - AC_DEFINE([CONFIG_STDOUT], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_STDOUT], 1, [Include an audio backend to output to standard output (stdout).]) fi AM_CONDITIONAL([USE_STDOUT], [test "x$with_stdout" = "xyes"]) AC_ARG_WITH([pipe],[AS_HELP_STRING([--with-pipe],[include the pipe audio back end])]) if test "x$with_pipe" = "xyes" ; then AC_MSG_RESULT(include the pipe audio back end) - AC_DEFINE([CONFIG_PIPE], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_PIPE], 1, [Include an audio backend to output to a unix pipe.]) fi AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ]) @@ -148,7 +148,7 @@ AM_CONDITIONAL([USE_CUSTOMPIDDIR], [ test "x${with_piddir}" != "x" ]) # Look for libdaemon AC_ARG_WITH(libdaemon,[AS_HELP_STRING([--with-libdaemon],[include support for daemonising in non-systemd systems])]) if test "x$with_libdaemon" = "x1"; then - AC_DEFINE([CONFIG_LIBDAEMON], 1, [Include libdaemon]) + AC_DEFINE([CONFIG_LIBDAEMON], 1, [Include libdaemon for daemonising in non-systemd systems]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [DAEMON], [libdaemon], @@ -217,7 +217,7 @@ AC_ARG_WITH(metadata,[AS_HELP_STRING([--with-metadata],[include support for a me # Look for avahi flag AC_ARG_WITH(avahi, [AS_HELP_STRING([--with-avahi],[choose Avahi-based mDNS support])]) if test "x$with_avahi" = "xyes" ; then - AC_DEFINE([CONFIG_AVAHI], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_AVAHI], 1, [Include Avahi-based mDNS support.]) AC_CHECK_LIB([avahi-client], [avahi_client_new], , AC_MSG_ERROR(Avahi support requires the avahi-client library!)) AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!)) fi @@ -247,7 +247,7 @@ AM_CONDITIONAL([USE_ALSA], [test "x$with_alsa" = "xyes"]) # Look for jack flag AC_ARG_WITH(jack, [AS_HELP_STRING([--with-jack],[include a Jack Audio Connection Kit (jack) backend])]) if test "x$with_jack" = "xyes" ; then - AC_DEFINE([CONFIG_JACK], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_JACK], 1, [Include a Jack Audio Connection Kit (jack) audio backend]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [jack], [jack], @@ -262,7 +262,7 @@ AM_CONDITIONAL([USE_JACK], [test "x$with_jack" = "xyes"]) # Look for SNDIO flag AC_ARG_WITH(sndio, [AS_HELP_STRING([--with-sndio],[choose SNDIO API support])]) if test "x$with_sndio" = "xyes" ; then - AC_DEFINE([CONFIG_SNDIO], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_SNDIO], 1, [Include a sndio-compatible audio backend.]) AC_CHECK_LIB([sndio], [sio_open], , AC_MSG_ERROR(SNDIO support requires the sndio library -- libsndio-dev suggested)) fi AM_CONDITIONAL([USE_SNDIO], [test "x$with_sndio" = "xyes"]) @@ -270,7 +270,7 @@ AM_CONDITIONAL([USE_SNDIO], [test "x$with_sndio" = "xyes"]) # Look for AO flag AC_ARG_WITH(ao, [AS_HELP_STRING([--with-ao],[choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!])]) if test "x$with_ao" = "xyes" ; then - AC_DEFINE([CONFIG_AO], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_AO], 1, [Include an AO-compatible audio backend.]) AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested)) fi AM_CONDITIONAL([USE_AO], [test "x$with_ao" = "xyes"]) @@ -286,7 +286,7 @@ AM_CONDITIONAL([USE_SOUNDIO], [test "x$with_soundio" = "xyes"]) # Look for pulseaudio flag AC_ARG_WITH(pa, [AS_HELP_STRING([--with-pa],[choose PulseAudio support.])]) if test "x$with_pa" = "xyes" ; then - AC_DEFINE([CONFIG_PA], 1, [Needed by the compiler.]) + AC_DEFINE([CONFIG_PA], 1, [Include PulseAudio support.]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [PULSEAUDIO], [libpulse >= 0.9.2], @@ -299,63 +299,65 @@ fi AM_CONDITIONAL([USE_PA], [test "x$with_pa" = "xyes"]) # Look for Convolution flag -AC_ARG_WITH(convolution, [AS_HELP_STRING([--with-convolution],[choose audio DSP convolution support])], [ - REQUESTED_CONVOLUTION=1 +AC_ARG_WITH(convolution, [AS_HELP_STRING([--with-convolution],[choose audio DSP convolution support])]) +if test "x$with_convolution" = "xyes" ; then AM_INIT_AUTOMAKE([subdir-objects]) - AC_DEFINE([CONFIG_CONVOLUTION], 1, [Needed by the compiler.]) - AC_CHECK_LIB([sndfile], [sf_open], , AC_MSG_ERROR(Convolution support requires the sndfile library -- libsndfile1-dev suggested!))], ) -AM_CONDITIONAL([USE_CONVOLUTION], [test "x$REQUESTED_CONVOLUTION" = "x1"]) + AC_DEFINE([CONFIG_CONVOLUTION], 1, [Include audio DSP convolution support.]) + AC_CHECK_LIB([sndfile], [sf_open], , AC_MSG_ERROR(Convolution support requires the sndfile library -- libsndfile1-dev suggested!)) +fi +AM_CONDITIONAL([USE_CONVOLUTION], [test "x$with_convolution" = "xyes"]) # Look for dns_sd flag -AC_ARG_WITH(dns_sd, [AS_HELP_STRING([--with-dns_sd],[choose dns_sd mDNS support])], [ - REQUESTED_DNS_SD=1 - AC_DEFINE([CONFIG_DNS_SD], 1, [Needed by the compiler.]) - AC_SEARCH_LIBS([DNSServiceRefDeallocate], [dns_sd], , AC_MSG_ERROR(dns_sd support requires the dns_sd library!))], ) -AM_CONDITIONAL([USE_DNS_SD], [test "x$REQUESTED_DNS_SD" = "x1"]) +AC_ARG_WITH(dns_sd, [AS_HELP_STRING([--with-dns_sd],[choose dns_sd mDNS support])]) +if test "x$with_dns_sd" = "xyes" ; then + AC_DEFINE([CONFIG_DNS_SD], 1, [Include dns_sd mDNS support.]) + AC_SEARCH_LIBS([DNSServiceRefDeallocate], [dns_sd], , [AC_MSG_ERROR(dns_sd support requires the dns_sd library!)]) +fi +AM_CONDITIONAL([USE_DNS_SD], [test "x$with_dns_sd" = "xyes"]) # Look for dbus flag -AC_ARG_WITH(dbus-interface, [AS_HELP_STRING([--with-dbus-interface],[include support for the native Shairport Sync D-Bus interface])], [ - AC_DEFINE([CONFIG_DBUS_INTERFACE], 1, [Include support for the native Shairport Sync D-Bus interface]) - REQUESTED_DBUS=1 - PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(D-Bus messaging support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) - ], ) -AM_CONDITIONAL([USE_DBUS], [test "x$REQUESTED_DBUS" = "x1"]) +AC_ARG_WITH(dbus-interface, [AS_HELP_STRING([--with-dbus-interface],[include support for the native Shairport Sync D-Bus interface])]) +if test "x$with_dbus_interface" = "xyes" ; then + AC_DEFINE([CONFIG_DBUS_INTERFACE], 1, [Support the native Shairport Sync D-Bus interface]) + # remember to include glib, below +fi +AM_CONDITIONAL([USE_DBUS], [test "x$with_dbus_interface" = "xyes"]) # Look for dbus test client flag -AC_ARG_WITH(dbus-test-client, [AS_HELP_STRING([--with-dbus-test-client],[compile a separare D-Bus test client])], [ - REQUESTED_DBUS_CLIENT=1 - PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(D-Bus client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) - ], ) -AM_CONDITIONAL([USE_DBUS_CLIENT], [test "x$REQUESTED_DBUS_CLIENT" = "x1"]) +AC_ARG_WITH(dbus-test-client, [AS_HELP_STRING([--with-dbus-test-client],[compile a D-Bus test client application])]) +# remember to include glib, below +AM_CONDITIONAL([USE_DBUS_CLIENT], [test "x$with_dbus_test_client" = "xyes"]) # Look for mpris flag -AC_ARG_WITH(mpris-interface, [AS_HELP_STRING([--with-mpris-interface],[include support for a D-Bus interface conforming to the MPRIS standard])], [ - AC_DEFINE([CONFIG_MPRIS_INTERFACE], 1, [Include support for a D-Bus interface conforming to the MPRIS standard]) - REQUESTED_MPRIS=1 - PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(MPRIS messaging support for mpris requires the glib 2.0 library -- libglib2.0-dev suggested!)]) - ], ) -AM_CONDITIONAL([USE_MPRIS], [test "x$REQUESTED_MPRIS" = "x1"]) +AC_ARG_WITH(mpris-interface, [AS_HELP_STRING([--with-mpris-interface],[include support for a D-Bus interface conforming to the MPRIS standard])]) +if test "x$with_mpris_interface" = "xyes" ; then + AC_DEFINE([CONFIG_MPRIS_INTERFACE], 1, [Support the MPRIS standard]) + # remember to include glib, below +fi +AM_CONDITIONAL([USE_MPRIS], [test "x$with_mpris_interface" = "xyes"]) # Look for mpris test client flag -AC_ARG_WITH(mpris-test-client, [AS_HELP_STRING([--with-mpris-test-client],[compile an separate MPRIS test client])], [ - REQUESTED_MPRIS_CLIENT=1 +AC_ARG_WITH(mpris-test-client, [AS_HELP_STRING([--with-mpris-test-client],[compile an MPRIS test client application])]) +# remember to include glib, below +AM_CONDITIONAL([USE_MPRIS_CLIENT], [test "x$with_mpris_test_client" = "xyes"]) + +if test "x$with_mpris_test_client" = "xyes" || test "x$with_dbus_test_client" = "xyes" || test "x$with_mpris_interface" = "xyes" || test "x$with_dbus_interface" = "xyes" ; then PKG_CHECK_MODULES([glib], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${glib_CFLAGS} ${CFLAGS}" LIBS="${glib_LIBS} ${LIBS}"],[AC_MSG_ERROR(MPRIS client support requires the glib 2.0 library -- libglib2.0-dev suggested!)]) - ], ) -AM_CONDITIONAL([USE_MPRIS_CLIENT], [test "x$REQUESTED_MPRIS_CLIENT" = "x1"]) +fi # Look for mqtt flag -AC_ARG_WITH(mqtt-client, [AS_HELP_STRING([--with-mqtt-client],[include a client for MQTT -- the Message Queuing Telemetry Transport protocol])], [ +AC_ARG_WITH(mqtt-client, [AS_HELP_STRING([--with-mqtt-client],[include a client for MQTT -- the Message Queuing Telemetry Transport protocol])]) +if test "x$with_mqtt_client" = "xyes" ; then AC_DEFINE([CONFIG_MQTT], 1, [Include a client for MQTT, the Message Queuing Telemetry Transport protocol]) - REQUESTED_MQTT=1 AC_CHECK_LIB([mosquitto], [mosquitto_lib_init], , AC_MSG_ERROR(MQTT support requires the mosquitto library -- libmosquitto-dev suggested!)) - ],) -AM_CONDITIONAL([USE_MQTT], [test "x$REQUESTED_MQTT" = "x1"]) +fi +AM_CONDITIONAL([USE_MQTT], [test "x$with_mqtt_client" = "xyes"]) -if test "x$REQUESTED_MQTT" = "x1" && test "x$with_avahi" = "xyes" ; then - AC_MSG_WARN([MQTT needs Avahi to allow remote control functionality. At present, only metadata publishing will be supported]) +if test "x$with_mqtt_client" = "xyes" && test "x$with_avahi" != "xyes" ; then + AC_MSG_WARN([MQTT needs Avahi for remote control functionality. With the current configuration settings, only metadata publishing will be supported.]) fi -if test "x$REQUESTED_MPRIS" = "x1" || test "x$REQUESTED_DBUS" = "x1" || test "x$REQUESTED_MQTT" = "x1"; then +if test "x$with_mpris_interface" = "xyes" || test "x$with_dbus_interface" = "xyes" || test "x$with_mqtt_client" = "xyes" ; then REQUESTED_EXTENDED_METADATA_SUPPORT=1 AC_DEFINE([CONFIG_METADATA_HUB], 1, [Needed by the compiler.]) AC_DEFINE([CONFIG_DACP_CLIENT], 1, [Needed by the compiler.]) diff --git a/tests/configure_test.sh b/tests/configure_test.sh new file mode 100644 index 0000000..f1dccf9 --- /dev/null +++ b/tests/configure_test.sh @@ -0,0 +1,201 @@ +#!/bin/sh + +# These tests check that the requested configuration can be made and can be built +# In many cases it will check that the Shairport Sync configuration string +# contains or omits the relevant string +# If doesn't check for the presence or absence of products except +# when it checks for the configuration string + +# At present, it is Linux-only. +check_configuration_string_includes() +{ + echo -n " checking configuration string includes \"$1\"..." + ./shairport-sync -V | grep -q $1 + if [ "$?" -eq "1" ] ; then + echo "\nError: \"$1\" not included in configuration string" + exit 1 + fi + echo -n "ok" +} + +check_configuration_string_excludes() +{ + echo -n " checking configuration string excludes \"$1\"..." + ./shairport-sync -V | grep -q $1 + if [ "$?" -eq "0" ] ; then + echo "\nError: \"$1\" is unexpectedly included in the configuration string" + exit 1 + fi + echo -n "ok" +} +check_for_success() +{ + if [ "$2" = "x" ] ; then + A2="" + else + A2="$2" + fi + if [ "$3" = "x" ] ; then + A3="" + else + A3="$3" + fi + if [ "$4" = "x" ] ; then + A4="" + else + A4="$4" + fi + if [ "$5" = "x" ] ; then + A5="" + else + A5=$5 + fi + if [ "$1" = "x" -o "$1" = "x$A2" ] ; then + TESTCOUNT="$(expr "$TESTCOUNT" '+' '1')" + echo -n "Checking \"$A2\": " + echo -n "configuring..." + echo "./configure $A3 $A2" > $LOGFILE + ./configure $A3 $A2 >> $LOGFILE 2>&1 + if [ "$?" -eq "0" ] ; then + echo -n "ok making..." + echo "make clean" >> $LOGFILE + make clean >> $LOGFILE 2>&1 + echo "make -j $((`nproc`*2))" >> $LOGFILE + make -j $((`nproc`*2)) >> $LOGFILE 2>&1 + if [ "$?" -ne "0" ] ; then + echo "\nError at build step with arg \"$A2\"." + exit 1 + fi + echo -n "ok" + else + echo "\nError at configure step with arg \"$A2\"." + exit 1 + fi + if [ "$A4" != "" ] ; then + check_configuration_string_includes $A4 + fi + if [ "$A5" != "" ] ; then + check_configuration_string_excludes $A5 + fi + echo "." + fi +} + +check_for_configuration_fail() +{ + if [ "$1" = "x" -o "$1" = "x$2" ] ; then + echo -n "Checking \"$2\" fails during configuration... " + TESTCOUNT="$(expr "$TESTCOUNT" '+' '1')" + ./configure $3 $2 > $LOGFILE 2>&1 + if [ "$?" -eq "0" ] ; then + echo "\nError: configuration did not fail with arg \"$2\"." + exit 1 + fi + echo " done." + fi + return 0 +} + +echo -n "Preparing..." +LOGFILE=configure_test.log +CWD=`pwd` +cd .. +autoreconf -fi > $LOGFILE 2>&1 +if [ "$?" -ne "0" ] ; then + echo "\Error running \"autoreconf -fi\"" + exit 1 +fi +echo "ok." +TESTCOUNT=0 +check_for_success x$1 --with-pkg-config --with-ssl=mbedtls +check_for_success x$1 --with-ssl=openssl x OpenSSL +check_for_success x$1 --with-ssl=mbedtls x mbedTLS +check_for_success x$1 --with-ssl=polarssl x PolarSSL +check_for_configuration_fail x$1 --with-ssl +check_for_configuration_fail x$1 --without-ssl=openssl +check_for_configuration_fail x$1 --without-ssl=mbedtls +check_for_configuration_fail x$1 --without-ssl=polarssl +check_for_configuration_fail x$1 +check_for_success x$1 --with-alsa --with-ssl=mbedtls ALSA +check_for_success x$1 --without-alsa --with-ssl=mbedtls x ALSA + +check_for_success x$1 --with-dummy --with-ssl=mbedtls dummy +check_for_success x$1 --without-dummy --with-ssl=mbedtls x dummy + +check_for_success x$1 --with-stdout --with-ssl=mbedtls stdout +check_for_success x$1 --without-stdout --with-ssl=mbedtls x stdout + +check_for_success x$1 --with-pipe --with-ssl=mbedtls pipe +check_for_success x$1 --without-pipe --with-ssl=mbedtls x pipe + +check_for_success x$1 --with-external-mdns --with-ssl=mbedtls external_mdns +check_for_success x$1 --without-external-mdns --with-ssl=mbedtls x external_mdns + +check_for_success x$1 --with-apple-alac --with-ssl=mbedtls alac +check_for_success x$1 --without-apple-alac --with-ssl=mbedtls x alac + +check_for_success x$1 --with-piddir=/var --with-ssl=mbedtls +check_for_success x$1 --without-piddir --with-ssl=mbedtls + +check_for_success x$1 --with-libdaemon --with-ssl=mbedtls +check_for_success x$1 --without-libdaemon --with-ssl=mbedtls + +check_for_success x$1 --with-soxr --with-ssl=mbedtls soxr +check_for_success x$1 --without-soxr --with-ssl=mbedtls x soxr + +check_for_success x$1 --with-metadata --with-ssl=mbedtls metadata +check_for_success x$1 --without-metadata --with-ssl=mbedtls x metadata + +check_for_success x$1 --with-avahi --with-ssl=mbedtls Avahi +check_for_success x$1 --without-avahi --with-ssl=mbedtls x Avahi + +check_for_success x$1 --with-tinysvcmdns --with-ssl=mbedtls tinysvcmdns +check_for_success x$1 --without-tinysvcmdns --with-ssl=mbedtls x tinysvcmdns + +check_for_success x$1 --with-jack --with-ssl=mbedtls jack +check_for_success x$1 --without-jack --with-ssl=mbedtls x jack + +check_for_success x$1 --with-sndio --with-ssl=mbedtls sndio +check_for_success x$1 --without-sndio --with-ssl=mbedtls x sndio + +check_for_success x$1 --with-ao --with-ssl=mbedtls ao +check_for_success x$1 --without-ao --with-ssl=mbedtls x ao + +# the following is disabled because there is no soundio library for Raspberry OS +#check_for_success x$1 --with-soundio --with-ssl=mbedtls soundio +check_for_success x$1 --without-soundio --with-ssl=mbedtls x soundio + +check_for_success x$1 --with-pa --with-ssl=mbedtls pa +check_for_success x$1 --without-pa --with-ssl=mbedtls x pa + +check_for_success x$1 --with-convolution --with-ssl=mbedtls convolution +check_for_success x$1 --without-convolution --with-ssl=mbedtls x convolution + +check_for_success x$1 --with-dns_sd --with-ssl=mbedtls dns_sd +check_for_success x$1 --without-dns_sd --with-ssl=mbedtls x dns_sd + +check_for_success x$1 --with-dbus-interface --with-ssl=mbedtls metadata-dbus +check_for_success x$1 --without-dbus-interface --with-ssl=mbedtls x dbus + +check_for_success x$1 --with-dbus-test-client --with-ssl=mbedtls +check_for_success x$1 --without-dbus-test-client --with-ssl=mbedtls + +check_for_success x$1 --with-mpris-interface --with-ssl=mbedtls metadata-mpris +check_for_success x$1 --without-mpris-interface --with-ssl=mbedtls x mpris + +check_for_success x$1 --with-mpris-test-client --with-ssl=mbedtls +check_for_success x$1 --without-mpris-test-client --with-ssl=mbedtls + +check_for_success x$1 --with-mqtt-client --with-ssl=mbedtls metadata-mqtt +check_for_success x$1 --without-mqtt-client --with-ssl=mbedtls x mqtt + +check_for_success x$1 --with-configfiles '--sysconfdir=/etc --with-alsa --with-soxr --with-avahi --with-ssl=openssl --with-systemd' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc +check_for_success x$1 --without-configfiles '--sysconfdir=/etc --with-alsa --with-soxr --with-avahi --with-ssl=openssl --with-systemd' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc + +check_for_success x$1 --with-systemd '--sysconfdir=/etc --with-alsa --with-soxr --with-avahi --with-ssl=openssl' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc +check_for_success x$1 --without-systemd '--sysconfdir=/etc --with-alsa --with-soxr --with-avahi --with-ssl=openssl' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc +check_for_success x$1 --with-systemv '--sysconfdir=/etc --with-libdaemon --with-alsa --with-soxr --with-avahi --with-ssl=openssl' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc +check_for_success x$1 --without-systemv '--sysconfdir=/etc --with-libdaemon --with-alsa --with-soxr --with-avahi --with-ssl=openssl' OpenSSL-Avahi-ALSA-soxr-sysconfdir:/etc + +cd $CWD +echo "$TESTCOUNT tests completed." \ No newline at end of file -- cgit v1.2.3 From 48295d48b0044eb1dc95daedce6ae6a1f83d0b04 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 1 Jan 2021 19:26:13 +0000 Subject: Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 63422e8..618edea 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.8d4 +==== +**Bug Fixes** +* Fix a fault in the configuration script `configure.ac`. The fault was that a `--without-*` configuration argument was being misunderstood and interpreted partly as a `--with-*` argument. Thanks to [David Racine](https://github.com/bassdr) for the [report](https://github.com/mikebrady/shairport-sync/issues/1123). + Version 3.3.8d3 ==== **Enhancements** -- cgit v1.2.3 From 534ce8cd0689ec02c529c388636e8cd4033739e7 Mon Sep 17 00:00:00 2001 From: Emanuel Haupt Date: Fri, 1 Jan 2021 22:14:09 +0100 Subject: Add packaging status --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5f92027..1934009 100644 --- a/README.md +++ b/README.md @@ -632,3 +632,7 @@ MQTT --------------- Please refer to the [MQTT INFO](https://github.com/mikebrady/shairport-sync/blob/master/MQTT.md) page for additional info on building, configuring and using MQTT to interface shairport-sync with common home automation systems (contributed by users). +Packaging status +---------------- + +[![Packaging status](https://repology.org/badge/vertical-allrepos/shairport-sync.svg)](https://repology.org/project/shairport-sync/versions) -- cgit v1.2.3 From e189710499e97c72c0db3092022a206d32114fe7 Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Thu, 7 Jan 2021 01:04:16 +0000 Subject: Fix typos of "daemon" --- shairport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shairport.c b/shairport.c index ba99523..a32216d 100644 --- a/shairport.c +++ b/shairport.c @@ -1322,7 +1322,7 @@ void exit_function() { #ifdef CONFIG_LIBDAEMON if ((this_is_the_daemon_process) || (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not - // actually deamonised at all + // actually daemonised at all #endif debug(2, "exit function called..."); /* @@ -1617,7 +1617,7 @@ int main(int argc, char **argv) { debug(2, "killed the %s daemon.", config.appName); else daemon_log(LOG_WARNING, - "killed the %s deamon, but cannot remove old PID file: \"%s\", errno %u.", + "killed the %s daemon, but cannot remove old PID file: \"%s\", errno %u.", config.appName, strerror(errno), errno); } return ret < 0 ? 1 : 0; -- cgit v1.2.3 From cfc6612e7536f438482c7b7723289452ac82ee5d Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 24 Jan 2021 17:34:33 +0000 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0554aee..5f67ac5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,7 @@ Version 3.3.8 * Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. * Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. * Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. +* Fix the configure.ac file so that `--without-` configuration options are not interpreted as `--with-` options instead! Thanks to [David Racine](https://github.com/bassdr) for the report. Version 3.3.7 ==== -- cgit v1.2.3 From e5362c770ddb0adfa351da56561d35e22458439a Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 30 Jan 2021 11:39:56 +0000 Subject: Update INSTALL.md --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 964be27..4748b4f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -33,8 +33,8 @@ Remove it as follows: ``` Do this until no more copies of `shairport-sync` are found. -### Remove Old Startup Scripts -You should also remove the startup script files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service` and `/etc/init.d/shairport-sync` if they exist – new ones will be installed if necessary. +### Remove Old Startup and Service Scripts +You should also remove the startup script and service definition files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service`, `/etc/init.d/shairport-sync`, `/etc/dbus-1/system.d/shairport-sync-dbus.conf` and `/etc/dbus-1/system.d/shairport-sync-mpris.conf` if they exist – new ones will be installed if necessary. ### Reboot after Cleaning Up If you removed any installations of Shairport Sync or any of its startup script files in the last two steps, you should reboot. -- cgit v1.2.3 From be24f4747316170bad0e4d7332c036bce95ba25c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 30 Jan 2021 11:40:49 +0000 Subject: Update UPDATING.md --- UPDATING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPDATING.md b/UPDATING.md index 5803e27..6988528 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -29,8 +29,8 @@ Remove it as follows: ``` Do this until no more copies of `shairport-sync` are found. This is especially important if you are building Shairport Sync over an old version that was installed from packages, as the built application will be installed in a different directory to the packaged installation. -### Remove Old Startup Scripts -You should also remove the startup script files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service` and `/etc/init.d/shairport-sync` if they exist – new ones will be installed if necessary. As with the previous section, this is especially important if you are building Shairport Sync over an old version that was installed from packages, as the scripts for the built application may be different to those of the packaged version. If they are left in place, bad things can happen. +### Remove Old Startup and Service Scripts +You should also remove the startup script and service definition files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service`, `/etc/init.d/shairport-sync`, `/etc/dbus-1/system.d/shairport-sync-dbus.conf` and `/etc/dbus-1/system.d/shairport-sync-mpris.conf` if they exist – new ones will be installed if necessary. As with the previous section, this is especially important if you are building Shairport Sync over an old version that was installed from packages, as the scripts for the built application may be different to those of the packaged version. If they are left in place, bad things can happen. ### Reboot after Cleaning Up If you removed any installations of Shairport Sync or any of its startup script files in the last two steps, you should reboot. -- cgit v1.2.3 From 1d6bd41db83553c5d0c3a88da81610030b83623c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 30 Jan 2021 11:42:02 +0000 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45534d9..2a49d1c 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ If you wish to build and install the latest version of Shairport Sync on Debian, You should check to see if `shairport-sync` is already installed – you can use the command `$ which shairport-sync` to find where it is located, if installed. If it is installed you should delete it – you may need superuser privileges. After deleting, check again in case further copies are installed elsewhere. -You should also remove the startup script files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service` and `/etc/init.d/shairport-sync` if they exist – new ones will be installed in necessary. +You should also remove the startup script and service definition files `/etc/systemd/system/shairport-sync.service`, `/lib/systemd/system/shairport-sync.service`, `/etc/init.d/shairport-sync`, `/etc/dbus-1/system.d/shairport-sync-dbus.conf` and `/etc/dbus-1/system.d/shairport-sync-mpris.conf` if they exist – new ones will be installed if necessary. If you removed any installations of Shairport Sync or any of its startup script files, you should reboot. -- cgit v1.2.3 From b91eb443afebdc82e26e7a648c288f7b1d1237de Mon Sep 17 00:00:00 2001 From: GriffOn Yi Date: Tue, 2 Feb 2021 23:09:35 +0800 Subject: add gentoo guide --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2a49d1c..9f8d832 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ Note that the installation uses the libao library and so synchronisation is not **Fedora:** Please see the guide at [FEDORA.md](https://github.com/mikebrady/shairport-sync/blob/master/FEDORA.md). +**Gentoo:** Shairport Sync is available in griffon_overlay, please see the guide at [https://github.com/windfail/griffon_overlay/wiki/shairport-sync](https://github.com/windfail/griffon_overlay/wiki/shairport-sync). + **Cygwin:** Please see the guide at [CYGWIN.md](https://github.com/mikebrady/shairport-sync/blob/master/CYGWIN.md). Sincere thanks to all package contributors! -- cgit v1.2.3 From 030923b666e37b6509039ff9980fafec1f7238e3 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 22 Feb 2021 07:39:06 +0000 Subject: Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 4748b4f..85a2445 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,7 +17,7 @@ If you are using WiFi, you should turn off WiFi Power Management: ``` # iwconfig wlan0 power off ``` -WiFi Power Management will put the WiFi system in low-power mode when the WiFi system considered inactive, and in this mode it may not respond to events initiated from the network, such as AirPlay requests. Hence, WiFi Power Management should be turned off. (See [TROUBLESHOOTING.md](https://github.com/mikebrady/shairport-sync/blob/master/TROUBLESHOOTING.md#wifi-adapter-running-in-power-saving--low-power-mode) for more details.) +WiFi Power Management will put the WiFi system in low-power mode when the WiFi system is considered inactive, and in this mode it may not respond to events initiated from the network, such as AirPlay requests. Hence, WiFi Power Management should be turned off. (See [TROUBLESHOOTING.md](https://github.com/mikebrady/shairport-sync/blob/master/TROUBLESHOOTING.md#wifi-adapter-running-in-power-saving--low-power-mode) for more details.) Reboot the Pi. -- cgit v1.2.3 From 8b65069e634f9d9d4395b30538f8f5dee0e66d53 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 8 Mar 2021 17:48:41 +0000 Subject: Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9f8d832..e6f6d35 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ Shairport Sync may already be available as a package in your Linux distribution **Debian:** shairport-sync is in the Debian archive. +**Ansible Playbooks for CentOS 8 Stream on Raspberry Pi** A suite of Ansible playbooks and scripts to make deploying Shairport Sync easy on CentOS 8 Stream specifically on Raspberry Pi is available at https://github.com/p3ck/ansible-shairport. + **OpenWrt:** There is a Shairport Sync package in OpenWrt trunk. Also, there's an OpenWrt package at https://github.com/mikebrady/shairport-sync-for-openwrt, including one that builds back to Barrier Breaker. **Arch Linux:** Shairport Sync is available for x86_64 and i686 platforms in the Arch Linux Community Repository -- search for `shairport-sync`. See also https://www.archlinux.org/packages/. -- cgit v1.2.3 From a946296302a44f4da9ebf2e8741854dbf9977b25 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 8 Mar 2021 17:49:10 +0000 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6f6d35..15a04c7 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Shairport Sync may already be available as a package in your Linux distribution **Debian:** shairport-sync is in the Debian archive. -**Ansible Playbooks for CentOS 8 Stream on Raspberry Pi** A suite of Ansible playbooks and scripts to make deploying Shairport Sync easy on CentOS 8 Stream specifically on Raspberry Pi is available at https://github.com/p3ck/ansible-shairport. +**Ansible Playbooks for CentOS 8 Stream on Raspberry Pi:** A suite of Ansible playbooks and scripts to make deploying Shairport Sync easy on CentOS 8 Stream specifically on Raspberry Pi is available at https://github.com/p3ck/ansible-shairport. **OpenWrt:** There is a Shairport Sync package in OpenWrt trunk. Also, there's an OpenWrt package at https://github.com/mikebrady/shairport-sync-for-openwrt, including one that builds back to Barrier Breaker. -- cgit v1.2.3 From 15e72dfb3e689c6d26978641f5e735bab8aa44b6 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 2 Apr 2021 18:43:54 +0100 Subject: Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 15a04c7..38c7075 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ What else? * An [MPRIS](https://specifications.freedesktop.org/mpris-spec/2.2/) interface, partially complete and very functional, including access to metadata and artwork, and some limited remote control. * An interface to [MQTT](https://en.wikipedia.org/wiki/MQTT), an often-used protocol in home automation projects. * A native D-Bus interface, including access to metadata and artwork, some limited remote control and some system settings. +* Digital Signal Processing facilities -- please see the [DSP Wiki Page Guide](https://github.com/mikebrady/shairport-sync/wiki/Digital-Signal-Processing-with-Shairport-Sync). (Thanks to [Yann Pomarède](https://github.com/yannpom) for the code and to [Paul Wieland](https://github.com/PaulWieland) for the guide.) Heritage ------- -- cgit v1.2.3 From 6c628da897b6661e9aab1b620d103cbe1d5da789 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 26 Apr 2021 11:21:01 +0100 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5f67ac5..f744408 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -6,7 +6,9 @@ Version 3.3.8 * Documentation for the MQTT interface. Many thanks to [minix1234](https://github.com/minix1234)! **Bug Fixes** -* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any on-*** script failed. +* Fix a bug in the `alsa` back end. In the interval between checking that the alsa device handle was non-`NULL` and actually using it, the handle could be set to `NULL`. The interval between check and usage is now protected. +* Fix a bug in the `alsa` precision timing code. Thanks to [durwin99](https://github.com/durwin99), [Nicolas Da Mutten](https://github.com/cleverer), [mistakenideas](https://github.com/mistakenideas), [Ben Willmore](https://github.com/ben-willmore) and [giggywithit](https://github.com/giggywithit) for the [report](https://github.com/mikebrady/shairport-sync/issues/1158). +* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any `on-...` script failed. * Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. * Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. * Fix the configure.ac file so that `--without-` configuration options are not interpreted as `--with-` options instead! Thanks to [David Racine](https://github.com/bassdr) for the report. -- cgit v1.2.3 From b9854d3fdcfc66a0b593b39cbfd15480abf319cb Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 26 Apr 2021 11:23:13 +0100 Subject: Version 3.3.8 is 3.3.8rc7. --- audio_alsa.c | 79 ++++++++++++++++++++++------------ common.c | 2 +- configure.ac | 15 +++---- dacp.c | 8 ++-- rtsp.c | 100 ++++++++++++++++++++++---------------------- scripts/shairport-sync.conf | 1 + 6 files changed, 117 insertions(+), 88 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 69722d1..b464279 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1455,19 +1455,31 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, int ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status); if (ret == 0) { -// must be 1.1 or later to use snd_pcm_status_get_driver_htstamp -#if SND_LIB_MINOR == 0 snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp); -#else - snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &update_timestamp); + +/* +// must be 1.1 or later to use snd_pcm_status_get_driver_htstamp +#if SND_LIB_MINOR != 0 + snd_htimestamp_t driver_htstamp; + snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &driver_htstamp); + uint64_t driver_htstamp_ns = driver_htstamp.tv_sec; + driver_htstamp_ns = driver_htstamp_ns * 1000000000; + driver_htstamp_ns = driver_htstamp_ns + driver_htstamp.tv_nsec; + debug(1,"driver_htstamp: %f.", driver_htstamp_ns * 0.000000001); #endif +*/ *state = snd_pcm_status_get_state(alsa_snd_pcm_status); if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) { - uint64_t update_timestamp_ns = - update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec; + // uint64_t update_timestamp_ns = + // update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec; + + uint64_t update_timestamp_ns = update_timestamp.tv_sec; + update_timestamp_ns = update_timestamp_ns * 1000000000; + update_timestamp_ns = update_timestamp_ns + update_timestamp.tv_nsec; + // if the update_timestamp is zero, we take this to mean that the device doesn't report // interrupt timings. (It could be that it's not a real hardware device.) @@ -1497,7 +1509,7 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, if (update_timestamp_ns == 0) { ret = snd_pcm_delay(alsa_handle, delay); } else { - *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status); + snd_pcm_sframes_t delay_temp = snd_pcm_status_get_delay(alsa_snd_pcm_status); /* // It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though @@ -1514,11 +1526,15 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, else clock_gettime(CLOCK_REALTIME, &tn); - uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec; + // uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec; + uint64_t time_now_ns = tn.tv_sec; + time_now_ns = time_now_ns * 1000000000; + time_now_ns = time_now_ns + tn.tv_nsec; + // see if it's stalled - if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) { + if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == delay_temp)) { // hasn't outputted anything since the last call to delay() if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) || @@ -1539,19 +1555,27 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, } } else { stall_monitor_start_time = update_timestamp_ns; - stall_monitor_frame_count = *delay; + stall_monitor_frame_count = delay_temp; } if (ret == 0) { uint64_t delta = time_now_ns - update_timestamp_ns; - uint64_t frames_played_since_last_interrupt = - ((uint64_t)config.output_rate * delta) / 1000000000; +// uint64_t frames_played_since_last_interrupt = +// ((uint64_t)config.output_rate * delta) / 1000000000; + + uint64_t frames_played_since_last_interrupt = config.output_rate; + frames_played_since_last_interrupt = frames_played_since_last_interrupt * delta; + frames_played_since_last_interrupt = frames_played_since_last_interrupt / 1000000000; + + snd_pcm_sframes_t frames_played_since_last_interrupt_sized = frames_played_since_last_interrupt; - - *delay = *delay - frames_played_since_last_interrupt_sized; + if ((frames_played_since_last_interrupt_sized < 0) || ((uint64_t)frames_played_since_last_interrupt_sized != frames_played_since_last_interrupt)) + debug(1,"overflow resizing frames_played_since_last_interrupt % " PRIx64 " to frames_played_since_last_interrupt %lx.", frames_played_since_last_interrupt, frames_played_since_last_interrupt_sized); + delay_temp = delay_temp - frames_played_since_last_interrupt_sized; } + *delay = delay_temp; } } else { // not running, thus no delay information, thus can't check for // stall @@ -1582,25 +1606,26 @@ int delay(long *the_delay) { // sps_extra_code_output_state_cannot_make_ready codes int ret = 0; *the_delay = 0; - if (alsa_handle == NULL) - ret = ENODEV; - else { - int oldState; - snd_pcm_state_t state; - snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning + int oldState; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable - pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); + snd_pcm_state_t state; + snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); + + if (alsa_handle == NULL) + ret = ENODEV; + else ret = delay_and_status(&state, &my_delay, NULL); - debug_mutex_unlock(&alsa_mutex, 0); - pthread_cleanup_pop(0); - pthread_setcancelstate(oldState, NULL); + debug_mutex_unlock(&alsa_mutex, 0); + pthread_cleanup_pop(0); + pthread_setcancelstate(oldState, NULL); + + *the_delay = my_delay; // note: snd_pcm_sframes_t is a long - *the_delay = my_delay; // note: snd_pcm_sframes_t is a long - } return ret; } diff --git a/common.c b/common.c index 8520c71..6e0f12a 100644 --- a/common.c +++ b/common.c @@ -834,7 +834,7 @@ void command_set_volume(double volume) { _exit(EXIT_FAILURE); /* only if execv fails */ } } - _exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); } else { if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */ diff --git a/configure.ac b/configure.ac index 5898a17..f77087c 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], [3.3.8rc3], [4265913+mikebrady@users.noreply.github.com]) +AC_INIT([shairport-sync], [3.3.8], [4265913+mikebrady@users.noreply.github.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) @@ -147,17 +147,18 @@ AM_CONDITIONAL([USE_CUSTOMPIDDIR], [ test "x${with_piddir}" != "x" ]) # Look for libdaemon AC_ARG_WITH(libdaemon,[AS_HELP_STRING([--with-libdaemon],[include support for daemonising in non-systemd systems])]) -if test "x$with_libdaemon" = "x1"; then +if test "x$with_libdaemon" = "xyes"; then AC_DEFINE([CONFIG_LIBDAEMON], 1, [Include libdaemon for daemonising in non-systemd systems]) if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [DAEMON], [libdaemon], - [LIBS="${DAEMON_LIBS} ${LIBS}"]) + [LIBS="${DAEMON_LIBS} ${LIBS}"], + [AC_MSG_ERROR(the libdaemon library has been selected but is missing -- libdaemon-dev suggested!)]) else - AC_CHECK_LIB([daemon],[daemon_fork], , AC_MSG_ERROR(libdaemon needed)) + AC_CHECK_LIB([daemon],[daemon_fork], , AC_MSG_ERROR(the libdaemon library has been selected but is missing -- libdaemon-dev suggested!)) fi fi -AM_CONDITIONAL([USE_LIBDAEMON], [test "x$with_libdaemon" = "x1"]) +AM_CONDITIONAL([USE_LIBDAEMON], [test "x$with_libdaemon" = "xyes"]) # Check --with-ssl=argument AC_ARG_WITH(ssl, [AS_HELP_STRING([--with-ssl=],[choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=polarssl (deprecated) for encryption services ])]) @@ -241,7 +242,7 @@ if test "x$with_alsa" = "xyes" ; then else AC_CHECK_LIB([asound], [snd_pcm_open], , AC_MSG_ERROR(ALSA support requires the asound library!)) fi -fi +fi AM_CONDITIONAL([USE_ALSA], [test "x$with_alsa" = "xyes"]) # Look for jack flag @@ -317,7 +318,7 @@ AM_CONDITIONAL([USE_DNS_SD], [test "x$with_dns_sd" = "xyes"]) # Look for dbus flag AC_ARG_WITH(dbus-interface, [AS_HELP_STRING([--with-dbus-interface],[include support for the native Shairport Sync D-Bus interface])]) -if test "x$with_dbus_interface" = "xyes" ; then +if test "x$with_dbus_interface" = "xyes" ; then AC_DEFINE([CONFIG_DBUS_INTERFACE], 1, [Support the native Shairport Sync D-Bus interface]) # remember to include glib, below fi diff --git a/dacp.c b/dacp.c index effbe5e..0230ac1 100644 --- a/dacp.c +++ b/dacp.c @@ -446,11 +446,11 @@ void set_dacp_server_information(rtsp_conn_info *conn) { if (dacp_server.active_remote_id) free(dacp_server.active_remote_id); if (conn->dacp_active_remote) - dacp_server.active_remote_id = - strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, - // the active remote will change. + dacp_server.active_remote_id = + strdup(conn->dacp_active_remote); // even if the dacp_id remains the same, + // the active remote will change. else - dacp_server.active_remote_id = NULL; + dacp_server.active_remote_id = NULL; debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); pthread_cond_signal(&dacp_server_information_cv); diff --git a/rtsp.c b/rtsp.c index f3524ae..2661cce 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1652,40 +1652,41 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { #endif void metadata_init(void) { - int ret; - if (config.metadata_enabled) { - // create the metadata pipe, if necessary - size_t pl = strlen(config.metadata_pipename) + 1; - char *path = malloc(pl + 1); - snprintf(path, pl + 1, "%s", config.metadata_pipename); - mode_t oldumask = umask(000); - if (mkfifo(path, 0666) && errno != EEXIST) - die("Could not create metadata pipe \"%s\".", path); - umask(oldumask); - debug(1, "metadata pipe name is \"%s\".", path); - - // try to open it - fd = try_to_open_pipe_for_writing(path); - // we check that it's not a "real" error. From the "man 2 open" page: - // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO - // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - } - free(path); - int ret; - ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata thread!"); - - ret = pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); - if (ret) - debug(1, "Failed to create metadata multicast thread!"); + int ret; + if (config.metadata_enabled) { + // create the metadata pipe, if necessary + size_t pl = strlen(config.metadata_pipename) + 1; + char *path = malloc(pl + 1); + snprintf(path, pl + 1, "%s", config.metadata_pipename); + mode_t oldumask = umask(000); + if (mkfifo(path, 0666) && errno != EEXIST) + die("Could not create metadata pipe \"%s\".", path); + umask(oldumask); + debug(1, "metadata pipe name is \"%s\".", path); + + // try to open it + fd = try_to_open_pipe_for_writing(path); + // we check that it's not a "real" error. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO + // open for reading." Which is okay. + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } + free(path); + int ret; + ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata thread!"); + + ret = + pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function, NULL); + if (ret) + debug(1, "Failed to create metadata multicast thread!"); } #ifdef CONFIG_METADATA_HUB ret = pthread_create(&metadata_hub_thread, NULL, metadata_hub_thread_function, NULL); @@ -1715,19 +1716,19 @@ void metadata_stop(void) { pthread_join(metadata_hub_thread, NULL); // debug(2, "metadata stop hub done."); #endif - if (config.metadata_enabled) { - // debug(2, "metadata stop multicast thread."); - if (metadata_multicast_thread) { - pthread_cancel(metadata_multicast_thread); - pthread_join(metadata_multicast_thread, NULL); - // debug(2, "metadata stop multicast done."); - } - if (metadata_thread) { - // debug(2, "metadata stop metadata_thread thread."); - pthread_cancel(metadata_thread); - pthread_join(metadata_thread, NULL); - // debug(2, "metadata_stop finished successfully."); - } + if (config.metadata_enabled) { + // debug(2, "metadata stop multicast thread."); + if (metadata_multicast_thread) { + pthread_cancel(metadata_multicast_thread); + pthread_join(metadata_multicast_thread, NULL); + // debug(2, "metadata stop multicast done."); + } + if (metadata_thread) { + // debug(2, "metadata stop metadata_thread thread."); + pthread_cancel(metadata_thread); + pthread_join(metadata_thread, NULL); + // debug(2, "metadata_stop finished successfully."); + } } } } @@ -1799,8 +1800,9 @@ int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rts int block) { int rc; if (config.metadata_enabled) { - rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); - rc = send_metadata_to_queue(&metadata_multicast_queue, type, code, data, length, carrier, block); + rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block); + rc = + send_metadata_to_queue(&metadata_multicast_queue, type, code, data, length, carrier, block); } #ifdef CONFIG_METADATA_HUB diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 6444b8a..e7f9e74 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -134,6 +134,7 @@ sndio = pa = { // server = "host"; // Set this to override the default pulseaudio server that should be used. +// sink = "Sink Name"; // Set this to override the default pulseaudio sink that should be used. (Untested) // application_name = "Shairport Sync"; //Set this to the name that should appear in the Sounds "Applications" tab when Shairport Sync is active. }; -- cgit v1.2.3 From 07c6d1dbfef9518d2e6e9a77b2f1bbef4196e0ed Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 26 Apr 2021 11:30:14 +0100 Subject: Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f744408..e86b136 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -8,7 +8,7 @@ Version 3.3.8 **Bug Fixes** * Fix a bug in the `alsa` back end. In the interval between checking that the alsa device handle was non-`NULL` and actually using it, the handle could be set to `NULL`. The interval between check and usage is now protected. * Fix a bug in the `alsa` precision timing code. Thanks to [durwin99](https://github.com/durwin99), [Nicolas Da Mutten](https://github.com/cleverer), [mistakenideas](https://github.com/mistakenideas), [Ben Willmore](https://github.com/ben-willmore) and [giggywithit](https://github.com/giggywithit) for the [report](https://github.com/mikebrady/shairport-sync/issues/1158). -* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an on-play or any `on-...` script failed. +* Fix a bug that caused Shairport Sync to hang, but not actually crash, if an `on-...` script failed. * Fix a crash that occurred if metadata support is enabled during compilation but turned off in the configuration file. Thanks to [Tim Curtis](https://github.com/moodeaudio) for the report. * Fix a crash that occurred playing from AirPower on Android. Thanks to [Ircama](https://github.com/Ircama) for the report. * Fix the configure.ac file so that `--without-` configuration options are not interpreted as `--with-` options instead! Thanks to [David Racine](https://github.com/bassdr) for the report. -- cgit v1.2.3 From c19f697be2b6761616876787064d6b067cf87089 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 26 Apr 2021 11:35:11 +0100 Subject: Update shairport-sync.spec --- shairport-sync.spec | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/shairport-sync.spec b/shairport-sync.spec index 215b303..ed0c3d4 100644 --- a/shairport-sync.spec +++ b/shairport-sync.spec @@ -1,5 +1,5 @@ Name: shairport-sync -Version: 3.3.7 +Version: 3.3.8 Release: 1%{?dist} Summary: AirTunes emulator. Multi-Room with Audio Synchronisation # MIT licensed except for tinysvcmdns under BSD, @@ -66,29 +66,31 @@ getent passwd %{name} &> /dev/null || useradd --system -c "%{name} User" \ %license LICENSES %changelog -* Tue Nov 1 2020 Mike Brady 3.3.8 +- Bug fixes and enhancements. +* Tue Nov 1 2020 Mike Brady <4265913+mikebrady@users.noreply.github.com> 3.3.7 - Jack resampling, bug fixes and additions to the pipe and stdout backends, to metadata and to the D-Bus and MPRIS interfaces. -* Thu Feb 20 2020 Mike Brady 3.3.6 - Jack resampling, bug fixes and additions to the D-Bus and MPRIS interfaces. -* Wed Nov 13 2019 Mike Brady 3.3.5 - Bug fixes and additions to the D-Bus interface. -* Mon Oct 28 2019 Mike Brady 3.3.4 - Bug fixes and minor enhancements. -* Fri Jul 26 2019 Mike Brady 3.3.2 - Minor bug fixes. -* Wed Jun 05 2019 Mike Brady 3.3.1 - Bug fixes. -* Fri May 24 2019 Mike Brady 3.3 - Audio enhancements, stability improvements, MQTT and Jack Audio interfaces. -* Sun Oct 14 2018 Mike Brady 3.2.2 - Compatibility with iOS 12 and mac OS Mojave AirPlay latencies. Minor bug fix. -* Fri Jul 13 2018 Mike Brady 3.2.1 - Stability improvements when soxr interpolation is chosen. -* Mon Jul 09 2018 Mike Brady 3.2 - New D-Bus and MPRIS Interfaces, Bug Fixes and Enhancements. -* Thu Dec 21 2017 Mike Brady 3.1.7 +* Thu Dec 21 2017 Mike Brady <4265913+mikebrady@users.noreply.github.com> 3.1.7 - Bug fix for unexpectedly resuming play at full volume from iOS 11.2 and macOS 10.3.2. -* Mon Dec 11 2017 Mike Brady 3.1.5 +* Mon Dec 11 2017 Mike Brady <4265913+mikebrady@users.noreply.github.com> 3.1.5 - Bug fixes and better compatibility with iOS 11.2 and mac OS 10.13.2. - Better AirPlay synchronisation. * Wed Sep 13 2017 Bill Peck 3.1.2-1 @@ -115,27 +117,27 @@ getent passwd %{name} &> /dev/null || useradd --system -c "%{name} User" \ - new optional loudness and convolution filters - improvements in non-synchronised backends - enhancements, stability improvements and bug fixes -* Fri Feb 24 2017 Mike Brady 2.8.6 +* Fri Feb 24 2017 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.6 - Many changes including 8- 16- 24- and 32-bit output -* Fri Oct 21 2016 Mike Brady 2.8.6 +* Fri Oct 21 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.6 - Advertise self as ShairportSync rather than AirPort device 2.8.6 -* Sun Sep 25 2016 Mike Brady 2.8.5 +* Sun Sep 25 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.5 - Bug fixes and small enhancements 2.8.5 -* Sat May 28 2016 Mike Brady 2.8.4 +* Sat May 28 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.4 - Bug fixes and a few small enhancements 2.8.4 -* Fri Apr 15 2016 Mike Brady 2.8.2 +* Fri Apr 15 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.2 - Stability improvements, bug fixes and a few special-purpose settings 2.8.2 -* Wed Mar 02 2016 Mike Brady 2.8.1 +* Wed Mar 02 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.1 - Stability improvements and important bug fixes 2.8.1 -* Sat Jan 30 2016 Mike Brady 2.8.0 +* Sat Jan 30 2016 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.8.0 - Enhancements and bug fixes 2.8.0 -* Sun Oct 18 2015 Mike Brady 2.6 +* Sun Oct 18 2015 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.6 - Important enhancements and bug fixes 2.6 -* Thu Aug 27 2015 Mike Brady 2.4.1 +* Thu Aug 27 2015 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.4.1 - Minor bug fixes 2.4.1 -* Thu Aug 27 2015 Mike Brady 2.4 +* Thu Aug 27 2015 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.4 - Prepare for stable release 2.4 -* Wed Aug 26 2015 Mike Brady 2.3.13.1-1 +* Wed Aug 26 2015 Mike Brady <4265913+mikebrady@users.noreply.github.com> 2.3.13.1-1 - Harmonise release numbers * Fri Jul 24 2015 Bill Peck 2.3.7-1 - Initial spec file -- cgit v1.2.3