]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Stop using SIGUSR1 for cancelling threads, use pthread_cancel and friends instead...
authorMike Brady <mikebrady@eircom.net>
Mon, 30 Jul 2018 09:07:11 +0000 (10:07 +0100)
committerMike Brady <mikebrady@eircom.net>
Mon, 30 Jul 2018 09:07:11 +0000 (10:07 +0100)
14 files changed:
1  2 
audio.h
audio_alsa.c
audio_ao.c
audio_dummy.c
audio_soundio.c
common.c
common.h
dacp.c
metadata_hub.c
player.c
player.h
rtp.c
rtsp.c
shairport.c

diff --cc audio.h
index dc7eb873f7dcd984a019c25a7ae47a2209c1d5ed,323dbc4f2467a3e69410270408cbada3c6823a24..fc06a04c5f9136000c1da858404e88667e3f785b
+++ b/audio.h
@@@ -33,6 -33,7 +33,8 @@@ typedef struct 
    // will change dynamically, so keep watching it. Implemented in ALSA only.
    // returns a negative error code if there's a problem
    int (*delay)(long *the_delay); // snd_pcm_sframes_t is a signed long
 -  int (*rate_info)(uint64_t *elapsed_time, uint64_t *frames_played); // use this to get the true rate of the DAC
++  int (*rate_info)(uint64_t *elapsed_time,
++                   uint64_t *frames_played); // use this to get the true rate of the DAC
  
    // may be NULL, in which case soft volume is applied
    void (*volume)(double vol);
diff --cc audio_alsa.c
index e2a53ccd785a8b63e15cdd901348acb8b86453b5,8fd18c378a4d9dac0b841ea28c96a023e536e5af..27995d57a11d269cf37c12e35607194b6282d80b
@@@ -30,6 -30,6 +30,7 @@@
  #include "audio.h"
  #include "common.h"
  #include <alsa/asoundlib.h>
++#include <inttypes.h>
  #include <math.h>
  #include <memory.h>
  #include <pthread.h>
@@@ -128,25 -142,38 +142,38 @@@ int open_mixer() 
      snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index);
      snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl);
  
-     if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0)
-       die("Failed to open mixer");
-     debug(3, "Mixer device name is \"%s\".", alsa_mix_dev);
-     if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0)
-       die("Failed to attach mixer");
-     if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0)
-       die("Failed to register mixer element");
-     ret = snd_mixer_load(alsa_mix_handle);
-     if (ret < 0)
-       die("Failed to load mixer element");
-     debug(3, "Mixer Control name is \"%s\".", alsa_mix_ctrl);
-     alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);
-     if (!alsa_mix_elem)
-       die("Failed to find mixer element");
-     return 1;
-   } else {
-     return 0;
+     if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0) {
 -      debug(1,"Failed to open mixer");
++      debug(1, "Failed to open mixer");
+       response = -1;
+     } else {
+       debug(3, "Mixer device name is \"%s\".", alsa_mix_dev);
+       if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) {
 -        debug(1,"Failed to attach mixer");
++        debug(1, "Failed to attach mixer");
+         response = -2;
+       } else {
+         if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) {
 -          debug(1,"Failed to register mixer element");
++          debug(1, "Failed to register mixer element");
+           response = -3;
+         } else {
+           ret = snd_mixer_load(alsa_mix_handle);
+           if (ret < 0) {
 -            debug(1,"Failed to load mixer element");
++            debug(1, "Failed to load mixer element");
+             response = -4;
+           } else {
+             debug(3, "Mixer Control name is \"%s\".", alsa_mix_ctrl);
+             alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);
+             if (!alsa_mix_elem) {
 -              debug(1,"Failed to find mixer element");
++              debug(1, "Failed to find mixer element");
+               response = -5;
+             } else {
+               response = 1; // we found a hardware mixer and successfully opened it
+             }
+           }
+         }
+       }
+     }
 -  } 
 +  }
+   return response;
  }
  
  void close_mixer() {
@@@ -219,8 -246,8 +246,9 @@@ static int init(int argc, char **argv) 
        else if (strcasecmp(str, "yes") == 0)
          config.no_sync = 1;
        else {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or \"no\"");
 -        warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or \"no\". It is set to \"no\".");
++        warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or "
++             "\"no\". It is set to \"no\".");
+         config.no_sync = 0;
        }
      }
  
        else if (strcasecmp(str, "yes") == 0)
          config.alsa_use_hardware_mute = 1;
        else {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid mute_using_playback_switch option choice \"%s\". It should be \"yes\" or "
-             "\"no\"");
+         warn("Invalid mute_using_playback_switch option choice \"%s\". It should be \"yes\" or "
 -            "\"no\". It is set to \"no\".");
++             "\"no\". It is set to \"no\".");
+         config.alsa_use_hardware_mute = 0;
        }
      }
  
        else if (strcasecmp(str, "yes") == 0)
          config.alsa_use_hardware_mute = 1;
        else {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid use_hardware_mute_if_available option choice \"%s\". It should be \"yes\" or "
-             "\"no\"");
+         warn("Invalid use_hardware_mute_if_available option choice \"%s\". It should be \"yes\" or "
 -            "\"no\". It is set to \"no\".");
++             "\"no\". It is set to \"no\".");
+         config.alsa_use_hardware_mute = 0;
        }
      }
  
        else if (strcasecmp(str, "S8") == 0)
          config.output_format = SPS_FORMAT_S8;
        else {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid output format \"%s\". It should be \"U8\", \"S8\", \"S16\", \"S24\", "
-             "\"S24_3LE\", \"S24_3BE\" or "
-             "\"S32\"",
-             str);
+         warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", \"S16\", \"S24\", "
 -            "\"S24_3LE\", \"S24_3BE\" or "
 -            "\"S32\". It is set to \"S16\".",
 -            str);
++             "\"S24_3LE\", \"S24_3BE\" or "
++             "\"S32\". It is set to \"S16\".",
++             str);
+         config.output_format = SPS_FORMAT_S16;
        }
      }
  
          config.output_rate = value;
          break;
        default:
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800", value);
 -        warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800. It is set to 44,100", value);
++        warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800. It is "
++             "set to 44,100",
++             value);
+         config.output_rate = 44100;
        }
      }
  
        else if (strcasecmp(str, "yes") == 0)
          config.no_mmap = 0;
        else {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\"");
 -        warn("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\". It is set to \"yes\".");
++        warn("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\". "
++             "It is set to \"yes\".");
+         config.no_mmap = 0;
        }
      }
      /* Get the optional period size value */
        set_period_size_request = 1;
        debug(1, "Value read for period size is %d.", value);
        if (value < 0) {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid alsa period size setting \"%d\". It "
-             "must be greater than 0.",
-             value);
+         warn("Invalid alsa period size setting \"%d\". It "
 -            "must be greater than 0. No setting is made.",
 -            value);
++             "must be greater than 0. No setting is made.",
++             value);
+         set_period_size_request = 0;
        } else {
          period_size_requested = value;
        }
        set_buffer_size_request = 1;
        debug(1, "Value read for buffer size is %d.", value);
        if (value < 0) {
-         debug_mutex_unlock(&alsa_mutex, 3);
-         die("Invalid alsa buffer size setting \"%d\". It "
-             "must be greater than 0.",
-             value);
+         warn("Invalid alsa buffer size setting \"%d\". It "
 -            "must be greater than 0. No setting is made.",
 -            value);
++             "must be greater than 0. No setting is made.",
++             value);
+         set_buffer_size_request = 0;
        } else {
          buffer_size_requested = value;
        }
      if (alsa_mix_dev == NULL)
        alsa_mix_dev = alsa_out_dev;
  
