From: Mike Brady Date: Mon, 30 Jul 2018 09:07:11 +0000 (+0100) Subject: Stop using SIGUSR1 for cancelling threads, use pthread_cancel and friends instead... X-Git-Tag: 3.3RC0~286 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=76c5dd92856cde63067c6a830cbd0f454444ff86;p=thirdparty%2Fshairport-sync.git Stop using SIGUSR1 for cancelling threads, use pthread_cancel and friends instead, fix some memory leaks, add accurate ouput rate calculation to statistics, add "quit" verb to MPRIS and native d-bus interfaces. Probably still buggy. --- 76c5dd92856cde63067c6a830cbd0f454444ff86 diff --cc audio.h index dc7eb873,323dbc4f..fc06a04c --- a/audio.h +++ 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 e2a53ccd,8fd18c37..27995d57 --- a/audio_alsa.c +++ b/audio_alsa.c @@@ -30,6 -30,6 +30,7 @@@ #include "audio.h" #include "common.h" #include ++#include #include #include #include @@@ -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; } } @@@ -233,9 -260,9 +261,9 @@@ 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; } } @@@ -246,9 -273,9 +274,9 @@@ 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; } } @@@ -269,11 -296,11 +297,11 @@@ 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; } } @@@ -288,8 -315,8 +316,10 @@@ 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; } } @@@ -300,8 -327,8 +330,9 @@@ 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 */ @@@ -309,10 -336,10 +340,10 @@@ 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; } @@@ -323,10 -350,10 +354,10 @@@ 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; } @@@ -379,84 -404,87 +408,88 @@@ 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 = ¶meters; // 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 = ¶meters; // 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? ++ // 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 = ¶meters; // 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 = ¶meters; // 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 = ¶meters; // 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) && @@@ -534,9 -559,9 +564,9 @@@ 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) { @@@ -562,28 -587,29 +592,29 @@@ 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) { @@@ -591,9 -617,10 +622,10 @@@ 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) @@@ -607,23 -634,24 +639,24 @@@ 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) { @@@ -633,9 -661,9 +666,9 @@@ 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) { @@@ -827,11 -859,11 +863,11 @@@ 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 { @@@ -851,7 -889,18 +893,18 @@@ } } - 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) { @@@ -863,20 -912,23 +916,23 @@@ 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) @@@ -888,6 -942,34 +946,35 @@@ 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.", @@@ -903,9 -989,10 +994,10 @@@ 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 8a78e880,de8861e8..25ed83e9 --- a/audio_ao.c +++ b/audio_ao.c @@@ -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 5f55bab1,e29d389b..3f30cb4b --- a/audio_dummy.c +++ b/audio_dummy.c @@@ -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 common.c index 2c8810c9,cd3d5139..85aeceec --- a/common.c +++ 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 86cfa1a1,51eb29e6..8b2baffa --- a/common.h +++ 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 player.c index 3c60e24d,a3739d0b..a143740e --- a/player.c +++ 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; @@@ -1573,7 -1597,10 +1593,10 @@@ 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; @@@ -1639,20 -1661,19 +1657,19 @@@ // 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 @@@ -1752,9 -1784,9 +1780,8 @@@ } 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); } @@@ -1774,9 -1806,9 +1801,8 @@@ } 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); } @@@ -1923,9 -1955,9 +1949,8 @@@ 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... } @@@ -2020,12 -2052,14 +2045,15 @@@ 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 { @@@ -2076,8 -2110,8 +2104,8 @@@ #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]; @@@ -2264,58 -2299,60 +2293,65 @@@ 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 3274d648,c92b4889..42d75f9b --- a/player.h +++ 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 rtsp.c index b66e8954,f0e072c6..5c549e36 --- a/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; } + void rtsp_conversation_thread_cleanup_function(void *arg) { + rtsp_conn_info *conn = (rtsp_conn_info *)arg; - //debug(1, "Connection %d: rtsp_conversation_thread_func_cleanup_function called.", ++ // 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); ++ // 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; @@@ -1909,11 -2034,10 +2034,9 @@@ 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 { @@@ -2185,8 -2301,8 +2301,8 @@@ 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 c5450ad7,3c843845..5ce011f2 --- a/shairport.c +++ b/shairport.c @@@ -1138,7 -1139,54 +1139,54 @@@ void exit_function() // probably should be freeing malloc'ed memory here, including strdup-created strings... } + 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 - // stop_mpris_service(); ++// 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."); ++ 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");