From 78f9efc92d241f249911e7ba0eb6aac3bf98395d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 1 Feb 2016 11:27:20 +0000 Subject: [PATCH 01/38] Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 667e7960..da259e79 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], [2.8.0], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [2.9], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From f6a412604dc9957ab339686829cb0da06834b801 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 1 Feb 2016 11:58:36 +0000 Subject: [PATCH 02/38] Create TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 TROUBLESHOOTING.md diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 00000000..be5d2326 --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,5 @@ +Troubleshooting +----- +The installation and setup of Shairport Sync is straightforward on recent Linux distributions. Issues can occasionally arise caused by problems elsewhere in the system, typically WiFi reception and/or the WiFi adapter settings, the network, the router, firewall settings. + +In this brief document will be listed some problems and some solutions. From 9f62ec1ab0cff77c06e5c42edbc1d94a4ce1245b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 1 Feb 2016 13:33:42 +0000 Subject: [PATCH 03/38] Update README.md Remove stuff not appropriate for development --- README.md | 50 ++++---------------------------------------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index c6b6fb56..12754277 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Audio played by a Shairport Sync-powered device stays synchronised with the sour Shairport Sync does not support AirPlay video or photo streaming. -This is the stable "master" branch. Changes and updates are incorporated into this branch relatively slowly. To access the development version, where all the latest changes are made first, please switch to the "development" branch. +This is the unstable "development" branch. Changes and updates are incorporated into this branch quickly. To access the stable version, where changes are made after due time, please switch to the "master" branch. More Information ---------- @@ -49,55 +49,12 @@ For information about changes and updates, please refer to the RELEASENOTES.md f Note: Historically, Shairport Sync has taken its settings from command line arguments. While this is still the case, it does not always work well across distributions. Accordingly, from version 2.4 onwards, Shairport Sync reads settings from the file `/etc/shairport-sync.conf`. Access to new features will only be provided via the settings file. -Building And Installing +Building And Installing the Development Version --------------------- -If you wish to install Shairport Sync on OpenWrt, Arch or Fedora platforms, please follow the appropriate instructions below. Limited support is also available for MAc OS X. Otherwise follow the General Build Instructions. Then, when the program has been installed, refer to the section on Configuring Shairport Sync that follows. -**Note** - -The following procedures will install the shairport-sync application into your system. Before continuing, you should check to see if shairport-sync is already installed – you can use `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. +The following procedures will install the development version of the shairport-sync application into your system. Before continuing, you should check to see if shairport-sync is already installed – you can use `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. (If the existing installation of shairport-sync is where the new copy will be installed into, it will be overwritten; sometimes, however, the installation is to another location, so it is safer, initially, to delete previous versions manually.) -**Ubuntu:** -Personal Package Archives for Shairport Sync master and development branches are available at https://launchpad.net/~dantheperson. A `shairport-sync` installer package is available in Ubuntu 16.04, currently in its alpha phase. - -**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/. - -An Arch Linux installation package, suitable for compilation on any platform, is available at [EliaCereda/shairport-sync-PKGBUILD](https://github.com/EliaCereda/shairport-sync-PKGBUILD). - -**Mac OS X:** -A [HomeBrew](http://brew.sh) package exists for Shairport Sync. With HomeBrew installed, Shairport Sync can be installed using the command `$brew install shairport-sync`. Note that the installation uses the `libao` library and so synchronisation is not available — playback glitches will occur occasionally, when the `ao` system's buffers overflow or underflow. - -**Fedora:** -Install the toolchain and pre-requisites, if necessary: -``` -% sudo yum install make automake gcc gcc-c++ kernel-devel -% sudo yum install alsa-lib-devel autoconf automake avahi-devel libconfig-devel libdaemon-devel openssl-devel popt-devel soxr-devel -``` -Download the tarball from the "releases" tab on github or use `wget` and then use `rpmbuild`. This example is for version 2.6: -``` -% wget -O shairport-sync-2.6.tar.gz https://github.com/mikebrady/shairport-sync/archive/2.6.tar.gz -% rpmbuild -ta shairport-sync-2.6.tar.gz -``` -The `-ta` means "build all from this tarball". - -The RPM will be built in a directory and will have a pathname like, for example, `~/rpmbuild/RPMS/i686/shairport-sync-2.6-1.fc22.i686.rpm` You should then install it with (for this example): -``` -%sudo rpm -i ~/rpmbuild/RPMS/i686/shairport-sync-2.6-1.fc22.i686.rpm -``` -You may have to manually create the directory `/var/shairport-sync` for the installation to succeed. Having edited the configuration file `/etc/shairport-sync.conf` as appropriate (see "Configuring Shairport Sync" below), enable and start the service with: -``` -%sudo systemctl enable shairport-sync.service -%sudo systemctl start shairport-sync.service -``` -Sincere thanks to all package contributors! - -**General Build Instructions** - To build Shairport Sync from sources on Debian, Ubuntu, Raspbian, etc. follow these instructions. The following libraries are required: @@ -132,6 +89,7 @@ Download Shairport Sync: Next, `cd` into the shairport-sync directory and execute the following command: ``` +$ git checkout development $ autoreconf -i -f ``` (Note that the `autoreconf...` step may take some time on less powerful machines.) From 1892be5aebc5dfdd2f251d01e1c41c11118b3c1f Mon Sep 17 00:00:00 2001 From: Fabrice Carrega Date: Mon, 1 Feb 2016 22:43:47 +0100 Subject: [PATCH 04/38] Edited Troubleshooting.md #214 --- TROUBLESHOOTING.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index be5d2326..aabf6579 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -3,3 +3,39 @@ Troubleshooting The installation and setup of Shairport Sync is straightforward on recent Linux distributions. Issues can occasionally arise caused by problems elsewhere in the system, typically WiFi reception and/or the WiFi adapter settings, the network, the router, firewall settings. In this brief document will be listed some problems and some solutions. + +### UFW firewall blocking connections on Raspbian (Raspberry Pi) + +**Problem** + +You have installed Shairport-sync successfully, the deamon is running, you can see it from your remote terminal but you are unable to play a song. + +**Before you change anything to your configuration** + +- Type the following command: + + `sudo ufw disable` + +- Try to launch a song from your remote device on the Shairport-sync one, if this works, proceed to the next step and follow the ones described below, in the solution section. + +- Enable UFW through the following command: + + `sudo ufw enable` + +**Solution** + +You have to allow connections to your Pi from remote devices. To do so, after re-enabling UFW (see last stp of the previous section), enter the following commands in shell: + +``` +sudo ufw allow from 192.168.1.1/16 to any port 3689 proto tcp +sudo ufw allow from 192.168.1.1/16 to any port 5353 +sudo ufw allow from 192.168.1.1/16 to any port 5000:5005 proto tcp +sudo ufw allow from 192.168.1.1/16 to any port 6000:6005 proto udp +sudo ufw allow from 192.168.1.1/16 to any port 35000:65535 proto udp +``` + +You may have to change the IP adresses range depending on your own local network settings. + +You can check UFW config by typing `sudo ufw status` in shell. Please make sure that UFW is active, especially if you have deactivated it previously for testing purpose. + +Run your song from your remote device. Enjoy ! From 328379b7f14fe66b549ad05f21c81d18618606c8 Mon Sep 17 00:00:00 2001 From: Fabrice Carrega Date: Mon, 1 Feb 2016 22:47:17 +0100 Subject: [PATCH 05/38] Corrected typo #214 --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index aabf6579..f5dd8ef0 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -24,7 +24,7 @@ You have installed Shairport-sync successfully, the deamon is running, you can s **Solution** -You have to allow connections to your Pi from remote devices. To do so, after re-enabling UFW (see last stp of the previous section), enter the following commands in shell: +You have to allow connections to your Pi from remote devices. To do so, after re-enabling UFW (see last step of the previous section), enter the following commands in shell: ``` sudo ufw allow from 192.168.1.1/16 to any port 3689 proto tcp From c2949b0ff5b3495c8628bc0b501f0979626e445c Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 2 Feb 2016 09:08:19 +0000 Subject: [PATCH 06/38] Add proper data structure for passing parameters between threads. Add some debug messages. --- player.c | 10 ++++++---- rtp.c | 23 +++++++++++------------ rtp.h | 4 ++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/player.c b/player.c index c35c6b99..1aaef023 100644 --- a/player.c +++ b/player.c @@ -893,12 +893,13 @@ typedef struct stats { // statistics for running averages } stats_t; static void *player_thread_func(void *arg) { - int threads_stop = 0; + struct inter_threads_record itr; + itr.please_stop = 0; // this will be used to signal to the subsidiary threads // create and start the timing, control and audio receiver threads pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread; - pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&threads_stop); - pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&threads_stop); - pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&threads_stop); + pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&itr); + pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&itr); + pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&itr); session_corrections = 0; play_segment_reference_frame = 0; // zero signals that we are not in a play segment @@ -1232,6 +1233,7 @@ static void *player_thread_func(void *arg) { free(silence); debug(1,"Shut down audio, control and timing threads"); // usleep(1000000); + itr.please_stop = 1; pthread_kill(rtp_audio_thread, SIGUSR1); pthread_kill(rtp_control_thread, SIGUSR1); pthread_kill(rtp_timing_thread, SIGUSR1); diff --git a/rtp.c b/rtp.c index bc40948d..7e0b1984 100644 --- a/rtp.c +++ b/rtp.c @@ -47,11 +47,6 @@ #include #endif */ -typedef struct { - uint32_t seconds; - uint32_t fraction; -} ntp_timestamp; - typedef struct time_ping_record { uint64_t local_to_remote_difference; uint64_t dispersion; @@ -96,15 +91,16 @@ static pthread_mutex_t reference_time_mutex = PTHREAD_MUTEX_INITIALIZER; uint64_t static local_to_remote_time_difference; // used to switch between local and remote clocks void *rtp_audio_receiver(void *arg) { - // we inherit the signal mask (SIGUSR1) + debug(2, "Audio receiver -- Server RTP thread starting."); - int *stop = arg; // when set to 1, we should stop + // we inherit the signal mask (SIGUSR1) + struct inter_threads_record *itr = arg; int32_t last_seqno = -1; uint8_t packet[2048], *pktp; ssize_t nread; - while (*stop==0) { + while (itr->please_stop==0) { nread = recv(audio_socket, packet, sizeof(packet), 0); if (nread < 0) break; @@ -160,7 +156,8 @@ void *rtp_audio_receiver(void *arg) { void *rtp_control_receiver(void *arg) { // we inherit the signal mask (SIGUSR1) - int *stop = arg; // when set to 1, we should stop + debug(2, "Control receiver -- Server RTP thread starting."); + struct inter_threads_record *itr = arg; reference_timestamp = 0; // nothing valid received yet uint8_t packet[2048], *pktp; @@ -168,7 +165,7 @@ void *rtp_control_receiver(void *arg) { uint64_t remote_time_of_sync, local_time_now, remote_time_now; uint32_t sync_rtp_timestamp, rtp_timestamp_less_latency; ssize_t nread; - while (*stop==0) { + while (itr->please_stop==0) { nread = recv(control_socket, packet, sizeof(packet), 0); local_time_now = get_absolute_time_in_fp(); // clock_gettime(CLOCK_MONOTONIC,&tn); @@ -260,6 +257,7 @@ void *rtp_control_receiver(void *arg) { } void *rtp_timing_sender(void *arg) { + debug(2, "Timing sender thread starting."); int *stop = arg; // the parameter points to this request to stop thing struct timing_request { char leader; @@ -315,9 +313,10 @@ void *rtp_timing_sender(void *arg) { } void *rtp_timing_receiver(void *arg) { + debug(2, "Timing receiver -- Server RTP thread starting."); // we inherit the signal mask (SIGUSR1) - int *stop = arg; // when set to 1, we should stop + struct inter_threads_record *itr = arg; uint8_t packet[2048], *pktp; ssize_t nread; @@ -335,7 +334,7 @@ void *rtp_timing_receiver(void *arg) { uint64_t first_local_to_remote_time_difference = 0; uint64_t first_local_to_remote_time_difference_time; uint64_t l2rtd = 0; - while (*stop==0) { + while (itr->please_stop==0) { nread = recv(timing_socket, packet, sizeof(packet), 0); arrival_time = get_absolute_time_in_fp(); // clock_gettime(CLOCK_MONOTONIC,&att); diff --git a/rtp.h b/rtp.h index cc0776da..9adfb534 100644 --- a/rtp.h +++ b/rtp.h @@ -5,6 +5,10 @@ #include "player.h" +typedef struct inter_threads_record { + uint32_t please_stop; +} inter_threads_record; + void *rtp_audio_receiver(void *arg); void *rtp_control_receiver(void *arg); void *rtp_timing_receiver(void *arg); From 6c485fa5b8b1cc60d809114baf6ef7d5e0e427ac Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 2 Feb 2016 09:43:44 +0000 Subject: [PATCH 07/38] Make alac check allocated buffer size and make player check and discard bad audio frames. --- alac.c | 17 +++++++++++++++++ player.c | 36 ++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/alac.c b/alac.c index d5833c61..cf72781a 100644 --- a/alac.c +++ b/alac.c @@ -710,6 +710,7 @@ void alac_decode_frame(alac_file *alac, unsigned char *inbuffer, void *outbuffer, int *outputsize) { + int outbuffer_allocation_size = *outputsize; // initial value int channels; int32_t outputsamples = alac->setinfo_max_samples_per_frame; @@ -720,6 +721,11 @@ void alac_decode_frame(alac_file *alac, channels = readbits(alac, 3); *outputsize = outputsamples * alac->bytespersample; + if (*outputsize>outbuffer_allocation_size) { + fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E1.\n"); + *outputsize = 0; + return; + } switch(channels) { @@ -751,6 +757,12 @@ void alac_decode_frame(alac_file *alac, * as a 32bit integer */ outputsamples = readbits(alac, 32); *outputsize = outputsamples * alac->bytespersample; + if (*outputsize>outbuffer_allocation_size) { + fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E2.\n"); + *outputsize = 0; + return; + } + } readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8); @@ -928,6 +940,11 @@ void alac_decode_frame(alac_file *alac, * as a 32bit integer */ outputsamples = readbits(alac, 32); *outputsize = outputsamples * alac->bytespersample; + if (*outputsize>outbuffer_allocation_size) { + fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E3.\n"); + *outputsize = 0; + return; + } } readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8) + 1; diff --git a/player.c b/player.c index 1aaef023..a6b1f915 100644 --- a/player.c +++ b/player.c @@ -221,11 +221,12 @@ static inline int seq32_order(uint32_t a, uint32_t b) { return (C & 0x80000000) == 0; } -static void alac_decode(short *dest, uint8_t *buf, int len) { +static int alac_decode(short *dest, uint8_t *buf, int len) { unsigned char packet[MAX_PACKET]; unsigned char packetp[MAX_PACKET]; assert(len <= MAX_PACKET); - int outsize; + int reply = 0; //everything okay + int outsize=FRAME_BYTES(frame_size); // the size it should be if (encrypted) { unsigned char iv[16]; @@ -244,11 +245,13 @@ static void alac_decode(short *dest, uint8_t *buf, int len) { } if (outsize!=FRAME_BYTES(frame_size)) { if(outsizedata, data, len); - abuf->ready = 1; - abuf->timestamp = timestamp; - abuf->sequence_number = seqno; + if (alac_decode(abuf->data, data, len)==0) { + abuf->ready = 1; + abuf->timestamp = timestamp; + abuf->sequence_number = seqno; + } else { + debug(1,"Bad audio packet detected and discarded."); + abuf->ready = 0; + abuf->timestamp = 0; + abuf->sequence_number = 0; + } } // pthread_mutex_lock(&ab_mutex); @@ -893,13 +902,12 @@ typedef struct stats { // statistics for running averages } stats_t; static void *player_thread_func(void *arg) { - struct inter_threads_record itr; - itr.please_stop = 0; // this will be used to signal to the subsidiary threads + int threads_stop = 0; // create and start the timing, control and audio receiver threads pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread; - pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&itr); - pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&itr); - pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&itr); + pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&threads_stop); + pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&threads_stop); + pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&threads_stop); session_corrections = 0; play_segment_reference_frame = 0; // zero signals that we are not in a play segment @@ -1233,7 +1241,7 @@ static void *player_thread_func(void *arg) { free(silence); debug(1,"Shut down audio, control and timing threads"); // usleep(1000000); - itr.please_stop = 1; + threads_stop = 1; pthread_kill(rtp_audio_thread, SIGUSR1); pthread_kill(rtp_control_thread, SIGUSR1); pthread_kill(rtp_timing_thread, SIGUSR1); From 686082b34dd80f6544a570fe62ed955bebc30118 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 2 Feb 2016 14:43:37 +0000 Subject: [PATCH 08/38] Take a little more care of access to the alsa system; wait 100 ms after finishing with it to release data strucutres --- audio_alsa.c | 8 +++++++- player.c | 13 +++++++------ rtsp.c | 8 ++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index a389ee28..4e2677b1 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -437,8 +437,10 @@ static uint32_t delay() { static void play(short buf[], int samples) { int ret = 0; if (alsa_handle == NULL) { + pthread_mutex_lock(&alsa_mutex); ret = open_alsa_device(); open_mixer(); + pthread_mutex_unlock(&alsa_mutex); if ((ret == 0) && (audio_alsa.volume)) audio_alsa.volume(set_volume); } @@ -467,6 +469,7 @@ static void play(short buf[], int samples) { } static void flush(void) { + pthread_mutex_lock(&alsa_mutex); int derr; if (alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); @@ -495,6 +498,7 @@ static void flush(void) { snd_pcm_close(alsa_handle); alsa_handle = NULL; } + pthread_mutex_unlock(&alsa_mutex); } static void stop(void) { @@ -513,7 +517,8 @@ static void parameters(audio_parameters *info) { } static void volume(double vol) { - debug(2, "Setting volume db to %f.", vol); + pthread_mutex_lock(&alsa_mutex); + debug(2, "Setting volume db to %f.", vol); set_volume = vol; if (alsa_mix_handle) { if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 0) != 0) { @@ -523,6 +528,7 @@ static void volume(double vol) { die("Failed to set playback dB volume"); } } + pthread_mutex_unlock(&alsa_mutex); } static void linear_volume(double vol) { diff --git a/player.c b/player.c index a6b1f915..348eac63 100644 --- a/player.c +++ b/player.c @@ -902,12 +902,13 @@ typedef struct stats { // statistics for running averages } stats_t; static void *player_thread_func(void *arg) { - int threads_stop = 0; + struct inter_threads_record itr; + itr.please_stop = 0; // create and start the timing, control and audio receiver threads pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread; - pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&threads_stop); - pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&threads_stop); - pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&threads_stop); + pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&itr); + pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&itr); + pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&itr); session_corrections = 0; play_segment_reference_frame = 0; // zero signals that we are not in a play segment @@ -1237,11 +1238,11 @@ static void *player_thread_func(void *arg) { } if (config.output->stop) config.output->stop(); + usleep(100000); // allow this time to (?) allow the alsa subsystem to finish cleaning up after itself. 50 ms seems too short free(outbuf); free(silence); debug(1,"Shut down audio, control and timing threads"); - // usleep(1000000); - threads_stop = 1; + itr.please_stop = 1; pthread_kill(rtp_audio_thread, SIGUSR1); pthread_kill(rtp_control_thread, SIGUSR1); pthread_kill(rtp_timing_thread, SIGUSR1); diff --git a/rtsp.c b/rtsp.c index 7a768c3a..055fc6cd 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1694,10 +1694,10 @@ static void *rtsp_conversation_thread_func(void *pconn) { rtp_shutdown(); pthread_mutex_unlock(&play_lock); pthread_mutex_unlock(&playing_mutex); - } else { - debug(1, "This RTSP conversation thread doesn't think it's playing for a " - "close RTSP connection."); - } + } // else { + //debug(1, "This RTSP conversation thread doesn't think it's playing for a " + // "close RTSP connection."); + // } if (auth_nonce) free(auth_nonce); conn->running = 0; From c84f3dd058cb3ea5f6bf4c3f0a96145ee3088dba Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 2 Feb 2016 16:39:23 +0000 Subject: [PATCH 09/38] Allow at least 500 ms before starting a new rtsp conversation to allow previous one to die, if any. --- rtsp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/rtsp.c b/rtsp.c index 055fc6cd..c3f00730 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1839,6 +1839,7 @@ void rtsp_listen_loop(void) { perror("failed to accept connection"); free(conn); } else { + usleep(500000); pthread_t rtsp_conversation_thread; ret = pthread_create(&rtsp_conversation_thread, NULL, rtsp_conversation_thread_func, conn); From c8af40e9ca18433714dc76144212ecede7223521 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 5 Feb 2016 09:25:21 +0000 Subject: [PATCH 10/38] Experimentally, discard flush request that come before the first packet -- TuneBlade bug workaround --- player.c | 8 ++++++++ rtsp.c | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/player.c b/player.c index 348eac63..35dd3e5a 100644 --- a/player.c +++ b/player.c @@ -301,6 +301,14 @@ static void free_buffer(void) { void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len) { + // ignore a request to flush that has been made before the first packet... + if (packet_count==0) { + pthread_mutex_lock(&flush_mutex); + flush_requested = 0; + flush_rtp_timestamp = 0; + pthread_mutex_unlock(&flush_mutex); + } + pthread_mutex_lock(&ab_mutex); packet_count++; time_of_last_audio_packet = get_absolute_time_in_fp(); diff --git a/rtsp.c b/rtsp.c index c3f00730..07d5ebd9 100644 --- a/rtsp.c +++ b/rtsp.c @@ -237,8 +237,12 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { // determine if we are the currently playing thread static inline int rtsp_playing(void) { if (pthread_mutex_trylock(&playing_mutex)) { + // if playing_mutex is locked... + // return 0 if the threads are different, non-zero if the threads are the same return pthread_equal(playing_thread, pthread_self()); } else { + // you actually acquired the playing_mutex, implying that there is no currently playing thread + // so unlock it return 0, implying you are not playing pthread_mutex_unlock(&playing_mutex); return 0; } @@ -1694,6 +1698,7 @@ static void *rtsp_conversation_thread_func(void *pconn) { rtp_shutdown(); pthread_mutex_unlock(&play_lock); pthread_mutex_unlock(&playing_mutex); + // usleep(1000000); } // else { //debug(1, "This RTSP conversation thread doesn't think it's playing for a " // "close RTSP connection."); From c998bbf5a4cc1354c4db71e638a3c9c1642cb48a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 5 Feb 2016 20:28:54 +0000 Subject: [PATCH 11/38] Update TROUBLESHOOTING.md Add a quick not about WiFi low-power mode --- TROUBLESHOOTING.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index f5dd8ef0..9af96faa 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -4,11 +4,27 @@ The installation and setup of Shairport Sync is straightforward on recent Linux In this brief document will be listed some problems and some solutions. +### WiFi adapter running in power-saving / low-power mode + +**Problem** + +Shairport Sync is installed and running, but sometimes it disappears from the network, and sometimes it suffers from long dropouts. + +**Possible Problem** + +These problems can be caused by lots of things, but one of them is that the WiFi adapter may be set to run in a low-power or power-saving mode. If it's not busy, then after a while it goes into a low-power mode. This is bad as the device needs to be always connected to the network to provide the AirPlay service. You need to turn off power-saving mode. How you do this varies with platform and with WiFi adapter – internet search is your friend. Here, for instance, is the command for the C.H.I.P. from Next Thing Co, which has built in WiFi and Linux and has the `iw` command installed: + +``` +iw dev wlan0 set power_save off +``` + +There are some more details in some the closed issues on this repository. + ### UFW firewall blocking connections on Raspbian (Raspberry Pi) **Problem** -You have installed Shairport-sync successfully, the deamon is running, you can see it from your remote terminal but you are unable to play a song. +You have installed Shairport Sync successfully, the deamon is running, you can see it from your remote terminal but you are unable to play a song. **Before you change anything to your configuration** From ec9ad0bbd6114ce0bc9a40ad76e998a3bfa491f4 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 5 Feb 2016 20:29:44 +0000 Subject: [PATCH 12/38] Update TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 9af96faa..63002362 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -10,9 +10,9 @@ In this brief document will be listed some problems and some solutions. Shairport Sync is installed and running, but sometimes it disappears from the network, and sometimes it suffers from long dropouts. -**Possible Problem** +**Possible Cause** -These problems can be caused by lots of things, but one of them is that the WiFi adapter may be set to run in a low-power or power-saving mode. If it's not busy, then after a while it goes into a low-power mode. This is bad as the device needs to be always connected to the network to provide the AirPlay service. You need to turn off power-saving mode. How you do this varies with platform and with WiFi adapter – internet search is your friend. Here, for instance, is the command for the C.H.I.P. from Next Thing Co, which has built in WiFi and Linux and has the `iw` command installed: +This can be caused by lots of things, but one of them is that the WiFi adapter may be set to run in a low-power or power-saving mode. If it's not busy, then after a while it goes into a low-power mode. This is bad as the device needs to be always connected to the network to provide the AirPlay service. You need to turn off power-saving mode. How you do this varies with platform and with WiFi adapter – internet search is your friend. Here, for instance, is the command for the C.H.I.P. from Next Thing Co, which has built in WiFi and Linux and has the `iw` command installed: ``` iw dev wlan0 set power_save off From d8c4201b9ab4591fbbc0793b66c75a361d0d02c7 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 8 Feb 2016 22:02:22 +0000 Subject: [PATCH 13/38] Change shutdown requester. --- shairport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shairport.c b/shairport.c index e6286891..f8ffa081 100644 --- a/shairport.c +++ b/shairport.c @@ -70,7 +70,7 @@ void shairport_shutdown() { return; shutting_down = 1; mdns_unregister(); - rtsp_shutdown_stream(); + rtsp_request_shutdown_stream(); if (config.output) config.output->deinit(); } From bf82a386ae0d780ba75970f4504c7c3e219fe35f Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 8 Feb 2016 22:03:03 +0000 Subject: [PATCH 14/38] Stop exporting rtsp_shutdown_stream --- rtsp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtsp.h b/rtsp.h index 49650055..a2f6a952 100644 --- a/rtsp.h +++ b/rtsp.h @@ -2,7 +2,7 @@ #define _RTSP_H void rtsp_listen_loop(void); -void rtsp_shutdown_stream(void); +// void rtsp_shutdown_stream(void); void rtsp_request_shutdown_stream(void); From 8f99cab8f9d633b559bb4eef04daa7fec11b7cca Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 8 Feb 2016 22:04:23 +0000 Subject: [PATCH 15/38] Stop using the playing_mutex mutex in favour of the play_lock mutex. Get rid of all references to playing_mutex. --- rtsp.c | 100 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/rtsp.c b/rtsp.c index 07d5ebd9..0699c681 100644 --- a/rtsp.c +++ b/rtsp.c @@ -73,6 +73,7 @@ enum rtsp_read_request_response { }; // Mike Brady's part... +static pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t play_lock = PTHREAD_MUTEX_INITIALIZER; // every time we want to retain or release a reference count, lock it with this @@ -81,9 +82,9 @@ static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER; // only one thread is allowed to use the player at once. // it monitors the request variable (at least when interrupted) -static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER; +//static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER; // static int please_shutdown = 0; -static pthread_t playing_thread = 0; +// static pthread_t playing_thread = 0; typedef struct { int fd; @@ -95,6 +96,14 @@ typedef struct { pthread_t player_thread; } rtsp_conn_info; +static rtsp_conn_info *playing_conn = NULL; // the data structure representing the connection that has the player. +static rtsp_conn_info **conns = NULL; + +void memory_barrier() { + pthread_mutex_lock(&barrier_mutex); + pthread_mutex_unlock(&barrier_mutex); +} + #ifdef CONFIG_METADATA typedef struct { pthread_mutex_t pc_queue_lock; @@ -236,14 +245,14 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { // determine if we are the currently playing thread static inline int rtsp_playing(void) { - if (pthread_mutex_trylock(&playing_mutex)) { + if (pthread_mutex_trylock(&play_lock)) { // if playing_mutex is locked... // return 0 if the threads are different, non-zero if the threads are the same - return pthread_equal(playing_thread, pthread_self()); + return pthread_equal(playing_conn->thread, pthread_self()); } else { // you actually acquired the playing_mutex, implying that there is no currently playing thread // so unlock it return 0, implying you are not playing - pthread_mutex_unlock(&playing_mutex); + pthread_mutex_unlock(&play_lock); return 0; } } @@ -254,27 +263,21 @@ void rtsp_request_shutdown_stream(void) { 0); // i.e. ask all playing threads to stop } -static void rtsp_take_player(void) { - if (rtsp_playing()) - return; +//static void rtsp_take_player(void) { +// if (rtsp_playing()) +// return; - if (pthread_mutex_trylock(&playing_mutex)) { - debug(1, "Request to all other playing threads to stop."); - ask_other_rtsp_conversation_threads_to_stop( - pthread_self()); // all threads apart from self - pthread_mutex_lock(&playing_mutex); - } - playing_thread = - pthread_self(); // make us the currently-playing thread (why?) -} - -void rtsp_shutdown_stream(void) { - rtsp_take_player(); - pthread_mutex_unlock(&playing_mutex); -} +// if (pthread_mutex_trylock(&playing_mutex)) { +// debug(1, "Request to all other playing threads to stop."); +// ask_other_rtsp_conversation_threads_to_stop( +// pthread_self()); // all threads apart from self +// pthread_mutex_lock(&playing_mutex); +// } +// playing_thread = +// pthread_self(); // make us the currently-playing thread (why?) +//} // keep track of the threads we have spawned so we can join() them -static rtsp_conn_info **conns = NULL; static int nconns = 0; static void track_thread(rtsp_conn_info *conn) { conns = realloc(conns, sizeof(rtsp_conn_info *) * (nconns + 1)); @@ -485,6 +488,7 @@ rtsp_read_request(rtsp_conn_info *conn, rtsp_message **the_packet) { int msg_size = -1; while (msg_size < 0) { + memory_barrier(); if (conn->stop != 0) { debug(1, "RTSP shutdown requested."); reply = rtsp_read_request_response_shutdown_requested; @@ -492,7 +496,7 @@ rtsp_read_request(rtsp_conn_info *conn, rtsp_message **the_packet) { } nread = read(conn->fd, buf + inbuf, buflen - inbuf); if (!nread) { - debug(1, "RTSP connection closed."); + debug(2, "RTSP connection closed."); reply = rtsp_read_request_response_shutdown_requested; goto shutdown; } @@ -798,7 +802,7 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, p = strchr(p, '=') + 1; tport = atoi(p); - rtsp_take_player(); +// rtsp_take_player(); rtp_setup(&conn->remote, cport, tport, active_remote, &lsport, &lcport, <port); if (!lsport) @@ -1329,9 +1333,31 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { + int have_the_player = 0; + // interrupt session if permitted - if ((config.allow_session_interruption == 1) || - (pthread_mutex_trylock(&play_lock) == 0)) { + if (pthread_mutex_trylock(&play_lock) == 0) { + have_the_player = 1; + } else { + if (config.allow_session_interruption == 1) { + // some other thread has the player ... ask it to relinquish the thread + if (playing_conn) { + playing_conn->stop = 1; + memory_barrier(); + pthread_kill(playing_conn->thread, SIGUSR1); + } else { + die("Non existent the_playing_conn with play_lock enabled."); + } + usleep(1000000); // here, it is possible for other connections to come in and nab the player. + debug(1,"Try to get the player now"); + //pthread_mutex_lock(&play_lock); + if (pthread_mutex_trylock(&play_lock) == 0) + have_the_player = 1; + } + } + + if (have_the_player) { + playing_conn = conn; // the present connection is now playing resp->respcode = 456; // 456 - Header Field Not Valid for Resource char *paesiv = NULL; char *prsaaeskey = NULL; @@ -1690,23 +1716,25 @@ static void *rtsp_conversation_thread_func(void *pconn) { } } while (reply != rtsp_read_request_response_shutdown_requested); - debug(1, "Now closing RTSP connection."); - if (conn->fd > 0) - close(conn->fd); + debug(1, "Closing down RTSP conversation thread..."); if (rtsp_playing()) { player_stop(&conn->player_thread); // might be less noisy doing this first rtp_shutdown(); + // usleep(400000); // let an angel pass... pthread_mutex_unlock(&play_lock); - pthread_mutex_unlock(&playing_mutex); + } + conn->running = 0; + if (conn->fd > 0) + close(conn->fd); + if (auth_nonce) + free(auth_nonce); +// pthread_mutex_unlock(&playing_mutex); // usleep(1000000); - } // else { +// } // else { //debug(1, "This RTSP conversation thread doesn't think it's playing for a " // "close RTSP connection."); // } - if (auth_nonce) - free(auth_nonce); - conn->running = 0; - debug(2, "Now terminating RTSP conversation thread."); + debug(2, "RTSP conversation thread terminated."); // please_shutdown = 0; return NULL; } From 7b94913c177e13c5aeeceffc7b4153efe36cf07a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 8 Feb 2016 22:05:19 +0000 Subject: [PATCH 16/38] Add a few debug messages, a few more lock/unlocks and tidy up a bit. --- audio_alsa.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 4e2677b1..6cbfee3a 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -97,7 +97,7 @@ static void help(void) { int open_mixer() { if (hardware_mixer) { - debug(3, "Open Mixer"); + debug(2, "Open Mixer"); int ret = 0; snd_mixer_selem_id_alloca(&alsa_mix_sid); snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index); @@ -122,7 +122,8 @@ int open_mixer() { } static int init(int argc, char **argv) { - // debug(1,"audio_alsa init called."); + pthread_mutex_lock(&alsa_mutex); + debug(2,"audio_alsa init called."); const char *str; int value; @@ -279,10 +280,12 @@ static int init(int argc, char **argv) { snd_mixer_close(alsa_mix_handle); alsa_mix_handle = NULL; + pthread_mutex_unlock(&alsa_mutex); return 0; } static void deinit(void) { + debug(2,"audio_alsa deinit called."); stop(); if (alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); @@ -290,6 +293,7 @@ static void deinit(void) { } int open_alsa_device(void) { + const snd_pcm_uframes_t minimal_buffer_headroom = 352 * 2; // we accept this much headroom in the hardware buffer, but we'll // accept less @@ -392,12 +396,14 @@ int open_alsa_device(void) { } static void start(int sample_rate) { + debug(2,"audio_alsa start called."); if (sample_rate != 44100) die("Unexpected sample rate %d -- only 44,100 supported!", sample_rate); desired_sample_rate = sample_rate; // must be a variable } static uint32_t delay() { + debug(3,"audio_alsa delay called."); if (alsa_handle == NULL) { return 0; } else { @@ -435,6 +441,7 @@ static uint32_t delay() { } static void play(short buf[], int samples) { + debug(3,"audio_alsa play called."); int ret = 0; if (alsa_handle == NULL) { pthread_mutex_lock(&alsa_mutex); @@ -469,6 +476,7 @@ static void play(short buf[], int samples) { } static void flush(void) { + debug(2,"audio_alsa flush called."); pthread_mutex_lock(&alsa_mutex); int derr; if (alsa_mix_handle) { @@ -502,13 +510,13 @@ static void flush(void) { } static void stop(void) { - if (alsa_handle != 0) - // when we want to stop, we want the alsa device - // to be closed immediately -- we may even be killing the thread, so we - // don't wish to wait - // so we should flush first - flush(); // flush will also close the device - // close_alsa_device(); + debug(2,"audio_alsa stop called."); + // when we want to stop, we want the alsa device + // to be closed immediately -- we may even be killing the thread, so we + // don't wish to wait + // so we should flush first + flush(); // flush will also close the device + // close_alsa_device(); } static void parameters(audio_parameters *info) { @@ -548,6 +556,8 @@ static void linear_volume(double vol) { } static void mute(int do_mute) { + pthread_mutex_lock(&alsa_mutex); + debug(2,"audio_alsa mute called."); if (alsa_mix_handle) { if (do_mute) { // debug(1,"Mute"); @@ -557,4 +567,5 @@ static void mute(int do_mute) { snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); } } + pthread_mutex_unlock(&alsa_mutex); } From ab31c3c7ea284344530e3f1aca31748e74644db8 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 12 Feb 2016 22:19:03 +0000 Subject: [PATCH 17/38] Create CYGWIN.md --- CYGWIN.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 CYGWIN.md diff --git a/CYGWIN.md b/CYGWIN.md new file mode 100644 index 00000000..1cecd78d --- /dev/null +++ b/CYGWIN.md @@ -0,0 +1,47 @@ +Installing to Cygwin +---- + +This is based on installing onto a fresh default installation of Cygwin 2.4.1 (64-bit installation) running in Windows 10 +inside VMWare Fusion on a Mac. + +Note: to enable Cygwin to resolve domain names, it was found necessary to go to Windows `Control Panel` > `Network and Internet` > `Network Connections` > +`Ethernet0` > `Properties` > `Internet Protocol Version 4 (TCP/IPv4)` > `Properties`, select `Use the following DNS server addresses` +and enter valid DNS server addresses, e.g. `8.8.8.8`. + +* Use `Cygwin Setup` to install the following packages: + * `pkg-config` + * `autoconf` + * `automake` + * `clang` + * `libdaemon-devel` + * `popt-devel` + * `make` + * `libao-devel` + * `openssl-devel` + * `libtool` + * `git` + * `wget` for convenience, + * `flex` for compiling `libconfig` + * `bison` for compiling `libconfig` + +* Download, compile, configure and install `libconfig`. + +For this, if your home directory name has a space in it, which happens by default with Cygwin, you should +temporarily rename it to a single word, e.g. `Mike Brady` -> `mike`. This is to allow the `make install` script to work properly. +``` +$ wget http://www.hyperrealm.com/libconfig/libconfig-1.5.tar.gz +$ tar -xvzf libconfig-1.5.tar.gz +$ cd libconfig-1.5 +$ ./configure +$ make +$ make install +``` +* Next, download, configure and compile Shairport Sync: +``` +$ git clone https://github.com/mikebrady/shairport-sync.git +$ cd shairport-sync +$ autoreconf -fi +$ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --with-ao --with-ssl=openssl +$ make +``` +* That's it. There should be a `shairport-sync.exe` file in your directory. From 9d8e138acdb281d7290a56cbc25e1038ea103b29 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 12 Feb 2016 22:26:52 +0000 Subject: [PATCH 18/38] Update CYGWIN.md --- CYGWIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CYGWIN.md b/CYGWIN.md index 1cecd78d..99a5360a 100644 --- a/CYGWIN.md +++ b/CYGWIN.md @@ -24,7 +24,7 @@ and enter valid DNS server addresses, e.g. `8.8.8.8`. * `flex` for compiling `libconfig` * `bison` for compiling `libconfig` -* Download, compile, configure and install `libconfig`. +* Download, configure, compile and install `libconfig`. For this, if your home directory name has a space in it, which happens by default with Cygwin, you should temporarily rename it to a single word, e.g. `Mike Brady` -> `mike`. This is to allow the `make install` script to work properly. From 0f14aad875ef0df54388cea772fc58ff3a79554a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 20 Feb 2016 13:46:27 +0000 Subject: [PATCH 19/38] Unlock mutex when initialising a device without a hardware mixer. Add check for hardware mixer to uses of mixer handle --- audio_alsa.c | 110 +++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 6cbfee3a..2591404c 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -222,63 +222,63 @@ static int init(int argc, char **argv) { debug(1, "Output device name is \"%s\".", alsa_out_dev); - if (!hardware_mixer) - return 0; + if (hardware_mixer) { + + if (alsa_mix_dev == NULL) + alsa_mix_dev = alsa_out_dev; - if (alsa_mix_dev == NULL) - alsa_mix_dev = alsa_out_dev; + // Open mixer - // Open mixer + open_mixer(); - open_mixer(); + if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, + &alsa_mix_maxv) < 0) + debug(1, "Can't read mixer's [linear] min and max volumes."); + else { + if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb, + &alsa_mix_maxdb) == 0) { - if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, - &alsa_mix_maxv) < 0) - debug(1, "Can't read mixer's [linear] min and max volumes."); - else { - if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb, - &alsa_mix_maxdb) == 0) { - - audio_alsa.volume = - &volume; // insert the volume function now we know it can do dB stuff - audio_alsa.parameters = ¶meters; // likewise the parameters stuff - if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) { - // Raspberry Pi does this - debug(1, "Lowest dB value is a mute."); - if (snd_mixer_selem_ask_playback_vol_dB( - alsa_mix_elem, alsa_mix_minv + 1, &alsa_mix_mindb) == 0) - debug(1, "Can't get dB value corresponding to a \"volume\" of 1."); + audio_alsa.volume = + &volume; // insert the volume function now we know it can do dB stuff + audio_alsa.parameters = ¶meters; // likewise the parameters stuff + if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) { + // Raspberry Pi does this + debug(1, "Lowest dB value is a mute."); + if (snd_mixer_selem_ask_playback_vol_dB( + alsa_mix_elem, alsa_mix_minv + 1, &alsa_mix_mindb) == 0) + debug(1, "Can't get dB value corresponding to a \"volume\" of 1."); + } + debug(1, "Hardware mixer has dB volume from %f to %f.", + (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); + } else { + // use the linear scale and do the db conversion ourselves + debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have " + "a dB volume scale, so it can't be used.", + alsa_mix_ctrl); + /* + debug(1, "Min and max volumes are %d and + %d.",alsa_mix_minv,alsa_mix_maxv); + alsa_mix_maxdb = 0; + if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0)) + alsa_mix_mindb = + -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0)); + else if (alsa_mix_maxv!=0) + alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0); + audio_alsa.volume = &linear_volume; // insert the linear volume function + audio_alsa.parameters = ¶meters; // likewise the parameters stuff + debug(1,"Max and min dB calculated are %d and + %d.",alsa_mix_maxdb,alsa_mix_mindb); + */ } - debug(1, "Hardware mixer has dB volume from %f to %f.", - (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); - } else { - // use the linear scale and do the db conversion ourselves - debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have " - "a dB volume scale, so it can't be used.", - alsa_mix_ctrl); - /* - debug(1, "Min and max volumes are %d and - %d.",alsa_mix_minv,alsa_mix_maxv); - alsa_mix_maxdb = 0; - if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0)) - alsa_mix_mindb = - -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0)); - else if (alsa_mix_maxv!=0) - alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0); - audio_alsa.volume = &linear_volume; // insert the linear volume function - audio_alsa.parameters = ¶meters; // likewise the parameters stuff - debug(1,"Max and min dB calculated are %d and - %d.",alsa_mix_maxdb,alsa_mix_mindb); - */ } - } - if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) { - audio_alsa.mute = - &mute; // insert the mute function now we know it can do muting stuff - debug(1, "Has mute ability."); - } + if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) { + audio_alsa.mute = + &mute; // insert the mute function now we know it can do muting stuff + debug(1, "Has mute ability."); + } - snd_mixer_close(alsa_mix_handle); + snd_mixer_close(alsa_mix_handle); + } alsa_mix_handle = NULL; pthread_mutex_unlock(&alsa_mutex); return 0; @@ -287,7 +287,7 @@ static int init(int argc, char **argv) { static void deinit(void) { debug(2,"audio_alsa deinit called."); stop(); - if (alsa_mix_handle) { + if (hardware_mixer && alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); } } @@ -479,7 +479,7 @@ static void flush(void) { debug(2,"audio_alsa flush called."); pthread_mutex_lock(&alsa_mutex); int derr; - if (alsa_mix_handle) { + if (hardware_mixer && alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); alsa_mix_handle = NULL; } @@ -528,7 +528,7 @@ static void volume(double vol) { pthread_mutex_lock(&alsa_mutex); debug(2, "Setting volume db to %f.", vol); set_volume = vol; - if (alsa_mix_handle) { + if (hardware_mixer && alsa_mix_handle) { if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 0) != 0) { debug(1, "Can't set playback volume accurately to %f dB.", vol); if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, -1) != 0) @@ -542,7 +542,7 @@ static void volume(double vol) { static void linear_volume(double vol) { debug(2, "Setting linear volume to %f.", vol); set_volume = vol; - if (alsa_mix_handle) { + if (hardware_mixer && alsa_mix_handle) { double linear_volume = pow(10, vol); // debug(1,"Linear volume is %f.",linear_volume); long int_vol = @@ -558,7 +558,7 @@ static void linear_volume(double vol) { static void mute(int do_mute) { pthread_mutex_lock(&alsa_mutex); debug(2,"audio_alsa mute called."); - if (alsa_mix_handle) { + if (hardware_mixer && alsa_mix_handle) { if (do_mute) { // debug(1,"Mute"); snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); From 32c667e6181df9b82c47c1d0ecc9b397618975c1 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 20 Feb 2016 13:48:16 +0000 Subject: [PATCH 20/38] Bump version number: Unlock mutex when initialising a device without a hardware mixer. Add check for hardware mixer to uses of mixer handle --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index da259e79..2ed835dc 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], [2.9], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [2.9.1], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 8e8a0ee7ab0db61dc46735a1206843fe943ffc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Krause?= Date: Sat, 20 Feb 2016 16:13:57 +0100 Subject: [PATCH 21/38] Fix static linking with openssl and alsa Otherwise build fails for cross-compilation. See http://autobuild.buildroot.net/results/682/6821605cf12e1a5393c29b30baf00cb0f1e1a451/build-end.log --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2ed835dc..7ca65ee6 100644 --- a/configure.ac +++ b/configure.ac @@ -114,7 +114,7 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl or --with-ssl=polarssl for encrypti AC_MSG_ERROR(choose either "openssl" or "polarssl" encryption) fi if test "x${with_ssl}" = xopenssl ; then - if test "x${with_pkg_config}" = x1 ; then + if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [SSL], [libssl,libcrypto], [LIBS="${SSL_LIBS} ${LIBS}" @@ -169,7 +169,7 @@ AC_ARG_WITH(alsa, [ --with-alsa = choose ALSA API support (GNU/Linux only)], HAS_ALSA=1 AM_CONDITIONAL([USE_ALSA], [test 0]) AC_DEFINE([CONFIG_ALSA], 1, [Needed by the compiler.]) - if test "x${with_pkg_config}" = x1 ; then + if test "x${with_pkg_config}" = xyes ; then PKG_CHECK_MODULES( [ALSA], [alsa], [LIBS="${ALSA_LIBS} ${LIBS}" From 6b46bc9de4013bea5fe6e4749a1d55f448d12c30 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Feb 2016 13:20:36 +0000 Subject: [PATCH 22/38] Update RELEASENOTES.md --- RELEASENOTES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3dd29541..010724fc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,10 @@ +Version 2.9.1 – Development Version +---- +Version 2.9.1 is focuses on bug fixes and stability improvements. +* Stability improvements are concentrated on what happens when a play sessions ends and is followed immediately by a new session. This happens in iOS 9.2 when you click to the next track or to the previous track. It also happens playing YouTube videos when a Mac's System Volume is routed through AirPlay. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues. +* A workaround for an apparent flushing issue in TuneBlade has been included. Thanks to [gibman](https://github.com/gibman) for reporting this issue. +* A number of bug fixes have been made to `configure.ac` – thanks to [Jörg Krause](https://github.com/joerg-krause). + Version 2.8 – Stable Version ---- Version 2.8 is derived from version 2.7.10 with slight documentation updates. Here is a summary of changes between the last stable version – 2.6 – and this version. For full details, refer to the release notes here, back as far as 2.7. From d6b1301833a677da689ff8a0a5dbb440a4c53bce Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Feb 2016 13:23:39 +0000 Subject: [PATCH 23/38] Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 010724fc..e5ecc155 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,7 +1,7 @@ Version 2.9.1 – Development Version ---- Version 2.9.1 is focuses on bug fixes and stability improvements. -* Stability improvements are concentrated on what happens when a play sessions ends and is followed immediately by a new session. This happens in iOS 9.2 when you click to the next track or to the previous track. It also happens playing YouTube videos when a Mac's System Volume is routed through AirPlay. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues. +* Stability improvements are concentrated on what happens when a play sessions ends and is followed immediately by a new session. This happens in iOS 9.2 when you click to the next track or to the previous track. It also happens playing YouTube videos when a Mac's System Audio is routed through AirPlay. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues. * A workaround for an apparent flushing issue in TuneBlade has been included. Thanks to [gibman](https://github.com/gibman) for reporting this issue. * A number of bug fixes have been made to `configure.ac` – thanks to [Jörg Krause](https://github.com/joerg-krause). From 1f76423b4fb22c7a4c9dcf145fbc169d53cd36b7 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Feb 2016 14:01:26 +0000 Subject: [PATCH 24/38] Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e5ecc155..b62d6a4e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,6 @@ Version 2.9.1 – Development Version ---- -Version 2.9.1 is focuses on bug fixes and stability improvements. +Version 2.9.1 focusses on bug fixes and stability improvements. * Stability improvements are concentrated on what happens when a play sessions ends and is followed immediately by a new session. This happens in iOS 9.2 when you click to the next track or to the previous track. It also happens playing YouTube videos when a Mac's System Audio is routed through AirPlay. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues. * A workaround for an apparent flushing issue in TuneBlade has been included. Thanks to [gibman](https://github.com/gibman) for reporting this issue. * A number of bug fixes have been made to `configure.ac` – thanks to [Jörg Krause](https://github.com/joerg-krause). From e525bbe266f55de3316ec782264aa9eeb8c8e2fc Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 22 Feb 2016 14:18:31 +0000 Subject: [PATCH 25/38] Turn off an annoying debug message --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index 2591404c..e31104b4 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -510,7 +510,7 @@ static void flush(void) { } static void stop(void) { - debug(2,"audio_alsa stop called."); + // debug(2,"audio_alsa stop called."); // when we want to stop, we want the alsa device // to be closed immediately -- we may even be killing the thread, so we // don't wish to wait From da50f19b1af2b8ae92d09faf8f878edf65350a59 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 22 Feb 2016 14:42:13 +0000 Subject: [PATCH 26/38] Quieten some more annoying debug messages --- audio_alsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index e31104b4..333ca8c9 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -403,7 +403,7 @@ static void start(int sample_rate) { } static uint32_t delay() { - debug(3,"audio_alsa delay called."); + // debug(3,"audio_alsa delay called."); if (alsa_handle == NULL) { return 0; } else { @@ -441,7 +441,7 @@ static uint32_t delay() { } static void play(short buf[], int samples) { - debug(3,"audio_alsa play called."); + // debug(3,"audio_alsa play called."); int ret = 0; if (alsa_handle == NULL) { pthread_mutex_lock(&alsa_mutex); From 6138099c0348591d3a2d5126d53879484d4b0c77 Mon Sep 17 00:00:00 2001 From: bboffey Date: Tue, 23 Feb 2016 16:25:29 +0000 Subject: [PATCH 27/38] Update TROUBLESHOOTING.md Added details of sound.conf buffer and period tweaks as a workaround for stuttering audio on certain USB DACs --- TROUBLESHOOTING.md | 74 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 63002362..e29e9fd3 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -1,6 +1,6 @@ Troubleshooting ----- -The installation and setup of Shairport Sync is straightforward on recent Linux distributions. Issues can occasionally arise caused by problems elsewhere in the system, typically WiFi reception and/or the WiFi adapter settings, the network, the router, firewall settings. +The installation and setup of Shairport Sync is straightforward on recent Linux distributions. Issues can occasionally arise caused by problems elsewhere in the system, typically WiFi reception and/or the WiFi adapter settings, the network, the router, firewall settings or some more esoteric audio interfaces. In this brief document will be listed some problems and some solutions. @@ -55,3 +55,75 @@ You may have to change the IP adresses range depending on your own local network You can check UFW config by typing `sudo ufw status` in shell. Please make sure that UFW is active, especially if you have deactivated it previously for testing purpose. Run your song from your remote device. Enjoy ! + +### Stuttering audio on certain USB DACs (such as the Creative Soundblaster MP3+) + +**Problem** +When using a USB DAC on a Raspberry Pi audio plays fine through other methods (such as through mpd, mopidy, mplayer or aplay) but when streamed to Shairport Sync regular dropouts or stutters are heard. + +**Possible Cause** +There is a suspicion (although this is not 100% confirmed) that this is a fun latency/timing issue related to a combination of +- The Raspberry Pi's ethernet itself being a USB device resulting in shared bandwidth/interrupts with USB DACs +- Shairport Sync continually checking the latency of the USB DAC to maintain synchronisation of audio +- Quirky USB DACs (already known to be problematic on the Raspberry Pi more info available [here](https://www.raspberrypi.org/documentation/hardware/raspberrypi/usb/README.md#knownissues) +For more discussion on this issue see [issue 167](https://github.com/mikebrady/shairport-sync/issues/167) or read on for the quick fix! + +**Solution** +To get nice smooth audio first check the details of your USB DAC by either using 'aplay -l' which will give you output something like this: +```` +**** List of PLAYBACK Hardware Devices **** +card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA] + Subdevices: 8/8 + Subdevice #0: subdevice #0 + Subdevice #1: subdevice #1 + Subdevice #2: subdevice #2 + Subdevice #3: subdevice #3 + Subdevice #4: subdevice #4 + Subdevice #5: subdevice #5 + Subdevice #6: subdevice #6 + Subdevice #7: subdevice #7 +card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 1: MP3 [Sound Blaster MP3+], device 0: USB Audio [USB Audio] + Subdevices: 0/1 + Subdevice #0: subdevice #0 +```` + +or look at your exisiting '/etc/asound.conf' file, which may look something like this + +```` +pcm.!default { + type hw + card 1 +} +ctl.!default { + type hw + card 1 +} +```` +The important information you want is the card number which in this case is 1. + +Now modify your 'etc/asound.conf' file (or create one if it doesn't exist) using the following template substituting the 'pcm "hw:1"' and 'card 1' sections with the card number of your device + +```` +pcm.!default { + type plug + slave.pcm { + type dmix + ipc_key 1024 + slave { + pcm "hw:1" + rate 48000 + period_time 0 + period_size 1920 + buffer_size 19200 + } + } +} +ctl.!default { + type hw + card 1 +} +```` +Note that some distributions (such as Volumio 2) don't use an asound.conf file by default, they instead specificy the hardware details directly in '/etc/shairport-sync.conf' and '/etc/mpd.conf' files so some more in depth modification is needed to override this. From c1232733b26060e0a610d044d416409f542699fc Mon Sep 17 00:00:00 2001 From: bboffey Date: Tue, 23 Feb 2016 19:05:16 +0000 Subject: [PATCH 28/38] Update TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index e29e9fd3..ce63dd54 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -114,7 +114,7 @@ pcm.!default { ipc_key 1024 slave { pcm "hw:1" - rate 48000 + rate 48000 # this line is only needed for USB DACs which only support 48khz period_time 0 period_size 1920 buffer_size 19200 @@ -126,4 +126,8 @@ ctl.!default { card 1 } ```` +This sets the default alsa audio device to be the USB DAC via a dmixer plugin (which can be used by multiple applications at once) using a modified period and buffer size and optionally mix to 48khz. + +This will then be used by default by Shairport-Sync and any other applications using alsa. + Note that some distributions (such as Volumio 2) don't use an asound.conf file by default, they instead specificy the hardware details directly in '/etc/shairport-sync.conf' and '/etc/mpd.conf' files so some more in depth modification is needed to override this. From e9966089b8eabd15766ee697a12d4020b923a9dd Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 13:36:53 +0000 Subject: [PATCH 29/38] Fix an error whereby the player would wait endlessly when a frame not marked ready (i.e. a missing frame) was presented. --- player.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/player.c b/player.c index 35dd3e5a..0fd36f53 100644 --- a/player.c +++ b/player.c @@ -681,8 +681,9 @@ static abuf_t *buffer_get_frame(void) { // The packet time + (latency + latency offset - backend_buffer_length). // Note: the last three items are expressed in frames and must be converted to time. - int do_wait = 1; + int do_wait = 0; // don't wait unless we can really prove we must if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) { + do_wait = 1; // if the current frame exists and is ready, then wait unless it's time to let it go... uint32_t reference_timestamp; uint64_t reference_timestamp_time,remote_reference_timestamp_time; get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time, &remote_reference_timestamp_time); @@ -766,7 +767,7 @@ static abuf_t *buffer_get_frame(void) { } if (!curframe->ready) { - // debug(1, " %d. Supplying a silent frame.", read); + debug(1, "Supplying a silent frame for frame %u", read); missing_packets++; memset(curframe->data, 0, FRAME_BYTES(frame_size)); curframe->timestamp = 0; From c235dc303149ded69dada31a7261e20e5c3be05f Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 15:06:38 +0000 Subject: [PATCH 30/38] Add debug message calculating packet arrival and disperal times --- rtp.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/rtp.c b/rtp.c index 7e0b1984..37fb5629 100644 --- a/rtp.c +++ b/rtp.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -99,9 +100,41 @@ void *rtp_audio_receiver(void *arg) { int32_t last_seqno = -1; uint8_t packet[2048], *pktp; + uint64_t time_of_previous_packet_fp = 0; + float longest_packet_time_interval_us = 0.0; + + // mean and variance calculations from "online_variance" algorithm at https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm + + int32_t stat_n = 0; + float stat_mean = 0.0; + float stat_M2 = 0.0; + ssize_t nread; while (itr->please_stop==0) { nread = recv(audio_socket, packet, sizeof(packet), 0); + + uint64_t local_time_now_fp = get_absolute_time_in_fp(); + if (time_of_previous_packet_fp) { + float time_interval_us = (((local_time_now_fp - time_of_previous_packet_fp)*1000000)>>32)*1.0; + time_of_previous_packet_fp = local_time_now_fp; + if (time_interval_us>longest_packet_time_interval_us) + longest_packet_time_interval_us=time_interval_us; + stat_n+=1; + float stat_delta = time_interval_us - stat_mean; + stat_mean += stat_delta/stat_n; + stat_M2 += stat_delta*(time_interval_us - stat_mean); + if (stat_n % 2500 == 0) { + debug(2,"Packet reception intervals: mean, standard deviation and max for the last 2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",stat_mean, sqrtf(stat_M2 / (stat_n-1)),longest_packet_time_interval_us); + stat_n = 0; + stat_mean = 0.0; + stat_M2 = 0.0; + time_of_previous_packet_fp = 0; + longest_packet_time_interval_us = 0.0; + } + } else { + time_of_previous_packet_fp = local_time_now_fp; + } + if (nread < 0) break; @@ -121,7 +154,7 @@ void *rtp_audio_receiver(void *arg) { else { last_seqno = (last_seqno + 1) & 0xffff; if (seqno != last_seqno) - debug(2, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno); + debug(3, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno); last_seqno = seqno; // reset warning... } uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4)); @@ -701,7 +734,7 @@ void rtp_shutdown(void) { void rtp_request_resend(seq_t first, uint32_t count) { if (running) { //if (!request_sent) { - debug(2, "requesting resend of %d packets starting at %u.", count, first); + debug(3, "requesting resend of %d packets starting at %u.", count, first); // request_sent = 1; //} From fff33fe619b5327d446fff67b008332cef543304 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 15:07:03 +0000 Subject: [PATCH 31/38] Quieten a few debug messages --- audio_alsa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 333ca8c9..3da9cf36 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -123,7 +123,7 @@ int open_mixer() { static int init(int argc, char **argv) { pthread_mutex_lock(&alsa_mutex); - debug(2,"audio_alsa init called."); + // debug(2,"audio_alsa init called."); const char *str; int value; @@ -285,7 +285,7 @@ static int init(int argc, char **argv) { } static void deinit(void) { - debug(2,"audio_alsa deinit called."); + // debug(2,"audio_alsa deinit called."); stop(); if (hardware_mixer && alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); @@ -396,7 +396,7 @@ int open_alsa_device(void) { } static void start(int sample_rate) { - debug(2,"audio_alsa start called."); + // debug(2,"audio_alsa start called."); if (sample_rate != 44100) die("Unexpected sample rate %d -- only 44,100 supported!", sample_rate); desired_sample_rate = sample_rate; // must be a variable @@ -476,7 +476,7 @@ static void play(short buf[], int samples) { } static void flush(void) { - debug(2,"audio_alsa flush called."); + // debug(2,"audio_alsa flush called."); pthread_mutex_lock(&alsa_mutex); int derr; if (hardware_mixer && alsa_mix_handle) { @@ -557,7 +557,7 @@ static void linear_volume(double vol) { static void mute(int do_mute) { pthread_mutex_lock(&alsa_mutex); - debug(2,"audio_alsa mute called."); + // debug(2,"audio_alsa mute called."); if (hardware_mixer && alsa_mix_handle) { if (do_mute) { // debug(1,"Mute"); From 95f16898588cf1017ef8e32c2cc468fd38365065 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 15:15:55 +0000 Subject: [PATCH 32/38] Quieten an out-of-sequence message --- rtp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtp.c b/rtp.c index 37fb5629..f842932d 100644 --- a/rtp.c +++ b/rtp.c @@ -124,7 +124,7 @@ void *rtp_audio_receiver(void *arg) { stat_mean += stat_delta/stat_n; stat_M2 += stat_delta*(time_interval_us - stat_mean); if (stat_n % 2500 == 0) { - debug(2,"Packet reception intervals: mean, standard deviation and max for the last 2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",stat_mean, sqrtf(stat_M2 / (stat_n-1)),longest_packet_time_interval_us); + debug(3,"Packet reception intervals: mean, standard deviation and max for the last 2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",stat_mean, sqrtf(stat_M2 / (stat_n-1)),longest_packet_time_interval_us); stat_n = 0; stat_mean = 0.0; stat_M2 = 0.0; @@ -153,8 +153,8 @@ void *rtp_audio_receiver(void *arg) { last_seqno = seqno; else { last_seqno = (last_seqno + 1) & 0xffff; - if (seqno != last_seqno) - debug(3, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno); + //if (seqno != last_seqno) + // debug(3, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno); last_seqno = seqno; // reset warning... } uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4)); From c8273d7768e510e8ecbdd3495e64be5064323811 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 15:20:26 +0000 Subject: [PATCH 33/38] Bump bugfix number -- fix an important bug dealing with truly msissing packets --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7ca65ee6..aa297af7 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], [2.9.1], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [2.9.2], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From ae604ff77f3c3458a7e76d83347b03ddbbf9378e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 15:35:53 +0000 Subject: [PATCH 34/38] Update RELEASENOTES.md --- RELEASENOTES.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b62d6a4e..a214e2fa 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,9 @@ +Version 2.9.2 – Development Version +---- +Version 2.9.2 focusses on further bug fixes and stability improvements. +* An important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, in spite of having been re-requested. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it. +* An extra diagnostic has been added which gives the mean, standard deviation and maximum values for inter-packet reception time on the audio port. It may be useful for exploring line quality. + Version 2.9.1 – Development Version ---- Version 2.9.1 focusses on bug fixes and stability improvements. @@ -24,8 +30,8 @@ Version 2.8 is derived from version 2.7.10 with slight documentation updates. He * Disabled picture sending if pictures haven’t been asked for. **Bug fixes** -* Fixed a bug that prevented Shairport Sync from correctly setting the hardware mixer volume if it had been altered externally. -* Modified the shutdown behaviour so that a shutdown followed immediately by a play request is handled properly. This was causing iOS 9.2 sometimes to drop the Airplay link between tunes. +* Fixed a bug that prevented Shairport Sync from correctly setting the hardware mixer volume if it had been altered externally. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues. +* Modified the shutdown behaviour so that a shutdown followed immediately by a play request is handled better. This was causing iOS 9.2 sometimes to drop the Airplay link between tunes. * Fixed a data-alignment bug that would cause a crash in certain circumstances on ARM processors with metadata enabled. * Corrected the names for a few settings tags. * Fixed some typos and misspellings. From f4d2631d1c085378a163bab64e1338f93013d46e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 19:54:51 +0000 Subject: [PATCH 35/38] Quieten some unnecessary debug messages. --- player.c | 2 +- rtsp.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/player.c b/player.c index 0fd36f53..ebd7cf19 100644 --- a/player.c +++ b/player.c @@ -767,7 +767,7 @@ static abuf_t *buffer_get_frame(void) { } if (!curframe->ready) { - debug(1, "Supplying a silent frame for frame %u", read); + // debug(1, "Supplying a silent frame for frame %u", read); missing_packets++; memset(curframe->data, 0, FRAME_BYTES(frame_size)); curframe->timestamp = 0; diff --git a/rtsp.c b/rtsp.c index 0699c681..1d589752 100644 --- a/rtsp.c +++ b/rtsp.c @@ -288,12 +288,12 @@ static void track_thread(rtsp_conn_info *conn) { static void cleanup_threads(void) { void *retval; int i; - debug(2, "culling threads."); + // debug(2, "culling threads."); for (i = 0; i < nconns;) { if (conns[i]->running == 0) { pthread_join(conns[i]->thread, &retval); free(conns[i]); - debug(2, "one joined..."); + debug(2, "one thread joined..."); nconns--; if (nconns) conns[i] = conns[nconns]; From 7ba3b7bfc4d42c63db691995b5ba275780e31a3d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 20:38:15 +0000 Subject: [PATCH 36/38] Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a214e2fa..360b4263 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,7 +1,7 @@ Version 2.9.2 – Development Version ---- Version 2.9.2 focusses on further bug fixes and stability improvements. -* An important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, in spite of having been re-requested. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it. +* Enhanced stability: an important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, in spite of having been re-requested. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it. * An extra diagnostic has been added which gives the mean, standard deviation and maximum values for inter-packet reception time on the audio port. It may be useful for exploring line quality. Version 2.9.1 – Development Version From 63d24a339a814ca1df7cd7781faa2f7d1c04ae15 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 29 Feb 2016 20:39:32 +0000 Subject: [PATCH 37/38] Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 360b4263..e40c644e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,7 +1,7 @@ Version 2.9.2 – Development Version ---- Version 2.9.2 focusses on further bug fixes and stability improvements. -* Enhanced stability: an important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, in spite of having been re-requested. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it. +* Enhanced stability: an important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, after all attempts to fetch it have been unsuccessful. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it. * An extra diagnostic has been added which gives the mean, standard deviation and maximum values for inter-packet reception time on the audio port. It may be useful for exploring line quality. Version 2.9.1 – Development Version From b4d91b54092191b552f89b931d43265a2814933a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 1 Mar 2016 12:48:16 +0000 Subject: [PATCH 38/38] Update TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index ce63dd54..5fb5e680 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -4,6 +4,8 @@ The installation and setup of Shairport Sync is straightforward on recent Linux In this brief document will be listed some problems and some solutions. +Before starting, ensure that your software is up-to-date. + ### WiFi adapter running in power-saving / low-power mode **Problem** @@ -68,7 +70,7 @@ There is a suspicion (although this is not 100% confirmed) that this is a fun la - Quirky USB DACs (already known to be problematic on the Raspberry Pi more info available [here](https://www.raspberrypi.org/documentation/hardware/raspberrypi/usb/README.md#knownissues) For more discussion on this issue see [issue 167](https://github.com/mikebrady/shairport-sync/issues/167) or read on for the quick fix! -**Solution** +**Possible Solution** To get nice smooth audio first check the details of your USB DAC by either using 'aplay -l' which will give you output something like this: ```` **** List of PLAYBACK Hardware Devices **** @@ -130,4 +132,6 @@ This sets the default alsa audio device to be the USB DAC via a dmixer plugin (w This will then be used by default by Shairport-Sync and any other applications using alsa. -Note that some distributions (such as Volumio 2) don't use an asound.conf file by default, they instead specificy the hardware details directly in '/etc/shairport-sync.conf' and '/etc/mpd.conf' files so some more in depth modification is needed to override this. +Note that some distributions (such as Volumio 2) don't use an asound.conf file by default, they instead specificy the hardware details directly in '/etc/mpd.conf' files so some more in-depth modification is needed to override this. + +(Note: not tested by Mike B.)