-     // 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) {
-         audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
-         audio_alsa.parameters = &parameters; // likewise the parameters stuff
-         if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
-           // For instance, the Raspberry Pi does this
-           debug(1, "Lowest dB value is a mute");
-           mixer_volume_setting_gives_mute = 1;
-           alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be necessary -- it's always
-                                                     // going to be SND_CTL_TLV_DB_GAIN_MUTE, right?
-           // debug(1, "Try minimum volume + 1 as lowest true attenuation value");
-           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 minimum volume + 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.",
-               alsa_mix_ctrl);
-         if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) {
-           debug_mutex_unlock(&alsa_mutex, 3);
-           die("Cannot open control \"%s\"", alsa_mix_dev);
-         }
-         if (snd_ctl_elem_id_malloc(&elem_id) < 0) {
-           debug_mutex_unlock(&alsa_mutex, 3);
-           die("Cannot allocate memory for control \"%s\"", alsa_mix_dev);
-         }
-         snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
-         snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
+     // Now, start trying to initialise the alsa device with the settings obtained
+     pthread_cleanup_debug_mutex_lock(&alsa_mutex, 1000, 1);
 -    if (open_mixer()==1) {
++    if (open_mixer() == 1) {
+       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_dB_range(alsa_mix_elem, &alsa_mix_mindb,
++                                                  &alsa_mix_maxdb) == 0) {
  
-         if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
-           debug(1, "Volume control \"%s\" has dB volume from %f to %f.", alsa_mix_ctrl,
-                 (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
-           has_softvol = 1;
            audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
            audio_alsa.parameters = &parameters; // likewise the parameters stuff
 -                                                      // going to be SND_CTL_TLV_DB_GAIN_MUTE, right?
+           if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
+             // For instance, the Raspberry Pi does this
+             debug(1, "Lowest dB value is a mute");
+             mixer_volume_setting_gives_mute = 1;
+             alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be necessary -- it's always
++            // going to be SND_CTL_TLV_DB_GAIN_MUTE, right?
+             // debug(1, "Try minimum volume + 1 as lowest true attenuation value");
+             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 minimum volume + 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 {
-           debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
+           // use the linear scale and do the db conversion ourselves
+           warn("The hardware mixer specified -- \"%s\" -- does not have "
 -                "a dB volume scale.",
 -                alsa_mix_ctrl);
++               "a dB volume scale.",
++               alsa_mix_ctrl);
+           if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) {
+             warn("Cannot open control \"%s\"", alsa_mix_dev);
+             response = -1;
+           }
+           if (snd_ctl_elem_id_malloc(&elem_id) < 0) {
 -            debug(1,"Cannot allocate memory for control \"%s\"", alsa_mix_dev);
++            debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev);
+             elem_id = NULL;
+             response = -2;
+           } else {
+             snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
+             snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
+             if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
+               debug(1, "Volume control \"%s\" has dB volume from %f to %f.", alsa_mix_ctrl,
+                     (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
+               has_softvol = 1;
 -              audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
++              audio_alsa.volume =
++                  &volume; // insert the volume function now we know it can do dB stuff
+               audio_alsa.parameters = &parameters; // likewise the parameters stuff
+             } else {
+               debug(1, "Cannot get the dB range from the volume control \"%s\"", 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 = &parameters; // likewise the parameters stuff
+           debug(1,"Max and min dB calculated are %d and
+           %d.",alsa_mix_maxdb,alsa_mix_mindb);
+           */
          }
-         /*
-         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 = &parameters; // likewise the parameters stuff
-         debug(1,"Max and min dB calculated are %d and
-         %d.",alsa_mix_maxdb,alsa_mix_mindb);
-         */
        }
+       if (((config.alsa_use_hardware_mute == 1) &&
+            (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) ||
+           mixer_volume_setting_gives_mute) {
+         audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff
+         // debug(1, "Has mixer and mute ability we will use.");
+       } else {
+         // debug(1, "Has mixer but not using hardware mute.");
+       }
+       close_mixer();
      }
-     if (((config.alsa_use_hardware_mute == 1) &&
-          (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) ||
-         mixer_volume_setting_gives_mute) {
-       audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff
-       // debug(1, "Has mixer and mute ability we will use.");
-     } else {
-       // debug(1, "Has mixer but not using hardware mute.");
-     }
-     close_mixer();
+     debug_mutex_unlock(&alsa_mutex, 3);
+     pthread_cleanup_pop(0); // release the mutex
    } else {
      // debug(1, "Has no mixer and thus no hardware mute.");
    }
@@@ -507,11 -533,10 +538,10 @@@ int open_alsa_device(void) 
  
    ret = snd_pcm_hw_params_any(alsa_handle, alsa_params);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     ;
-     die("audio_alsa: Broken configuration for device \"%s\": no configurations "
-         "available",
-         alsa_out_dev);
+     warn("audio_alsa: Broken configuration for device \"%s\": no configurations "
 -        "available",
 -        alsa_out_dev);
++         "available",
++         alsa_out_dev);
+     return -11;
    }
  
    if ((config.no_mmap == 0) &&
  
    ret = snd_pcm_hw_params_set_access(alsa_handle, alsa_params, access);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     die("audio_alsa: Access type not available for device \"%s\": %s", alsa_out_dev,
-         snd_strerror(ret));
+     warn("audio_alsa: Access type not available for device \"%s\": %s", alsa_out_dev,
 -        snd_strerror(ret));
++         snd_strerror(ret));
+     return -12;
    }
    snd_pcm_format_t sf;
    switch (sample_format) {
      sf = SND_PCM_FORMAT_S32;
      break;
    default:
-     debug_mutex_unlock(&alsa_mutex, 3);
      sf = SND_PCM_FORMAT_S16; // this is just to quieten a compiler warning
-     die("Unsupported output format at audio_alsa.c");
 -    debug(1,"Unsupported output format at audio_alsa.c");
++    debug(1, "Unsupported output format at audio_alsa.c");
+     return -1;
    }
    ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     die("audio_alsa: Sample format %d not available for device \"%s\": %s", sample_format,
-         alsa_out_dev, snd_strerror(ret));
+     warn("audio_alsa: Sample format %d not available for device \"%s\": %s", sample_format,
 -        alsa_out_dev, snd_strerror(ret));
++         alsa_out_dev, snd_strerror(ret));
+     return -2;
    }
  
    ret = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 2);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     die("audio_alsa: Channels count (2) not available for device \"%s\": %s", alsa_out_dev,
-         snd_strerror(ret));
+     warn("audio_alsa: Channels count (2) not available for device \"%s\": %s", alsa_out_dev,
 -        snd_strerror(ret));
++         snd_strerror(ret));
+     return -3;
    }
  
    ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &my_sample_rate, &dir);
    if (ret < 0) {
-     die("audio_alsa: Rate %iHz not available for playback: %s", desired_sample_rate,
-         snd_strerror(ret));
+     warn("audio_alsa: Rate %iHz not available for playback: %s", desired_sample_rate,
 -        snd_strerror(ret));
++         snd_strerror(ret));
+     return -4;
    }
  
    if (set_period_size_request != 0) {
      ret = snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, &period_size_requested,
                                                   &dir);
      if (ret < 0) {
-       debug_mutex_unlock(&alsa_mutex, 3);
-       die("audio_alsa: cannot set period size of %lu: %s", period_size_requested,
-           snd_strerror(ret));
+       warn("audio_alsa: cannot set period size of %lu: %s", period_size_requested,
 -          snd_strerror(ret));
++           snd_strerror(ret));
+       return -5;
+     } else {
        snd_pcm_uframes_t actual_period_size;
        snd_pcm_hw_params_get_period_size(alsa_params, &actual_period_size, &dir);
        if (actual_period_size != period_size_requested)
      debug(1, "Attempting to set the buffer size to %lu", buffer_size_requested);
      ret = snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params, &buffer_size_requested);
      if (ret < 0) {
-       debug_mutex_unlock(&alsa_mutex, 3);
-       die("audio_alsa: cannot set buffer size of %lu: %s", buffer_size_requested,
-           snd_strerror(ret));
+       warn("audio_alsa: cannot set buffer size of %lu: %s", buffer_size_requested,
 -          snd_strerror(ret));
++           snd_strerror(ret));
+       return -6;
+     } else {
+       snd_pcm_uframes_t actual_buffer_size;
+       snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size);
+       if (actual_buffer_size != buffer_size_requested)
+         inform("Actual period size set to a different value than requested. Requested: %lu, actual "
+                "setting: %lu",
+                buffer_size_requested, actual_buffer_size);
      }
-     snd_pcm_uframes_t actual_buffer_size;
-     snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size);
-     if (actual_buffer_size != buffer_size_requested)
-       inform("Actual period size set to a different value than requested. Requested: %lu, actual "
-              "setting: %lu",
-              buffer_size_requested, actual_buffer_size);
    }
  
    ret = snd_pcm_hw_params(alsa_handle, alsa_params);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     die("audio_alsa: Unable to set hw parameters for device \"%s\": %s.", alsa_out_dev,
-         snd_strerror(ret));
+     warn("audio_alsa: Unable to set hw parameters for device \"%s\": %s.", alsa_out_dev,
 -        snd_strerror(ret));
++         snd_strerror(ret));
+     return -7;
    }
  
    if (my_sample_rate != desired_sample_rate) {
  
    ret = snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_length);
    if (ret < 0) {
-     debug_mutex_unlock(&alsa_mutex, 3);
-     die("audio_alsa: Unable to get hw buffer length for device \"%s\": %s.", alsa_out_dev,
-         snd_strerror(ret));
+     warn("audio_alsa: Unable to get hw buffer length for device \"%s\": %s.", alsa_out_dev,
 -        snd_strerror(ret));
++         snd_strerror(ret));
+     return -9;
    }
  
    if (actual_buffer_length < config.audio_backend_buffer_desired_length + minimal_buffer_headroom) {
@@@ -808,6 -837,9 +841,9 @@@ static void start(int i_sample_rate, in
      sample_format = SPS_FORMAT_S16; // default
    else
      sample_format = i_sample_format;
 -  
++
+   frame_index = 0;
+   measurement_data_is_valid = 0;
  }
  
  int delay(long *the_delay) {
                snd_strerror(reply), *the_delay);
          snd_pcm_recover(alsa_handle, reply, 1);
        }
-     } else if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) {
-       *the_delay = 0;
-       reply = 0; // no error
      } else {
-       if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN) {
 -      frame_index = 0; //we'll be starting over...
++      frame_index = 0; // we'll be starting over...
+       measurement_data_is_valid = 0;
+       if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) {
          *the_delay = 0;
          reply = 0; // no error
        } else {
    }
  }
  
- static void play(void *buf, int samples) {
+ int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
+   if (measurement_data_is_valid) {
+     *elapsed_time = measurement_time - measurement_start_time;
+     *frames_played = frames_played_at_measurement_time - frames_played_at_measurement_start_time;
+   } else {
+     *elapsed_time = 0;
 -    *frames_played = 0;  
++    *frames_played = 0;
+   }
+   return 0;
+ }
+ static int play(void *buf, int samples) {
    // debug(3,"audio_alsa play called.");
    int ret = 0;
    if (alsa_handle == NULL) {
        if (audio_alsa.mute)
          do_mute(0);
      }
 -    
++
      debug_mutex_unlock(&alsa_mutex, 3);
+     pthread_cleanup_pop(0); // release the mutex
    }
    if (ret == 0) {
-     debug_mutex_lock(&alsa_mutex, 10000, 1);
+     pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
      //    snd_pcm_sframes_t current_delay = 0;
-     int err;
+     int err, err2;
      if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN) {
        if ((err = snd_pcm_prepare(alsa_handle))) {
          snd_pcm_recover(alsa_handle, err, 1);
          debug(1, "Error preparing after underrun: \"%s\".", snd_strerror(err));
        }
-     }
-     if ((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) ||
-         (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)) {
+       frame_index = 0; // we'll be starting over
+       measurement_data_is_valid = 0;
+     } else if ((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) ||
 -        (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)) {
++               (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)) {
        if (buf == NULL)
          debug(1, "NULL buffer passed to pcm_writei -- skipping it");
        if (samples == 0)
                  snd_strerror(err));
            snd_pcm_recover(alsa_handle, err, 1);
          }
 -        if (frame_index==0) {
++        if (frame_index == 0) {
+           frames_sent_for_playing = samples;
+         } else {
+           frames_sent_for_playing += samples;
+         }
 -        const uint64_t start_measurement_from_this_frame = (2*44100)/352; // two seconds of frames…
++        const uint64_t start_measurement_from_this_frame =
++            (2 * 44100) / 352; // two seconds of frames…
+         frame_index++;
+         if ((frame_index == start_measurement_from_this_frame) || (frame_index % 32 == 0)) {
+           long fl = 0;
 -          err2 = snd_pcm_delay(alsa_handle,&fl);
++          err2 = snd_pcm_delay(alsa_handle, &fl);
+           if (err2 != 0) {
+             frame_index = 0;
+             measurement_data_is_valid = 0;
+             debug(1, "Error %d in delay(): \"%s\". Delay reported is %d frames.", err2,
+                   snd_strerror(err2), fl);
+             snd_pcm_recover(alsa_handle, err2, 1);
+           }
+           uint64_t tf = get_absolute_time_in_fp();
+           frames_played_at_measurement_time = frames_sent_for_playing - fl;
+           if (frame_index == start_measurement_from_this_frame) {
+             frames_played_at_measurement_start_time = frames_played_at_measurement_time;
+             measurement_start_time = tf;
+           } else {
+             frames_played_at_measurement_time = frames_played_at_measurement_time;
+             measurement_time = tf;
+             measurement_data_is_valid = 1;
+           }
+         }
        }
      } else {
        debug(1, "Error -- ALSA device in incorrect state (%d) for play.",
  
  static void flush(void) {
    // debug(2,"audio_alsa flush called.");
-   debug_mutex_lock(&alsa_mutex, 10000, 1);
+   pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
    int derr;
    do_mute(1);
 -  
++
    if (alsa_handle) {
  
      if ((derr = snd_pcm_drop(alsa_handle)))
@@@ -941,7 -1030,7 +1035,7 @@@ static void parameters(audio_parameter
  void do_volume(double vol) { // caller is assumed to have the alsa_mutex when using this function
    debug(3, "Setting volume db to %f.", vol);
    set_volume = vol;
-   if (volume_set_request && open_mixer()) {
 -  if (volume_set_request && (open_mixer()==1)) {
++  if (volume_set_request && (open_mixer() == 1)) {
      if (has_softvol) {
        if (ctl && elem_id) {
          snd_ctl_elem_value_t *value;
@@@ -1024,7 -1115,7 +1120,7 @@@ void do_mute(int mute_state_requested) 
    if (config.alsa_use_hardware_mute == 1) {
      if (mute_request_pending == 0)
        local_mute_state_requested = mute_state_requested;
-     if (open_mixer()) {
 -    if (open_mixer()==1) {
++    if (open_mixer() == 1) {
        if (local_mute_state_requested) {
          // debug(1,"Playback Switch mute actually done");
          if (snd_mixer_selem_has_playback_switch(alsa_mix_elem))
diff --cc audio_ao.c
index 8a78e88045d2811c75d4336b2afa17d3a17c8ac2,de8861e8371cee1977334dbad4d58dfa4fc1d2cc..25ed83e9aaef44c1898400462a0314443c713d0b
@@@ -121,7 -121,9 +121,7 @@@ static void deinit(void) 
  static void start(__attribute__((unused)) int sample_rate,
                    __attribute__((unused)) int sample_format) {}
  
- static void play(void *buf, int samples) { ao_play(dev, buf, samples * 4); }
 -static int play(void *buf, int samples) {
 -  return ao_play(dev, buf, samples * 4);
 -}
++static int play(void *buf, int samples) { return ao_play(dev, buf, samples * 4); }
  
  static void stop(void) {}
  
diff --cc audio_dummy.c
index 5f55bab15056065d74559d8fe6f0232623f91395,e29d389b311c35d06641ce7cd8cde4689566b0d6..3f30cb4bae8009ed215222716bd666594022bfa9
@@@ -48,7 -48,7 +48,9 @@@ static void start(int sample_rate, __at
    debug(1, "dummy audio output started at Fs=%d Hz\n", sample_rate);
  }
  
- static void play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) {}
 -static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) { return 0; }
++static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) {
++  return 0;
++}
  
  static void stop(void) { debug(1, "dummy audio stopped\n"); }
  
diff --cc audio_soundio.c
Simple merge
diff --cc common.c
index 2c8810c93e104c4826f0c89ae52abec3de8be530,cd3d5139356190d71265c16579283906bd88ab24..85aeceecd6a97939bbcea01d30371d438ff725d6
+++ b/common.c
@@@ -732,21 -746,17 +746,17 @@@ double flat_vol2attn(double vol, long m
  
  double vol2attn(double vol, long max_db, long min_db) {
  
- // We use a little coordinate geometry to build a transfer function from the volume passed in to the
- // device's dynamic range.
- // (See the diagram in the documents folder.)
- // The x axis is the "volume in" which will be from -30 to 0. The y axis will be the "volume out"
- // which will be from the bottom of the range to the top.
- // We build the transfer function from one or more lines. We characterise each line with two
- // numbers:
- // the first is where on x the line starts when y=0 (x can be from 0 to -30); the second is where on
- // y the line stops when when x is -30.
- // thus, if the line was characterised as {0,-30}, it would be an identity transfer.
- // Assuming, for example, a dynamic range of lv=-60 to hv=0
- // Typically we'll use three lines -- a three order transfer function
- // First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
 -  // We use a little coordinate geometry to build a transfer function from the volume passed in to
 -  // the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
 -  // "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
 -  // the bottom of the range to the top. We build the transfer function from one or more lines. We
 -  // characterise each line with two numbers: the first is where on x the line starts when y=0 (x
 -  // can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
 -  // line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
 -  // dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
 -  // function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
 -  // Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
 -  // Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
++// We use a little coordinate geometry to build a transfer function from the volume passed in to
++// the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
++// "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
++// the bottom of the range to the top. We build the transfer function from one or more lines. We
++// characterise each line with two numbers: the first is where on x the line starts when y=0 (x
++// can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
++// line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
++// dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
++// function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
 +// Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
 +// Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
  
  #define order 3
  
@@@ -1128,6 -1141,10 +1141,8 @@@ int _debug_mutex_unlock(pthread_mutex_
    return r;
  }
  
 -void pthread_cleanup_debug_mutex_unlock(void *arg) {
 -  pthread_mutex_unlock((pthread_mutex_t* )arg);
 -}
++void pthread_cleanup_debug_mutex_unlock(void *arg) { pthread_mutex_unlock((pthread_mutex_t *)arg); }
  char *get_version_string() {
    char *version_string = malloc(200);
    if (version_string) {
diff --cc common.h
index 86cfa1a16c6193b6705b83cc7f191c7384ba6f73,51eb29e60e3075b9775588cc0f48a77b9d9c8b9e..8b2baffa58631d12b423b2f2b9d5455f3ff2193b
+++ b/common.h
@@@ -292,6 -298,11 +298,12 @@@ int _debug_mutex_unlock(pthread_mutex_
  
  #define debug_mutex_unlock(mu, d) _debug_mutex_unlock(mu, __FILE__, __LINE__, d)
  
 -
+ void pthread_cleanup_debug_mutex_unlock(void *arg);
 -#define pthread_cleanup_debug_mutex_lock(mu, t, d) if (_debug_mutex_lock(mu, t, __FILE__, __LINE__, d) == 0) pthread_cleanup_push(pthread_cleanup_debug_mutex_unlock,(void *)mu)
++#define pthread_cleanup_debug_mutex_lock(mu, t, d)                                                 \
++  if (_debug_mutex_lock(mu, t, __FILE__, __LINE__, d) == 0)                                        \
++  pthread_cleanup_push(pthread_cleanup_debug_mutex_unlock, (void *)mu)
  char *get_version_string(); // mallocs a string space -- remember to free it afterwards
  
  void sps_nanosleep(const time_t sec,
diff --cc dacp.c
Simple merge
diff --cc metadata_hub.c
Simple merge
diff --cc player.c
index 3c60e24deec916e508332f8fdbff3713e6ac83e5,a3739d0b3cbfd886b938c987b036acd11d1e1f28..a143740ea8bf9a3fb9c659de2b36c96a23dda719
+++ b/player.c
@@@ -315,18 -315,18 +315,16 @@@ static int alac_decode(short *dest, in
    }
  
    if (outsize > toutsize) {
--    debug(2,
--          "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
--          "truncated, but buffer overflow possible! Encrypted = %d.",
++    debug(2, "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
++             "truncated, but buffer overflow possible! Encrypted = %d.",
            outsize, toutsize, conn->stream.encrypted);
      reply = -1; // output packet is the wrong size
    }
  
    *destlen = outsize / conn->input_bytes_per_frame;
    if ((outsize % conn->input_bytes_per_frame) != 0)
--    debug(1,
--          "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
--          "and the audio frame size (%d).",
++    debug(1, "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
++             "and the audio frame size (%d).",
            *destlen, outsize, conn->input_bytes_per_frame);
    return reply;
  }
@@@ -465,178 -466,164 +464,163 @@@ static void free_audio_buffers(rtsp_con
      free(conn->audio_buffer[i].data);
  }
  
- void player_thread_lock_cleanup(void *arg) {
-   rtsp_conn_info *conn = (rtsp_conn_info *)arg;
-   debug(3, "Cleaning up player_thread_lock.");
-   pthread_rwlock_unlock(&conn->player_thread_lock);
- }
  void player_put_packet(seq_t seqno, uint32_t actual_timestamp, int64_t timestamp, uint8_t *data,
                         int len, rtsp_conn_info *conn) {
-   if (pthread_rwlock_tryrdlock(&conn->player_thread_lock) == 0) {
-     pthread_cleanup_push(player_thread_lock_cleanup, (void *)conn);
-     if (conn->player_thread != NULL) {
+   // all timestamps are done at the output rate
+   // the "actual_timestamp" is the one that comes in the packet, and is carried over for
+   // debugging
+   // and checking only.
  
-       // all timestamps are done at the output rate
-       // the "actual_timestamp" is the one that comes in the packet, and is carried over for
-       // debugging
-       // and checking only.
+   int64_t ltimestamp = timestamp * conn->output_sample_ratio;
  
-       int64_t ltimestamp = timestamp * conn->output_sample_ratio;
-       // ignore a request to flush that has been made before the first packet...
-       if (conn->packet_count == 0) {
-         debug_mutex_lock(&conn->flush_mutex, 1000, 1);
-         conn->flush_requested = 0;
-         conn->flush_rtp_timestamp = 0;
-         debug_mutex_unlock(&conn->flush_mutex, 3);
-       }
+   // ignore a request to flush that has been made before the first packet...
+   if (conn->packet_count == 0) {
+     debug_mutex_lock(&conn->flush_mutex, 1000, 1);
+     conn->flush_requested = 0;
+     conn->flush_rtp_timestamp = 0;
+     debug_mutex_unlock(&conn->flush_mutex, 3);
+   }
  
-       debug_mutex_lock(&conn->ab_mutex, 30000, 1);
-       conn->packet_count++;
-       conn->time_of_last_audio_packet = get_absolute_time_in_fp();
-       if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
+   debug_mutex_lock(&conn->ab_mutex, 30000, 1);
+   conn->packet_count++;
+   conn->time_of_last_audio_packet = get_absolute_time_in_fp();
+   if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
+     //    if (flush_rtp_timestamp != 0)
+     //        debug(1,"Flush_rtp_timestamp is %u",flush_rtp_timestamp);
+     if ((conn->flush_rtp_timestamp != 0) && (ltimestamp <= conn->flush_rtp_timestamp)) {
+       debug(3,
+             "Dropping flushed packet in player_put_packet, seqno %u, timestamp %lld, flushing to "
+             "timestamp: %lld.",
+             seqno, ltimestamp, conn->flush_rtp_timestamp);
+     } else {
+       if ((conn->flush_rtp_timestamp != 0x0) &&
+           (ltimestamp > conn->flush_rtp_timestamp)) // if we have gone past the flush boundary time
+         conn->flush_rtp_timestamp = 0x0;
  
-         //    if (flush_rtp_timestamp != 0)
-         //            debug(1,"Flush_rtp_timestamp is %u",flush_rtp_timestamp);
+       abuf_t *abuf = 0;
  
-         if ((conn->flush_rtp_timestamp != 0) && (ltimestamp <= conn->flush_rtp_timestamp)) {
-           debug(
-               3,
-               "Dropping flushed packet in player_put_packet, seqno %u, timestamp %lld, flushing to "
-               "timestamp: %lld.",
-               seqno, ltimestamp, conn->flush_rtp_timestamp);
-         } else {
-           if ((conn->flush_rtp_timestamp != 0x0) &&
-               (ltimestamp >
-                conn->flush_rtp_timestamp)) // if we have gone past the flush boundary time
-             conn->flush_rtp_timestamp = 0x0;
-           abuf_t *abuf = 0;
-           if (!conn->ab_synced) {
-             debug(3, "syncing to seqno %u.", seqno);
-             conn->ab_write = seqno;
-             conn->ab_read = seqno;
-             conn->ab_synced = 1;
-           }
+       if (!conn->ab_synced) {
+         debug(3, "syncing to seqno %u.", seqno);
+         conn->ab_write = seqno;
+         conn->ab_read = seqno;
+         conn->ab_synced = 1;
+       }
  
-           // here, we should check for missing frames
-           int resend_interval = (((250 * 44100) / 352) / 1000); // approximately 250 ms intervals
-           const int number_of_resend_attempts = 8;
-           int latency_based_resend_interval =
-               (conn->latency) / (number_of_resend_attempts * conn->max_frames_per_packet);
-           if (latency_based_resend_interval > resend_interval)
-             resend_interval = latency_based_resend_interval;
-           if (conn->resend_interval != resend_interval) {
-             debug(2, "Resend interval for latency of %" PRId64 " frames is %d frames.",
-                   conn->latency, resend_interval);
-             conn->resend_interval = resend_interval;
-           }
+       // here, we should check for missing frames
+       int resend_interval = (((250 * 44100) / 352) / 1000); // approximately 250 ms intervals
+       const int number_of_resend_attempts = 8;
+       int latency_based_resend_interval =
+           (conn->latency) / (number_of_resend_attempts * conn->max_frames_per_packet);
+       if (latency_based_resend_interval > resend_interval)
+         resend_interval = latency_based_resend_interval;
+       if (conn->resend_interval != resend_interval) {
+         debug(2, "Resend interval for latency of %" PRId64 " frames is %d frames.", conn->latency,
+               resend_interval);
+         conn->resend_interval = resend_interval;
+       }
  
-           if (conn->ab_write == seqno) { // expected packet
-             abuf = conn->audio_buffer + BUFIDX(seqno);
-             conn->ab_write = SUCCESSOR(seqno);
-           } else if (seq_order(conn->ab_write, seqno, conn->ab_read)) { // newer than expected
-             // if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
-             // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and
-             // seqno:
-             // %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
-             int32_t gap = seq_diff(conn->ab_write, seqno, conn->ab_read);
-             if (gap <= 0)
-               debug(1, "Unexpected gap size: %d.", gap);
-             int i;
-             for (i = 0; i < gap; i++) {
-               abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
-               abuf->ready = 0; // to be sure, to be sure
-               abuf->resend_level = 0;
-               abuf->timestamp = 0;
-               abuf->given_timestamp = 0;
-               abuf->sequence_number = 0;
-             }
-             // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
-             abuf = conn->audio_buffer + BUFIDX(seqno);
-             //        rtp_request_resend(ab_write, gap);
-             //        resend_requests++;
-             conn->ab_write = SUCCESSOR(seqno);
-           } else if (seq_order(conn->ab_read, seqno, conn->ab_read)) { // late but not yet played
-             conn->late_packets++;
-             abuf = conn->audio_buffer + BUFIDX(seqno);
-             /*
-             if (abuf->ready)
-                     debug(1,"Late apparently duplicate packet received that is %d packets
-             late.",seq_diff(seqno, conn->ab_write, conn->ab_read));
-             else
-                     debug(1,"Late packet received that is %d packets late.",seq_diff(seqno,
-             conn->ab_write, conn->ab_read));
-                   */
-           } else { // too late.
+       if (conn->ab_write == seqno) { // expected packet
+         abuf = conn->audio_buffer + BUFIDX(seqno);
+         conn->ab_write = SUCCESSOR(seqno);
+       } else if (seq_order(conn->ab_write, seqno, conn->ab_read)) { // newer than expected
+         // if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
+         // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and
+         // seqno:
+         // %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
+         int32_t gap = seq_diff(conn->ab_write, seqno, conn->ab_read);
+         if (gap <= 0)
+           debug(1, "Unexpected gap size: %d.", gap);
+         int i;
+         for (i = 0; i < gap; i++) {
+           abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
+           abuf->ready = 0; // to be sure, to be sure
+           abuf->resend_level = 0;
+           abuf->timestamp = 0;
+           abuf->given_timestamp = 0;
+           abuf->sequence_number = 0;
+         }
+         // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
+         abuf = conn->audio_buffer + BUFIDX(seqno);
+         //        rtp_request_resend(ab_write, gap);
+         //        resend_requests++;
+         conn->ab_write = SUCCESSOR(seqno);
+       } else if (seq_order(conn->ab_read, seqno, conn->ab_read)) { // late but not yet played
+         conn->late_packets++;
+         abuf = conn->audio_buffer + BUFIDX(seqno);
+         /*
+         if (abuf->ready)
+                 debug(1,"Late apparently duplicate packet received that is %d packets
+         late.",seq_diff(seqno, conn->ab_write, conn->ab_read));
+         else
+                 debug(1,"Late packet received that is %d packets late.",seq_diff(seqno,
+         conn->ab_write, conn->ab_read));
+               */
+       } else { // too late.
  
-             // debug(1,"Too late packet received that is %d packets late.",seq_diff(seqno,
-             // conn->ab_write, conn->ab_read));
-             conn->too_late_packets++;
-           }
-           // pthread_mutex_unlock(&ab_mutex);
-           if (abuf) {
-             int datalen = conn->max_frames_per_packet;
-             if (alac_decode(abuf->data, &datalen, data, len, conn) == 0) {
-               abuf->ready = 1;
-               abuf->length = datalen;
-               abuf->timestamp = ltimestamp;
-               abuf->given_timestamp = actual_timestamp;
-               abuf->sequence_number = seqno;
-             } else {
-               debug(1, "Bad audio packet detected and discarded.");
-               abuf->ready = 0;
-               abuf->resend_level = 0;
-               abuf->timestamp = 0;
-               abuf->given_timestamp = 0;
-               abuf->sequence_number = 0;
-             }
-           }
+         // debug(1,"Too late packet received that is %d packets late.",seq_diff(seqno,
+         // conn->ab_write, conn->ab_read));
+         conn->too_late_packets++;
+       }
+       // pthread_mutex_unlock(&ab_mutex);
+       if (abuf) {
+         int datalen = conn->max_frames_per_packet;
+         if (alac_decode(abuf->data, &datalen, data, len, conn) == 0) {
+           abuf->ready = 1;
+           abuf->length = datalen;
+           abuf->timestamp = ltimestamp;
+           abuf->given_timestamp = actual_timestamp;
+           abuf->sequence_number = seqno;
+         } else {
+           debug(1, "Bad audio packet detected and discarded.");
+           abuf->ready = 0;
+           abuf->resend_level = 0;
+           abuf->timestamp = 0;
+           abuf->given_timestamp = 0;
+           abuf->sequence_number = 0;
+         }
+       }
  
-           // pthread_mutex_lock(&ab_mutex);
-           int rc = pthread_cond_signal(&conn->flowcontrol);
-           if (rc)
-             debug(1, "Error signalling flowcontrol.");
-           // if it's at the expected time, do a look back for missing packets
-           // but release the ab_mutex when doing a resend
-           if (!conn->ab_buffering) {
-             int j;
-             for (j = 1; j <= number_of_resend_attempts; j++) {
-               // check j times, after a short period of has elapsed, assuming 352 frames per packet
-               // the higher the step_exponent, the less it will try. 1 means it will try very
-               // hard. 2.0 seems good.
-               float step_exponent = 2.0;
-               int back_step = (int)(resend_interval * pow(j, step_exponent));
-               int k;
-               for (k = -1; k <= 1; k++) {
-                 if ((back_step + k) <
-                     seq_diff(conn->ab_read, conn->ab_write,
-                              conn->ab_read)) { // if it's within the range of frames in use...
-                   int item_to_check = (conn->ab_write - (back_step + k)) & 0xffff;
-                   seq_t next = item_to_check;
-                   abuf_t *check_buf = conn->audio_buffer + BUFIDX(next);
-                   if ((!check_buf->ready) &&
-                       (check_buf->resend_level <
-                        j)) { // prevent multiple requests from the same level of lookback
-                     check_buf->resend_level = j;
-                     if (config.disable_resend_requests == 0) {
-                       if (((int)(resend_interval * pow(j + 1, step_exponent)) + k) >=
-                           seq_diff(conn->ab_read, conn->ab_write, conn->ab_read))
-                         debug(3,
-                               "Last-ditch (#%d) resend request for packet %u in range %u to %u. "
-                               "Looking back %d packets.",
-                               j, next, conn->ab_read, conn->ab_write, back_step + k);
-                       debug_mutex_unlock(&conn->ab_mutex, 3);
-                       rtp_request_resend(next, 1, conn);
-                       conn->resend_requests++;
-                       debug_mutex_lock(&conn->ab_mutex, 20000, 1);
-                     }
-                   }
+       // pthread_mutex_lock(&ab_mutex);
+       int rc = pthread_cond_signal(&conn->flowcontrol);
+       if (rc)
+         debug(1, "Error signalling flowcontrol.");
+       // if it's at the expected time, do a look back for missing packets
+       // but release the ab_mutex when doing a resend
+       if (!conn->ab_buffering) {
+         int j;
+         for (j = 1; j <= number_of_resend_attempts; j++) {
+           // check j times, after a short period of has elapsed, assuming 352 frames per packet
+           // the higher the step_exponent, the less it will try. 1 means it will try very
+           // hard. 2.0 seems good.
+           float step_exponent = 2.0;
+           int back_step = (int)(resend_interval * pow(j, step_exponent));
+           int k;
+           for (k = -1; k <= 1; k++) {
+             if ((back_step + k) <
+                 seq_diff(conn->ab_read, conn->ab_write,
+                          conn->ab_read)) { // if it's within the range of frames in use...
+               int item_to_check = (conn->ab_write - (back_step + k)) & 0xffff;
+               seq_t next = item_to_check;
+               abuf_t *check_buf = conn->audio_buffer + BUFIDX(next);
+               if ((!check_buf->ready) &&
+                   (check_buf->resend_level <
+                    j)) { // prevent multiple requests from the same level of lookback
+                 check_buf->resend_level = j;
+                 if (config.disable_resend_requests == 0) {
+                   if (((int)(resend_interval * pow(j + 1, step_exponent)) + k) >=
+                       seq_diff(conn->ab_read, conn->ab_write, conn->ab_read))
 -                    debug(3,
 -                          "Last-ditch (#%d) resend request for packet %u in range %u to %u. "
 -                          "Looking back %d packets.",
++                    debug(3, "Last-ditch (#%d) resend request for packet %u in range %u to %u. "
++                             "Looking back %d packets.",
+                           j, next, conn->ab_read, conn->ab_write, back_step + k);
+                   debug_mutex_unlock(&conn->ab_mutex, 3);
+                   rtp_request_resend(next, 1, conn);
+                   conn->resend_requests++;
+                   debug_mutex_lock(&conn->ab_mutex, 20000, 1);
                  }
                }
              }
@@@ -820,12 -810,13 +807,12 @@@ static abuf_t *buffer_get_frame(rtsp_co
        //      if (conn->packet_count>500) { //for testing -- about 4 seconds of play first
        if ((local_time_now > conn->time_of_last_audio_packet) &&
            (local_time_now - conn->time_of_last_audio_packet >= ct << 32)) {
--        debug(1,
--              "As Yeats almost said, \"Too long a silence / can make a stone of the heart\" "
--              "from RTSP conversation %d.",
++        debug(1, "As Yeats almost said, \"Too long a silence / can make a stone of the heart\" "
++                 "from RTSP conversation %d.",
                conn->connection_number);
          conn->stop = 1;
-         pthread_kill(conn->thread, SIGUSR1);
+         pthread_cancel(conn->thread);
+         // pthread_kill(conn->thread, SIGUSR1);
        }
      }
      int rco = get_requested_connection_state_to_output();
@@@ -1439,36 -1424,81 +1420,81 @@@ typedef struct stats { // statistics fo
    int64_t sync_error, correction, drift;
  } stats_t;
  
- void *player_thread_func(void *arg) {
-   // note, the thread will be started up with the player_thread_lock locked. You must release it
-   // quickly.
+ void player_thread_cleanup_handler(void *arg) {
+   debug(1, "player_thread_cleanup_handler called");
    rtsp_conn_info *conn = (rtsp_conn_info *)arg;
  
-   pthread_rwlock_wrlock(&conn->player_thread_lock);
+   debug(3, "Connection %d: player thread main loop exit.", conn->connection_number);
  
-   int rc = pthread_mutex_init(&conn->ab_mutex, NULL);
-   if (rc)
-     debug(1, "Error initialising ab_mutex.");
-   rc = pthread_mutex_init(&conn->flush_mutex, NULL);
-   if (rc)
-     debug(1, "Error initialising flush_mutex.");
- // set the flowcontrol condition variable to wait on a monotonic clock
- #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
-   pthread_condattr_t attr;
-   pthread_condattr_init(&attr);
-   pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it.
-   rc = pthread_cond_init(&conn->flowcontrol, &attr);
- #endif
- #ifdef COMPILE_FOR_OSX
-   rc = pthread_cond_init(&conn->flowcontrol, NULL);
+   if (config.statistics_requested) {
+     int rawSeconds = (int)difftime(time(NULL), conn->playstart);
+     int elapsedHours = rawSeconds / 3600;
+     int elapsedMin = (rawSeconds / 60) % 60;
+     int elapsedSec = rawSeconds % 60;
+     if (conn->frame_rate_status == 0)
 -      inform("Playback Stopped. Total playing time %02d:%02d:%02d at %0.2f frames per second.", elapsedHours, elapsedMin,
 -           elapsedSec,conn->frame_rate);
++      inform("Playback Stopped. Total playing time %02d:%02d:%02d at %0.2f frames per second.",
++             elapsedHours, elapsedMin, elapsedSec, conn->frame_rate);
+     else
+       inform("Playback Stopped. Total playing time %02d:%02d:%02d.", elapsedHours, elapsedMin,
 -           elapsedSec);    
++             elapsedSec);
+   }
+ #ifdef HAVE_DACP_CLIENT
+   relinquish_dacp_server_information(
+       conn); // say it doesn't belong to this conversation thread any more...
+ #else
+   // stop watching for DACP port number stuff
+   // this is only used for compatability, if dacp stuff isn't enabled.
+   if (conn->dapo_private_storage) {
+     mdns_dacp_dont_monitor(conn->dapo_private_storage);
+     conn->dapo_private_storage = NULL;
+   } else {
+     debug(2, "DACP Monitor already stopped");
+   }
  #endif
-   if (rc)
-     debug(1, "Error initialising flowcontrol condition variable.");
  
-   pthread_rwlock_unlock(&conn->player_thread_lock);
+   debug(2, "Cancelling timing, control and audio threads...");
+   debug(2, "Cancel timing thread.");
+   pthread_cancel(conn->rtp_timing_thread);
+   debug(2, "Join timing thread.");
+   pthread_join(conn->rtp_timing_thread, NULL);
+   debug(2, "Timing thread terminated.");
+   debug(2, "Cancel control thread.");
+   pthread_cancel(conn->rtp_control_thread);
+   debug(2, "Join control thread.");
+   pthread_join(conn->rtp_control_thread, NULL);
+   debug(2, "Control thread terminated.");
+   debug(2, "Cancel audio thread.");
+   pthread_cancel(conn->rtp_audio_thread);
+   debug(2, "Join audio thread.");
+   pthread_join(conn->rtp_audio_thread, NULL);
+   debug(2, "Audio thread terminated.");
+   if (conn->outbuf) {
+     free(conn->outbuf);
+     conn->outbuf = NULL;
+   }
+   if (conn->sbuf) {
+     free(conn->sbuf);
+     conn->sbuf = NULL;
+   }
+   if (conn->tbuf) {
+     free(conn->tbuf);
+     conn->tbuf = NULL;
+   }
+   free_audio_buffers(conn);
+   terminate_decoders(conn);
+   if (config.output->stop)
+     config.output->stop();
  
-   // it's safe now
+   clear_reference_timestamp(conn);
+   conn->rtp_running = 0;
+ }
+ void *player_thread_func(void *arg) {
+   rtsp_conn_info *conn = (rtsp_conn_info *)arg;
  
    conn->packet_count = 0;
    conn->previous_random_number = 0;
    int32_t minimum_buffer_occupancy = INT32_MAX;
    int32_t maximum_buffer_occupancy = INT32_MIN;
  
-   time_t playstart = time(NULL);
+   conn->playstart = time(NULL);
 -  
++
+   conn->frame_rate = 0.0;
+   conn->frame_rate_status = -1; // zero means okay
  
    conn->buffer_occupancy = 0;
  
    // if ((input_rate!=config.output_rate) || (input_bit_depth!=output_bit_depth)) {
    // debug(1,"Define tbuf of length
    // %d.",output_bytes_per_frame*(max_frames_per_packet*output_sample_ratio+max_frame_size_change));
-   tbuf = malloc(
 -  conn->tbuf = malloc(
--      sizeof(int32_t) * 2 *
--      (conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
-   if (tbuf == NULL)
++  conn->tbuf =
++      malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
++                                    conn->max_frame_size_change));
+   if (conn->tbuf == NULL)
      die("Failed to allocate memory for the transition buffer.");
  
-   sbuf = 0;
    // initialise this, because soxr stuffing might be chosen later
  
-   sbuf = malloc(
 -  conn->sbuf = malloc(
--      sizeof(int32_t) * 2 *
--      (conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
-   if (sbuf == NULL)
-     debug(1, "Failed to allocate memory for the sbuf buffer.");
++  conn->sbuf =
++      malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
++                                    conn->max_frame_size_change));
+   if (conn->sbuf == NULL)
+     die("Failed to allocate memory for the sbuf buffer.");
  
    // The size of these dependents on the number of frames, the size of each frame and the maximum
    // size change
            } else {
              // the player may change the contents of the buffer, so it has to be zeroed each time;
              // might as well malloc and freee it locally
--            memset(silence, 0,
--                   conn->output_bytes_per_frame * conn->max_frames_per_packet *
--                       conn->output_sample_ratio);
++            memset(silence, 0, conn->output_bytes_per_frame * conn->max_frames_per_packet *
++                                   conn->output_sample_ratio);
              config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
              free(silence);
            }
            } else {
              // the player may change the contents of the buffer, so it has to be zeroed each time;
              // might as well malloc and freee it locally
--            memset(silence, 0,
--                   conn->output_bytes_per_frame * conn->max_frames_per_packet *
--                       conn->output_sample_ratio);
++            memset(silence, 0, conn->output_bytes_per_frame * conn->max_frames_per_packet *
++                                   conn->output_sample_ratio);
              config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
              free(silence);
            }
                  SUCCESSOR(conn->last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay.
              if (inframe->sequence_number !=
                  conn->last_seqno_read) { // seq_t, ei.e. uint16_t and int32_t, so okay
--              debug(2,
--                    "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
--                    "and ab_write: %u.",
++              debug(2, "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
++                       "and ab_write: %u.",
                      conn->last_seqno_read, inframe->sequence_number, conn->ab_read, conn->ab_write);
                conn->last_seqno_read = inframe->sequence_number; // reset warning...
              }
                    silence_length = filler_length * 5;
  
                  char *long_silence = malloc(conn->output_bytes_per_frame * silence_length);
-                 if (long_silence == NULL)
-                   die("Failed to allocate memory for a long_silence buffer of %d frames.",
-                       silence_length);
-                 memset(long_silence, 0, conn->output_bytes_per_frame * silence_length);
-                 config.output->play(long_silence, silence_length);
-                 free(long_silence);
+                 if (long_silence) {
+                   memset(long_silence, 0, conn->output_bytes_per_frame * silence_length);
+                   config.output->play(long_silence, silence_length);
+                   free(long_silence);
+                 } else {
 -                  warn("Failed to allocate memory for a long_silence buffer of %d frames for a sync error of %" PRId64 " frames.",
 -                      silence_length, sync_error);                
++                  warn("Failed to allocate memory for a long_silence buffer of %d frames for a "
++                       "sync error of %" PRId64 " frames.",
++                       silence_length, sync_error);
+                 }
                }
              } else {
  
  #ifdef CONFIG_CONVOLUTION
                    || config.convolution
  #endif
--              ) {
-                 int32_t *tbuf32 = (int32_t *)tbuf;
++                  ) {
+                 int32_t *tbuf32 = (int32_t *)conn->tbuf;
                  float fbuf_l[inbuflength];
                  float fbuf_r[inbuflength];
  
              if (at_least_one_frame_seen) {
                if ((config.output->delay)) {
                  if (config.no_sync == 0) {
-                   inform(
-                       " %*.1f," /* Sync error in milliseconds */
-                       "%*.1f,"  /* net correction in ppm */
-                       "%*.1f,"  /* corrections in ppm */
-                       "%*d,"    /* total packets */
-                       "%*llu,"  /* missing packets */
-                       "%*llu,"  /* late packets */
-                       "%*llu,"  /* too late packets */
-                       "%*llu,"  /* resend requests */
-                       "%*lli,"  /* min DAC queue size */
-                       "%*d,"    /* min buffer occupancy */
-                       "%*d",    /* max buffer occupancy */
-                       9, /* should be 10, but there's an explicit space at the start to ensure
-                             alignment */
-                       1000 * moving_average_sync_error / config.output_rate, 10,
-                       moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), 10,
-                       moving_average_insertions_plus_deletions * 1000000 /
-                           (352 * conn->output_sample_ratio),
-                       12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
-                       conn->too_late_packets, 7, conn->resend_requests, 7, minimum_dac_queue_size,
-                       5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy);
+                   if (config.output->rate_info) {
+                     uint64_t elapsed_play_time, frames_played;
 -                    conn->frame_rate_status = config.output->rate_info(&elapsed_play_time,&frames_played);
 -                    if (conn->frame_rate_status==0) {
 -                      conn->frame_rate = 1.0*(frames_played*(uint64_t)0x100000000)/elapsed_play_time;
++                    conn->frame_rate_status =
++                        config.output->rate_info(&elapsed_play_time, &frames_played);
++                    if (conn->frame_rate_status == 0) {
++                      conn->frame_rate =
++                          1.0 * (frames_played * (uint64_t)0x100000000) / elapsed_play_time;
+                     }
+                   }
 -                  inform(
 -                      "%*.1f," /* Sync error in milliseconds */
 -                      "%*.1f,"  /* net correction in ppm */
 -                      "%*.1f,"  /* corrections in ppm */
 -                      "%*d,"    /* total packets */
 -                      "%*llu,"  /* missing packets */
 -                      "%*llu,"  /* late packets */
 -                      "%*llu,"  /* too late packets */
 -                      "%*llu,"  /* resend requests */
 -                      "%*lli,"  /* min DAC queue size */
 -                      "%*d,"    /* min buffer occupancy */
 -                      "%*d,"    /* max buffer occupancy */
 -                      "%*.2f",    /* frame rate */
 -                      10, 1000 * moving_average_sync_error / config.output_rate, 10,
 -                      moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), 10,
 -                      moving_average_insertions_plus_deletions * 1000000 /
 -                          (352 * conn->output_sample_ratio),
 -                      12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
 -                      conn->too_late_packets, 7, conn->resend_requests, 7, minimum_dac_queue_size,
 -                      5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 10, conn->frame_rate);
++                  inform("%*.1f," /* Sync error in milliseconds */
++                         "%*.1f," /* net correction in ppm */
++                         "%*.1f," /* corrections in ppm */
++                         "%*d,"   /* total packets */
++                         "%*llu," /* missing packets */
++                         "%*llu," /* late packets */
++                         "%*llu," /* too late packets */
++                         "%*llu," /* resend requests */
++                         "%*lli," /* min DAC queue size */
++                         "%*d,"   /* min buffer occupancy */
++                         "%*d,"   /* max buffer occupancy */
++                         "%*.2f", /* frame rate */
++                         10,
++                         1000 * moving_average_sync_error / config.output_rate, 10,
++                         moving_average_correction * 1000000 / (352 * conn->output_sample_ratio),
++                         10, moving_average_insertions_plus_deletions * 1000000 /
++                                 (352 * conn->output_sample_ratio),
++                         12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
++                         conn->too_late_packets, 7, conn->resend_requests, 7,
++                         minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5,
++                         maximum_buffer_occupancy, 10, conn->frame_rate);
                  } else {
-                   inform(" %*.1f," /* Sync error in milliseconds */
-                          "%*d,"    /* total packets */
-                          "%*llu,"  /* missing packets */
-                          "%*llu,"  /* late packets */
-                          "%*llu,"  /* too late packets */
-                          "%*llu,"  /* resend requests */
-                          "%*lli,"  /* min DAC queue size */
-                          "%*d,"    /* min buffer occupancy */
-                          "%*d",    /* max buffer occupancy */
-                          9, /* should be 10, but there's an explicit space at the start to ensure
-                                alignment */
+                   inform("%*.1f," /* Sync error in milliseconds */
 -                         "%*d,"    /* total packets */
 -                         "%*llu,"  /* missing packets */
 -                         "%*llu,"  /* late packets */
 -                         "%*llu,"  /* too late packets */
 -                         "%*llu,"  /* resend requests */
 -                         "%*lli,"  /* min DAC queue size */
 -                         "%*d,"    /* min buffer occupancy */
 -                         "%*d",    /* max buffer occupancy */
 -                         10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number,
 -                         7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets,
 -                         7, conn->resend_requests, 7, minimum_dac_queue_size, 5,
++                         "%*d,"   /* total packets */
++                         "%*llu," /* missing packets */
++                         "%*llu," /* late packets */
++                         "%*llu," /* too late packets */
++                         "%*llu," /* resend requests */
++                         "%*lli," /* min DAC queue size */
++                         "%*d,"   /* min buffer occupancy */
++                         "%*d",   /* max buffer occupancy */
++                         10,
 +                         1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
 +                         conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
 +                         conn->resend_requests, 7, minimum_dac_queue_size, 5,
                           minimum_buffer_occupancy, 5, maximum_buffer_occupancy);
                  }
                } else {
-                 inform(" %*.1f," /* Sync error in milliseconds */
-                        "%*d,"    /* total packets */
-                        "%*llu,"  /* missing packets */
-                        "%*llu,"  /* late packets */
-                        "%*llu,"  /* too late packets */
-                        "%*llu,"  /* resend requests */
-                        "%*d,"    /* min buffer occupancy */
-                        "%*d",    /* max buffer occupancy */
-                        9, /* should be 10, but there's an explicit space at the start to ensure
-                              alignment */
+                 inform("%*.1f," /* Sync error in milliseconds */
 -                       "%*d,"    /* total packets */
 -                       "%*llu,"  /* missing packets */
 -                       "%*llu,"  /* late packets */
 -                       "%*llu,"  /* too late packets */
 -                       "%*llu,"  /* resend requests */
 -                       "%*d,"    /* min buffer occupancy */
 -                       "%*d",    /* max buffer occupancy */
 -                       10, 1000 * moving_average_sync_error / config.output_rate, 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,
++                       "%*d,"   /* total packets */
++                       "%*llu," /* missing packets */
++                       "%*llu," /* late packets */
++                       "%*llu," /* too late packets */
++                       "%*llu," /* resend requests */
++                       "%*d,"   /* min buffer occupancy */
++                       "%*d",   /* max buffer occupancy */
++                       10,
 +                       1000 * moving_average_sync_error / config.output_rate, 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);
                }
              } else {
diff --cc player.h
index 3274d6487a65ba7fcb62a3deda1cad2207dbc629,c92b48898fe5ba1f7570d64436894e7585dec09a..42d75f9bc04239740df60fb017dcb73a96b73fc9
+++ b/player.h
@@@ -81,12 -82,21 +82,20 @@@ typedef struct 
    SOCKADDR remote, local;
    int stop;
    int running;
-   pthread_t thread, timer_requester;
+   time_t playstart;
+   pthread_t thread, timer_requester, rtp_audio_thread, rtp_control_thread, rtp_timing_thread;
    // pthread_t *ptp;
-   pthread_t *player_thread;
-   pthread_rwlock_t player_thread_lock; // used to control access by "outsiders"
  
 -  
+   // buffers to delete on exit
+   signed short *tbuf;
+   int32_t *sbuf;
+   char *outbuf;
 -  
++
+   // for holding the rate information until printed out at the end of a session
+   double frame_rate;
+   int frame_rate_status;
+   pthread_t *player_thread;
    abuf_t audio_buffer[BUFFER_FRAMES];
    int max_frames_per_packet, input_num_channels, input_bit_depth, input_rate;
    int input_bytes_per_frame, output_bytes_per_frame, output_sample_ratio;
diff --cc rtp.c
Simple merge
diff --cc rtsp.c
index b66e8954041c4d18fceb7f053f2e6de182d8b21e,f0e072c63da5f732175a299208674962ef05fc69..5c549e361f2d5c831131100e62926c9e0877f745
--- 1/rtsp.c
--- 2/rtsp.c
+++ b/rtsp.c
@@@ -292,8 -316,11 +316,11 @@@ void ask_other_rtsp_conversation_thread
    for (i = 0; i < nconns; i++) {
      if (((except_this_thread == 0) || (pthread_equal(conns[i]->thread, except_this_thread) == 0)) &&
          (conns[i]->running != 0)) {
-       conns[i]->stop = 1;
-       pthread_kill(conns[i]->thread, SIGUSR1);
+       pthread_cancel(conns[i]->thread);
 -      pthread_join(conns[i]->thread,NULL);
 -      debug(1,"Connection %d: asked to stop.",conns[i]->connection_number);
++      pthread_join(conns[i]->thread, NULL);
++      debug(1, "Connection %d: asked to stop.", conns[i]->connection_number);
+       // conns[i]->stop = 1;
+       // pthread_kill(conns[i]->thread, SIGUSR1);
      }
    }
  }
@@@ -893,9 -922,9 +920,10 @@@ static void handle_set_parameter_parame
  #ifdef CONFIG_METADATA
          if (!strncmp(cp, "progress: ", 10)) {
        char *progress = cp + 10;
-       // debug(2, "progress: \"%s\"\n",
-       //      progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second
 -      // debug(2, "progress: \"%s\"\n",progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second
++      // debug(2, "progress: \"%s\"\n",progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per
++      // second
        send_ssnc_metadata('prgr', strdup(progress), strlen(progress), 1);
 -      
++
      } else
  #endif
      {
@@@ -1638,7 -1697,7 +1696,8 @@@ static void handle_announce(rtsp_conn_i
      resp->respcode = 200;
    } else {
      resp->respcode = 453;
-     debug(1, "Already playing.");
 -    debug(1, "Connection %d: failed because a connection is already playing.", conn->connection_number);
++    debug(1, "Connection %d: failed because a connection is already playing.",
++          conn->connection_number);
    }
  
  out:
@@@ -1884,6 -1943,53 +1943,53 @@@ authenticate
    return 1;
  }
  
 -  //debug(1, "Connection %d: rtsp_conversation_thread_func_cleanup_function called.",
+ void rtsp_conversation_thread_cleanup_function(void *arg) {
+   rtsp_conn_info *conn = (rtsp_conn_info *)arg;
 -    //    conn->connection_number,conn->fd);    
++  // debug(1, "Connection %d: rtsp_conversation_thread_func_cleanup_function called.",
+   //      conn->connection_number);
+   player_stop(conn);
+   if (conn->fd > 0) {
+     // debug(1, "Connection %d: closing fd %d.",
++    //    conn->connection_number,conn->fd);
+     close(conn->fd);
+   }
+   if (conn->auth_nonce) {
+     free(conn->auth_nonce);
+     conn->auth_nonce = NULL;
+   }
+   rtp_terminate(conn);
+   if (playing_conn == conn) {
+     debug(3, "Unlocking play lock on RTSP conversation thread %d.", conn->connection_number);
+     playing_conn = NULL;
+     pthread_mutex_unlock(&play_lock);
+   }
+   if (conn->dacp_id) {
+     free(conn->dacp_id);
+     conn->dacp_id = NULL;
+   }
+   debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number);
+   conn->running = 0;
+   // remove flow control and mutexes
+   int rc = pthread_cond_destroy(&conn->flowcontrol);
+   if (rc)
+     debug(1, "Connection %d: error %d destroying flow control condition variable.",
+           conn->connection_number, rc);
+   rc = pthread_mutex_destroy(&conn->ab_mutex);
+   if (rc)
+     debug(1, "Connection %d: error %d destroying ab_mutex.", conn->connection_number, rc);
+   rc = pthread_mutex_destroy(&conn->flush_mutex);
+   if (rc)
+     debug(1, "Connection %d: error %d destroying flush_mutex.", conn->connection_number, rc);
+ }
+ void msg_cleanup_function(void *arg) {
+   // debug(1, "msg_cleanup_function called.");
+   msg_free((rtsp_message *)arg);
+ }
  static void *rtsp_conversation_thread_func(void *pconn) {
    rtsp_conn_info *conn = 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,
 -            "RTSP thread %d received an RTSP Packet of type \"%s\":", conn->connection_number,
 -            req->method),
 +      debug(debug_level, "RTSP thread %d received an RTSP Packet of type \"%s\":",
 +            conn->connection_number, req->method),
            debug_print_msg_headers(debug_level, req);
-       resp = msg_init();
-       resp->respcode = 400;
  
        apple_challenge(conn->fd, req, resp);
        hdr = msg_get_header(req, "CSeq");
@@@ -2166,7 -2282,7 +2281,8 @@@ void rtsp_listen_loop(void) 
  
      conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen);
      if (conn->fd < 0) {
-       debug(1, "New RTSP connection on port %d not accepted:", config.port);
 -      debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number, config.port);
++      debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number,
++            config.port);
        perror("failed to accept connection");
        free(conn);
      } else {
            sa = (struct sockaddr_in *)&conn->remote;
            inet_ntop(AF_INET, &(sa->sin_addr), remote_ip4, INET_ADDRSTRLEN);
            unsigned short int rport = ntohs(sa->sin_port);
-           debug(2, "New RTSP connection from %s:%u to self at %s:%u on conversation thread %d.",
-                 remote_ip4, rport, ip4, tport, conn->connection_number);
+           debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
 -                conn->connection_number,remote_ip4, rport, ip4, tport);
++                conn->connection_number, remote_ip4, rport, ip4, tport);
          }
  #ifdef AF_INET6
          if (local_info->SAFAMILY == AF_INET6) {
diff --cc shairport.c
index c5450ad797dd0c47e001705d72a161b188a6de8a,3c8438454dc2c7f88e26ff8c2e42c60cf71f72d5..5ce011f254cbb4c8436334026f72d7fb147d0860
@@@ -1138,7 -1139,54 +1139,54 @@@ void exit_function() 
    // probably should be freeing malloc'ed memory here, including strdup-created strings...
  }
  
 -  // stop_mpris_service();
+ void main_cleanup_handler(__attribute__((unused)) void *arg) {
+   debug(1, "main cleanup handler called.");
+ #ifdef HAVE_MQTT
+   if (config.mqtt_enabled) {
+     // terminate_mqtt();
+   }
+ #endif
+ #if defined(HAVE_DBUS) || defined(HAVE_MPRIS)
+ #ifdef HAVE_MPRIS
 -    debug(1,"Deinitialise the audio backend.");
++// stop_mpris_service();
+ #endif
+ #ifdef HAVE_DBUS
+   stop_dbus_service();
+ #endif
+   debug(1, "Stopping DBUS Loop Thread");
+   g_main_loop_quit(g_main_loop);
+   pthread_join(dbus_thread, NULL);
+ #endif
+ #ifdef HAVE_DACP_CLIENT
+   debug(1, "Stopping DACP Monitor");
+   dacp_monitor_stop();
+ #endif
+ #ifdef HAVE_METADATA_HUB
+   debug(1, "Stopping metadata hub");
+   metadata_hub_stop();
+ #endif
+ #ifdef CONFIG_METADATA
+   metadata_stop(); // close down the metadata pipe
+ #endif
+   if (config.output->deinit) {
++    debug(1, "Deinitialise the audio backend.");
+     config.output->deinit();
+   }
+   daemon_log(LOG_NOTICE, "Unexpected exit...");
+   daemon_retval_send(0);
+   daemon_pid_file_remove();
+   daemon_signal_done();
+   exit(0);
+ }
  int main(int argc, char **argv) {
+   conns = NULL; // no connections active
+   memset((void *)&main_thread_id, 0, sizeof(main_thread_id));
    fp_time_at_startup = get_absolute_time_in_fp();
    fp_time_at_last_debug_message = fp_time_at_startup;
    //  debug(1,"startup");