Clean up some of the wording and layout of the configuration file
Clean up and improve the way lead-in sillence is used to give a more accurate sync time to the first packet of audio. Allow very good sync to occur with 0.3 seconds of lead-in silence. Improve the use of the buffer offset. Format some other files.
This commit is contained in:
@@ -27,7 +27,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef __ALAC__DECOMP_H
|
#ifndef __ALAC__DECOMP_H
|
||||||
#define __ALAC__DECOMP_H
|
#define __ALAC__DECOMP_H
|
||||||
|
|
||||||
|
|||||||
@@ -191,17 +191,17 @@ void parse_general_audio_options(void) {
|
|||||||
/* Get the latency offset in seconds. */
|
/* Get the latency offset in seconds. */
|
||||||
if (config_lookup_float(config.cfg, "general.audio_backend_latency_offset_in_seconds",
|
if (config_lookup_float(config.cfg, "general.audio_backend_latency_offset_in_seconds",
|
||||||
&dvalue)) {
|
&dvalue)) {
|
||||||
config.audio_backend_latency_offset = dvalue;
|
config.audio_backend_latency_offset = dvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check if the length of the silent lead-in ia set to \"auto\". */
|
/* Check if the length of the silent lead-in ia set to \"auto\". */
|
||||||
if (config_lookup_string(config.cfg, "general.audio_backend_silent_lead_in_time", &str)) {
|
if (config_lookup_string(config.cfg, "general.audio_backend_silent_lead_in_time", &str)) {
|
||||||
if (strcasecmp(str, "auto") == 0) {
|
if (strcasecmp(str, "auto") == 0) {
|
||||||
config.audio_backend_silent_lead_in_time_auto = 1;
|
config.audio_backend_silent_lead_in_time_auto = 1;
|
||||||
} else {
|
} else {
|
||||||
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
||||||
warn("Invalid audio_backend_silent_lead_in_time \"%s\". It should be \"auto\" or the lead-in time in seconds. "
|
warn("Invalid audio_backend_silent_lead_in_time \"%s\". It should be \"auto\" or the "
|
||||||
|
"lead-in time in seconds. "
|
||||||
"It remains set to \"auto\". Note: numbers should not be placed in quotes.",
|
"It remains set to \"auto\". Note: numbers should not be placed in quotes.",
|
||||||
str);
|
str);
|
||||||
else
|
else
|
||||||
@@ -214,14 +214,16 @@ void parse_general_audio_options(void) {
|
|||||||
/* Get the desired length of the silent lead-in. */
|
/* Get the desired length of the silent lead-in. */
|
||||||
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_time", &dvalue)) {
|
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_time", &dvalue)) {
|
||||||
if ((dvalue < 0.0) || (dvalue > 4)) {
|
if ((dvalue < 0.0) || (dvalue > 4)) {
|
||||||
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
||||||
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
|
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
|
||||||
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. The setting remains at \"auto\".",
|
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
|
||||||
dvalue);
|
"The setting remains at \"auto\".",
|
||||||
|
dvalue);
|
||||||
else
|
else
|
||||||
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
|
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
|
||||||
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. It remains set to %f.",
|
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
|
||||||
dvalue, config.audio_backend_silent_lead_in_time);
|
"It remains set to %f.",
|
||||||
|
dvalue, config.audio_backend_silent_lead_in_time);
|
||||||
} else {
|
} else {
|
||||||
config.audio_backend_silent_lead_in_time = dvalue;
|
config.audio_backend_silent_lead_in_time = dvalue;
|
||||||
config.audio_backend_silent_lead_in_time_auto = 0;
|
config.audio_backend_silent_lead_in_time_auto = 0;
|
||||||
|
|||||||
+134
-126
@@ -337,8 +337,9 @@ void actual_close_alsa_device() {
|
|||||||
if (alsa_handle) {
|
if (alsa_handle) {
|
||||||
int derr;
|
int derr;
|
||||||
if ((derr = snd_pcm_hw_free(alsa_handle)))
|
if ((derr = snd_pcm_hw_free(alsa_handle)))
|
||||||
debug(1, "Error %d (\"%s\") freeing the output device hardware while "
|
debug(1,
|
||||||
"closing it.",
|
"Error %d (\"%s\") freeing the output device hardware while "
|
||||||
|
"closing it.",
|
||||||
derr, snd_strerror(derr));
|
derr, snd_strerror(derr));
|
||||||
|
|
||||||
if ((derr = snd_pcm_close(alsa_handle)))
|
if ((derr = snd_pcm_close(alsa_handle)))
|
||||||
@@ -353,7 +354,10 @@ void actual_close_alsa_device() {
|
|||||||
// The lowest rate that the DAC is capable of is chosen.
|
// The lowest rate that the DAC is capable of is chosen.
|
||||||
|
|
||||||
unsigned int auto_speed_output_rates[] = {
|
unsigned int auto_speed_output_rates[] = {
|
||||||
44100, 88200, 176400, 352800,
|
44100,
|
||||||
|
88200,
|
||||||
|
176400,
|
||||||
|
352800,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions,
|
// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions,
|
||||||
@@ -702,9 +706,10 @@ int actual_open_alsa_device(int do_auto_setup) {
|
|||||||
buffer_size);
|
buffer_size);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
debug(1, "The alsa buffer is smaller (%lu bytes) than the desired backend "
|
debug(1,
|
||||||
"buffer "
|
"The alsa buffer is smaller (%lu bytes) than the desired backend "
|
||||||
"length (%ld) you have chosen.",
|
"buffer "
|
||||||
|
"length (%ld) you have chosen.",
|
||||||
actual_buffer_length, config.audio_backend_buffer_desired_length);
|
actual_buffer_length, config.audio_backend_buffer_desired_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,120 +869,117 @@ int prepare_mixer() {
|
|||||||
// do any alsa device initialisation (general case)
|
// do any alsa device initialisation (general case)
|
||||||
// at present, this is only needed if a hardware mixer is being used
|
// at present, this is only needed if a hardware mixer is being used
|
||||||
// if there's a hardware mixer, it needs to be initialised before use
|
// if there's a hardware mixer, it needs to be initialised before use
|
||||||
if (alsa_mix_ctrl == NULL) {
|
if (alsa_mix_ctrl == NULL) {
|
||||||
audio_alsa.volume = NULL;
|
audio_alsa.volume = NULL;
|
||||||
audio_alsa.parameters = NULL;
|
audio_alsa.parameters = NULL;
|
||||||
audio_alsa.mute = NULL;
|
audio_alsa.mute = NULL;
|
||||||
} else {
|
} else {
|
||||||
debug(2, "alsa: hardware mixer prepare");
|
debug(2, "alsa: hardware mixer prepare");
|
||||||
int oldState;
|
int oldState;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
|
|
||||||
if (alsa_mix_dev == NULL)
|
if (alsa_mix_dev == NULL)
|
||||||
alsa_mix_dev = alsa_out_dev;
|
alsa_mix_dev = alsa_out_dev;
|
||||||
|
|
||||||
// Now, start trying to initialise the alsa device with the settings
|
// Now, start trying to initialise the alsa device with the settings
|
||||||
// obtained
|
// obtained
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1);
|
pthread_cleanup_debug_mutex_lock(&alsa_mixer_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,
|
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
|
||||||
&alsa_mix_maxv) < 0)
|
0)
|
||||||
debug(1, "Can't read mixer's [linear] min and max volumes.");
|
debug(1, "Can't read mixer's [linear] min and max volumes.");
|
||||||
else {
|
else {
|
||||||
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
|
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
|
||||||
&alsa_mix_maxdb) == 0) {
|
&alsa_mix_maxdb) == 0) {
|
||||||
|
|
||||||
audio_alsa.volume = &volume; // insert the volume function now we
|
audio_alsa.volume = &volume; // insert the volume function now we
|
||||||
// know it can do dB stuff
|
// know it can do dB stuff
|
||||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||||
if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
|
if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
|
||||||
// For instance, the Raspberry Pi does this
|
// For instance, the Raspberry Pi does this
|
||||||
debug(1, "Lowest dB value is a mute");
|
debug(1, "Lowest dB value is a mute");
|
||||||
mixer_volume_setting_gives_mute = 1;
|
mixer_volume_setting_gives_mute = 1;
|
||||||
alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be
|
alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be
|
||||||
// necessary -- it's
|
// necessary -- it's
|
||||||
// always
|
// 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
|
// debug(1, "Try minimum volume + 1 as lowest true attenuation
|
||||||
// value");
|
// value");
|
||||||
if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
|
if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
|
||||||
&alsa_mix_mindb) != 0)
|
&alsa_mix_mindb) != 0)
|
||||||
debug(1, "Can't get dB value corresponding to a minimum volume "
|
debug(1, "Can't get dB value corresponding to a minimum volume "
|
||||||
"+ 1.");
|
"+ 1.");
|
||||||
}
|
|
||||||
debug(3, "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
|
|
||||||
warn("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) {
|
|
||||||
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);
|
|
||||||
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, "alsa: hardware mixer \"%s\" selected, with 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
|
|
||||||
} 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(3, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0,
|
||||||
if (((config.alsa_use_hardware_mute == 1) &&
|
(1.0 * alsa_mix_maxdb) / 100.0);
|
||||||
(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 {
|
} else {
|
||||||
// debug(1, "Has mixer but not using hardware mute.");
|
// 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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,
|
||||||
|
"alsa: hardware mixer \"%s\" selected, with 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
|
||||||
|
} 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);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
close_mixer();
|
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex
|
if (((config.alsa_use_hardware_mute == 1) &&
|
||||||
pthread_cleanup_pop(0);
|
(snd_mixer_selem_has_playback_switch(alsa_mix_elem))) ||
|
||||||
pthread_setcancelstate(oldState, NULL);
|
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_mixer_mutex, 3); // release the mutex
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
pthread_setcancelstate(oldState, NULL);
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
int alsa_device_init() {
|
int alsa_device_init() { return prepare_mixer(); }
|
||||||
return prepare_mixer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int init(int argc, char **argv) {
|
static int init(int argc, char **argv) {
|
||||||
// for debugging
|
// for debugging
|
||||||
@@ -1135,8 +1137,10 @@ static int init(int argc, char **argv) {
|
|||||||
"\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", "
|
"\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", "
|
||||||
"\"S24_3LE\", \"S24_3BE\" or "
|
"\"S24_3LE\", \"S24_3BE\" or "
|
||||||
"\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".",
|
"\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".",
|
||||||
str, config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(
|
str,
|
||||||
config.output_format));
|
config.output_format_auto_requested == 1
|
||||||
|
? "auto"
|
||||||
|
: sps_format_description_string(config.output_format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1286,9 +1290,9 @@ static int init(int argc, char **argv) {
|
|||||||
warn("Invalid use_precision_timing option choice \"%s\". It should be "
|
warn("Invalid use_precision_timing option choice \"%s\". It should be "
|
||||||
"\"yes\", \"auto\" or \"no\". "
|
"\"yes\", \"auto\" or \"no\". "
|
||||||
"It remains set to \"%s\".",
|
"It remains set to \"%s\".",
|
||||||
config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO
|
config.use_precision_timing == YNA_NO
|
||||||
? "auto"
|
? "no"
|
||||||
: "yes");
|
: config.use_precision_timing == YNA_AUTO ? "auto" : "yes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1519,14 +1523,16 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
|
|||||||
|
|
||||||
if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
|
if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
|
||||||
((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
|
((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
|
||||||
debug(2, "DAC seems to have stalled with time_now_ns: %" PRIX64
|
debug(2,
|
||||||
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
|
"DAC seems to have stalled with time_now_ns: %" PRIX64
|
||||||
", stall_monitor_error_threshold %" PRIX64 ".",
|
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
|
||||||
|
", stall_monitor_error_threshold %" PRIX64 ".",
|
||||||
time_now_ns, update_timestamp_ns, stall_monitor_start_time,
|
time_now_ns, update_timestamp_ns, stall_monitor_start_time,
|
||||||
stall_monitor_error_threshold);
|
stall_monitor_error_threshold);
|
||||||
debug(2, "DAC seems to have stalled with time_now: %lx,%lx"
|
debug(2,
|
||||||
", update_timestamp: %lx,%lx, stall_monitor_start_time %" PRIX64
|
"DAC seems to have stalled with time_now: %lx,%lx"
|
||||||
", stall_monitor_error_threshold %" PRIX64 ".",
|
", update_timestamp: %lx,%lx, stall_monitor_start_time %" PRIX64
|
||||||
|
", stall_monitor_error_threshold %" PRIX64 ".",
|
||||||
tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec,
|
tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec,
|
||||||
stall_monitor_start_time, stall_monitor_error_threshold);
|
stall_monitor_start_time, stall_monitor_error_threshold);
|
||||||
ret = sps_extra_code_output_stalled;
|
ret = sps_extra_code_output_stalled;
|
||||||
@@ -1692,8 +1698,9 @@ int do_play(void *buf, int samples) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* "
|
debug(1,
|
||||||
"%d for play.",
|
"alsa: device status returns fault status %d and SND_PCM_STATE_* "
|
||||||
|
"%d for play.",
|
||||||
ret, state);
|
ret, state);
|
||||||
frame_index = 0;
|
frame_index = 0;
|
||||||
measurement_data_is_valid = 0;
|
measurement_data_is_valid = 0;
|
||||||
@@ -2007,8 +2014,9 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
|||||||
error_count++;
|
error_count++;
|
||||||
char errorstring[1024];
|
char errorstring[1024];
|
||||||
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
|
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
|
||||||
debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples "
|
debug(2,
|
||||||
"to alsa device -- %d errors in %d trials.",
|
"alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples "
|
||||||
|
"to alsa device -- %d errors in %d trials.",
|
||||||
ret, (char *)errorstring, frames_of_silence, error_count, frame_count);
|
ret, (char *)errorstring, frames_of_silence, error_count, frame_count);
|
||||||
if ((error_count > 40) && (frame_count < 100)) {
|
if ((error_count > 40) && (frame_count < 100)) {
|
||||||
warn("disable_standby_mode has been turned off because too many underruns "
|
warn("disable_standby_mode has been turned off because too many underruns "
|
||||||
@@ -2021,8 +2029,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
debug_mutex_unlock(&alsa_mutex, 0);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
usleep(sleep_time_us); // has a cancellation point in it
|
usleep(sleep_time_us); // has a cancellation point in it
|
||||||
}
|
}
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-36
@@ -89,14 +89,9 @@ typedef struct soxr_quality {
|
|||||||
const char *name;
|
const char *name;
|
||||||
} soxr_quality_t;
|
} soxr_quality_t;
|
||||||
|
|
||||||
static soxr_quality_t soxr_quality_table[] = {
|
static soxr_quality_t soxr_quality_table[] = {{SOXR_VHQ, "very high"}, {SOXR_HQ, "high"},
|
||||||
{ SOXR_VHQ, "very high" },
|
{SOXR_MQ, "medium"}, {SOXR_LQ, "low"},
|
||||||
{ SOXR_HQ, "high" },
|
{SOXR_QQ, "quick"}, {-1, NULL}};
|
||||||
{ SOXR_MQ, "medium" },
|
|
||||||
{ SOXR_LQ, "low" },
|
|
||||||
{ SOXR_QQ, "quick" },
|
|
||||||
{ -1, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int parse_soxr_quality_name(const char *name) {
|
static int parse_soxr_quality_name(const char *name) {
|
||||||
for (soxr_quality_t *s = soxr_quality_table; s->name != NULL; ++s) {
|
for (soxr_quality_t *s = soxr_quality_table; s->name != NULL; ++s) {
|
||||||
@@ -107,9 +102,9 @@ static int parse_soxr_quality_name(const char *name) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static soxr_t soxr = NULL;
|
static soxr_t soxr = NULL;
|
||||||
static soxr_quality_spec_t quality_spec;
|
static soxr_quality_spec_t quality_spec;
|
||||||
static soxr_io_spec_t io_spec;
|
static soxr_io_spec_t io_spec;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline sample_t sample_conv(short sample) {
|
static inline sample_t sample_conv(short sample) {
|
||||||
@@ -122,8 +117,7 @@ static inline sample_t sample_conv(short sample) {
|
|||||||
return ((sample < 0) ? (-1.0 * sample / SHRT_MIN) : (1.0 * sample / SHRT_MAX));
|
return ((sample < 0) ? (-1.0 * sample / SHRT_MIN) : (1.0 * sample / SHRT_MAX));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deinterleave(const char *interleaved_input_buffer,
|
static void deinterleave(const char *interleaved_input_buffer, sample_t *jack_output_buffer[],
|
||||||
sample_t *jack_output_buffer[],
|
|
||||||
jack_nframes_t offset, jack_nframes_t nframes) {
|
jack_nframes_t offset, jack_nframes_t nframes) {
|
||||||
jack_nframes_t f;
|
jack_nframes_t f;
|
||||||
// We're dealing with 16bit audio here:
|
// We're dealing with 16bit audio here:
|
||||||
@@ -271,7 +265,7 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
|
|||||||
io_spec = soxr_io_spec(SOXR_INT16_I, SOXR_FLOAT32_I);
|
io_spec = soxr_io_spec(SOXR_INT16_I, SOXR_FLOAT32_I);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
if (sample_rate != 44100) {
|
if (sample_rate != 44100) {
|
||||||
die("The JACK server is running at the wrong sample rate (%d) for Shairport Sync."
|
die("The JACK server is running at the wrong sample rate (%d) for Shairport Sync."
|
||||||
" Must be 44100 Hz.",
|
" Must be 44100 Hz.",
|
||||||
sample_rate);
|
sample_rate);
|
||||||
@@ -353,8 +347,7 @@ void jack_deinit() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void jack_start(int i_sample_rate,
|
void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_format) {
|
||||||
__attribute__((unused)) int i_sample_format) {
|
|
||||||
// Nothing to do, JACK client has already been set up at jack_init().
|
// Nothing to do, JACK client has already been set up at jack_init().
|
||||||
// Also, we have no say over the sample rate or sample format of JACK,
|
// Also, we have no say over the sample rate or sample format of JACK,
|
||||||
// We convert the 16bit samples to float, and die if the sample rate is != 44k1 without soxr.
|
// We convert the 16bit samples to float, and die if the sample rate is != 44k1 without soxr.
|
||||||
@@ -365,13 +358,7 @@ void jack_start(int i_sample_rate,
|
|||||||
soxr_delete(soxr);
|
soxr_delete(soxr);
|
||||||
}
|
}
|
||||||
soxr_error_t e = NULL;
|
soxr_error_t e = NULL;
|
||||||
soxr = soxr_create(i_sample_rate,
|
soxr = soxr_create(i_sample_rate, sample_rate, NPORTS, &e, &io_spec, &quality_spec, NULL);
|
||||||
sample_rate,
|
|
||||||
NPORTS,
|
|
||||||
&e,
|
|
||||||
&io_spec,
|
|
||||||
&quality_spec,
|
|
||||||
NULL);
|
|
||||||
if (!soxr) {
|
if (!soxr) {
|
||||||
die("Unable to create soxr resampler for JACK: %s", e);
|
die("Unable to create soxr resampler for JACK: %s", e);
|
||||||
}
|
}
|
||||||
@@ -428,13 +415,7 @@ int play(void *buf, int samples) {
|
|||||||
size_t i_done, o_done;
|
size_t i_done, o_done;
|
||||||
soxr_error_t e;
|
soxr_error_t e;
|
||||||
while (samples > 0 && thisbuf > 0) {
|
while (samples > 0 && thisbuf > 0) {
|
||||||
e = soxr_process(soxr,
|
e = soxr_process(soxr, (soxr_in_t)in, samples, &i_done, (soxr_out_t)out, thisbuf, &o_done);
|
||||||
(soxr_in_t)in,
|
|
||||||
samples,
|
|
||||||
&i_done,
|
|
||||||
(soxr_out_t)out,
|
|
||||||
thisbuf,
|
|
||||||
&o_done);
|
|
||||||
if (e)
|
if (e)
|
||||||
die("Error during soxr process: %s", e);
|
die("Error during soxr process: %s", e);
|
||||||
|
|
||||||
@@ -445,13 +426,13 @@ int play(void *buf, int samples) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
j = 0;
|
j = 0;
|
||||||
for (j = 0; j < thisbuf && samples > 0; ++j) {
|
for (j = 0; j < thisbuf && samples > 0; ++j) {
|
||||||
for (c = 0; c < NPORTS; ++c)
|
for (c = 0; c < NPORTS; ++c)
|
||||||
out[j * NPORTS + c] = sample_conv(*in++);
|
out[j * NPORTS + c] = sample_conv(*in++);
|
||||||
--samples;
|
--samples;
|
||||||
}
|
}
|
||||||
jack_ringbuffer_write_advance(jackbuf, j * jack_sample_size * NPORTS);
|
jack_ringbuffer_write_advance(jackbuf, j * jack_sample_size * NPORTS);
|
||||||
#ifdef CONFIG_SOXR
|
#ifdef CONFIG_SOXR
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -262,7 +262,7 @@ static int delay(long *_delay) {
|
|||||||
uint64_t frame_difference = (time_difference * par.rate) / 1000000000;
|
uint64_t frame_difference = (time_difference * par.rate) / 1000000000;
|
||||||
estimated_extra_frames_output = frame_difference;
|
estimated_extra_frames_output = frame_difference;
|
||||||
// sanity check -- total estimate can not exceed frames written.
|
// sanity check -- total estimate can not exceed frames written.
|
||||||
if ((estimated_extra_frames_output + played) > written/framesize) {
|
if ((estimated_extra_frames_output + played) > written / framesize) {
|
||||||
// debug(1,"play estimate fails sanity check, possibly due to running on a VM");
|
// debug(1,"play estimate fails sanity check, possibly due to running on a VM");
|
||||||
estimated_extra_frames_output = 0; // can't make any sensible guess
|
estimated_extra_frames_output = 0; // can't make any sensible guess
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -24,8 +24,9 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
|
|||||||
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
|
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
|
||||||
int fill_count = fill_bytes / outstream->bytes_per_frame;
|
int fill_count = fill_bytes / outstream->bytes_per_frame;
|
||||||
|
|
||||||
debug(3, "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
|
debug(3,
|
||||||
"outstream->bytes_per_frame: %d",
|
"[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
|
||||||
|
"outstream->bytes_per_frame: %d",
|
||||||
frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame);
|
frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame);
|
||||||
|
|
||||||
if (frame_count_min > fill_count) {
|
if (frame_count_min > fill_count) {
|
||||||
|
|||||||
@@ -1578,9 +1578,11 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, sps_format_t f
|
|||||||
return previous_random_number;
|
return previous_random_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will check the incoming string "s" of length "len" with the existing NUL-terminated string "str" and update "flag" accordingly.
|
// This will check the incoming string "s" of length "len" with the existing NUL-terminated string
|
||||||
|
// "str" and update "flag" accordingly.
|
||||||
|
|
||||||
// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings are stored.
|
// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings
|
||||||
|
// are stored.
|
||||||
|
|
||||||
// If the strings are different, the str is free'd and replaced by a pointer
|
// If the strings are different, the str is free'd and replaced by a pointer
|
||||||
// to a newly strdup'd string and the flag is set
|
// to a newly strdup'd string and the flag is set
|
||||||
@@ -1593,7 +1595,7 @@ int string_update_with_size(char **str, int *flag, char *s, size_t len) {
|
|||||||
free(*str);
|
free(*str);
|
||||||
//*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
|
//*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
|
||||||
char *p = malloc(len + 1);
|
char *p = malloc(len + 1);
|
||||||
memcpy(p,s,len);
|
memcpy(p, s, len);
|
||||||
p[len] = '\0';
|
p[len] = '\0';
|
||||||
*str = p;
|
*str = p;
|
||||||
*flag = 1;
|
*flag = 1;
|
||||||
@@ -1610,7 +1612,7 @@ int string_update_with_size(char **str, int *flag, char *s, size_t len) {
|
|||||||
if ((s) && (len)) {
|
if ((s) && (len)) {
|
||||||
//*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
|
//*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
|
||||||
char *p = malloc(len + 1);
|
char *p = malloc(len + 1);
|
||||||
memcpy(p,s,len);
|
memcpy(p, s, len);
|
||||||
p[len] = '\0';
|
p[len] = '\0';
|
||||||
*str = p;
|
*str = p;
|
||||||
*flag = 1;
|
*flag = 1;
|
||||||
|
|||||||
@@ -100,10 +100,13 @@ typedef enum {
|
|||||||
const char *sps_format_description_string(sps_format_t format);
|
const char *sps_format_description_string(sps_format_t format);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double missing_port_dacp_scan_interval_seconds; // if no DACP port number can be found, check at these intervals
|
double missing_port_dacp_scan_interval_seconds; // if no DACP port number can be found, check at
|
||||||
double resend_control_first_check_time; // wait this long before asking for a missing packet to be resent
|
// these intervals
|
||||||
|
double resend_control_first_check_time; // wait this long before asking for a missing packet to be
|
||||||
|
// resent
|
||||||
double resend_control_check_interval_time; // wait this long between making requests
|
double resend_control_check_interval_time; // wait this long between making requests
|
||||||
double resend_control_last_check_time; // if the packet is missing this close to the time of use, give up
|
double resend_control_last_check_time; // if the packet is missing this close to the time of use,
|
||||||
|
// give up
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
config_t *cfg;
|
config_t *cfg;
|
||||||
int endianness;
|
int endianness;
|
||||||
@@ -114,7 +117,7 @@ typedef struct {
|
|||||||
// on host %h"
|
// on host %h"
|
||||||
|
|
||||||
#ifdef CONFIG_PA
|
#ifdef CONFIG_PA
|
||||||
char *pa_server; // the pulseaudio server address that Shairport Sync will play on.
|
char *pa_server; // the pulseaudio server address that Shairport Sync will play on.
|
||||||
char *pa_application_name; // the name under which Shairport Sync shows up as an "Application" in
|
char *pa_application_name; // the name under which Shairport Sync shows up as an "Application" in
|
||||||
// the Sound Preferences in most desktop Linuxes.
|
// the Sound Preferences in most desktop Linuxes.
|
||||||
// Defaults to "Shairport Sync". Shairport Sync must be playing to see it.
|
// Defaults to "Shairport Sync". Shairport Sync must be playing to see it.
|
||||||
@@ -212,15 +215,13 @@ typedef struct {
|
|||||||
// buffer
|
// buffer
|
||||||
double disable_standby_mode_silence_scan_interval; // check the threshold this often
|
double disable_standby_mode_silence_scan_interval; // check the threshold this often
|
||||||
|
|
||||||
double lead_in_silence_initial_period; // the size of the first block of silence to send to the DAC
|
|
||||||
int lead_in_silence_minimum_adjustments_to_make; // make at least this number of checks and timing adjustments
|
|
||||||
double lead_in_silence_adjustment_interval; // make checks and adjustments at this interval
|
|
||||||
|
|
||||||
double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any
|
double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any
|
||||||
// fixed latency there might be in the audio path
|
// fixed latency there might be in the audio path
|
||||||
int audio_backend_silent_lead_in_time_auto; // true if the lead-in time should be from as soon as packets are received
|
int audio_backend_silent_lead_in_time_auto; // true if the lead-in time should be from as soon as
|
||||||
|
// packets are received
|
||||||
double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
|
double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
|
||||||
uint32_t minimum_free_buffer_headroom; // when effective latency is calculated, ensure this number of buffers are unallocated
|
uint32_t minimum_free_buffer_headroom; // when effective latency is calculated, ensure this number
|
||||||
|
// of buffers are unallocated
|
||||||
double active_state_timeout; // the amount of time from when play ends to when the system leaves
|
double active_state_timeout; // the amount of time from when play ends to when the system leaves
|
||||||
// into the "active" mode.
|
// into the "active" mode.
|
||||||
uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
|
uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
|
|||||||
// debug(1,"dacp_send_command: command is: \"%s\".",command);
|
// debug(1,"dacp_send_command: command is: \"%s\".",command);
|
||||||
|
|
||||||
if (dacp_server.port == 0) {
|
if (dacp_server.port == 0) {
|
||||||
debug(2, "No DACP port specified yet");
|
debug(3, "No DACP port specified yet");
|
||||||
result = 490; // no port specified
|
result = 490; // no port specified
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -222,18 +222,16 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
|
|||||||
pthread_cleanup_push(connect_cleanup, (void *)&sockfd);
|
pthread_cleanup_push(connect_cleanup, (void *)&sockfd);
|
||||||
// debug(2, "dacp_send_command: open socket %d.",sockfd);
|
// debug(2, "dacp_send_command: open socket %d.",sockfd);
|
||||||
|
|
||||||
|
|
||||||
// This is for limiting the time to be spent waiting for a response.
|
// This is for limiting the time to be spent waiting for a response.
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 500000;
|
tv.tv_usec = 500000;
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
|
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
|
||||||
debug(1, "dacp_send_command: error %d setting receive timeout.", errno);
|
debug(1, "dacp_send_command: error %d setting receive timeout.", errno);
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
|
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
|
||||||
debug(1, "dacp_send_command: error %d setting send timeout.", errno);
|
debug(1, "dacp_send_command: error %d setting send timeout.", errno);
|
||||||
|
|
||||||
|
|
||||||
// connect!
|
// connect!
|
||||||
// debug(1, "DACP socket created.");
|
// debug(1, "DACP socket created.");
|
||||||
if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
|
if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
|
||||||
@@ -418,7 +416,8 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
|
|||||||
// which return immediately with a 403 code if there are no changes.
|
// which return immediately with a 403 code if there are no changes.
|
||||||
dacp_server.always_use_revision_number_1 = 0;
|
dacp_server.always_use_revision_number_1 = 0;
|
||||||
char *p = strstr(conn->UserAgent, "forked-daapd");
|
char *p = strstr(conn->UserAgent, "forked-daapd");
|
||||||
if ((p != 0) && (p == conn->UserAgent)) {// must exist and be at the start of the UserAgent string
|
if ((p != 0) &&
|
||||||
|
(p == conn->UserAgent)) { // must exist and be at the start of the UserAgent string
|
||||||
dacp_server.always_use_revision_number_1 = 1;
|
dacp_server.always_use_revision_number_1 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,14 +504,15 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
|||||||
(metadata_store.advanced_dacp_server_active != 0);
|
(metadata_store.advanced_dacp_server_active != 0);
|
||||||
metadata_store.dacp_server_active = 0;
|
metadata_store.dacp_server_active = 0;
|
||||||
metadata_store.advanced_dacp_server_active = 0;
|
metadata_store.advanced_dacp_server_active = 0;
|
||||||
debug(2,
|
debug(3,
|
||||||
"setting metadata_store.dacp_server_active and "
|
"setting metadata_store.dacp_server_active and "
|
||||||
"metadata_store.advanced_dacp_server_active to 0 with an update "
|
"metadata_store.advanced_dacp_server_active to 0 with an update "
|
||||||
"flag value of %d",
|
"flag value of %d",
|
||||||
ch);
|
ch);
|
||||||
metadata_hub_modify_epilog(ch);
|
metadata_hub_modify_epilog(ch);
|
||||||
|
|
||||||
uint64_t time_to_wait_for_wakeup_ns = (uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
|
uint64_t time_to_wait_for_wakeup_ns =
|
||||||
|
(uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
|
||||||
|
|
||||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||||
uint64_t time_of_wakeup_ns = get_absolute_time_in_ns() + time_to_wait_for_wakeup_ns;
|
uint64_t time_of_wakeup_ns = get_absolute_time_in_ns() + time_to_wait_for_wakeup_ns;
|
||||||
@@ -534,11 +534,13 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
|||||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||||
result = pthread_cond_timedwait(&dacp_server_information_cv, &dacp_server_information_lock,
|
result = pthread_cond_timedwait(&dacp_server_information_cv, &dacp_server_information_lock,
|
||||||
&time_to_wait); // this is a pthread cancellation point
|
&time_to_wait); // this is a pthread cancellation point
|
||||||
// debug(1, "result is %d, dacp_server.scan_enable is %d, time_to_wait_for_wakeup_ns is %" PRId64 ".", result, dacp_server.scan_enable, time_to_wait_for_wakeup_ns);
|
// debug(1, "result is %d, dacp_server.scan_enable is %d, time_to_wait_for_wakeup_ns is %"
|
||||||
|
// PRId64 ".", result, dacp_server.scan_enable, time_to_wait_for_wakeup_ns);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef COMPILE_FOR_OSX
|
#ifdef COMPILE_FOR_OSX
|
||||||
result = pthread_cond_timedwait_relative_np(&dacp_server_information_cv, &dacp_server_information_lock, &time_to_wait);
|
result = pthread_cond_timedwait_relative_np(&dacp_server_information_cv,
|
||||||
|
&dacp_server_information_lock, &time_to_wait);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (dacp_server.scan_enable == 1) {
|
if (dacp_server.scan_enable == 1) {
|
||||||
@@ -547,12 +549,13 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
always_use_revision_number_1 = dacp_server.always_use_revision_number_1; // set this while access is locked
|
always_use_revision_number_1 =
|
||||||
|
dacp_server.always_use_revision_number_1; // set this while access is locked
|
||||||
|
|
||||||
result = dacp_get_volume(&the_volume); // just want the http code
|
result = dacp_get_volume(&the_volume); // just want the http code
|
||||||
pthread_cleanup_pop(1);
|
pthread_cleanup_pop(1);
|
||||||
|
|
||||||
if (result == 490) { // 490 means no port was specified
|
if (result == 490) { // 490 means no port was specified
|
||||||
if ((dacp_server.dacp_id != NULL) && (strlen(dacp_server.dacp_id) != 0)) {
|
if ((dacp_server.dacp_id != NULL) && (strlen(dacp_server.dacp_id) != 0)) {
|
||||||
// debug(1,"mdns_dacp_monitor_set_id");
|
// debug(1,"mdns_dacp_monitor_set_id");
|
||||||
mdns_dacp_monitor_set_id(dacp_server.dacp_id);
|
mdns_dacp_monitor_set_id(dacp_server.dacp_id);
|
||||||
@@ -597,8 +600,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
|||||||
else
|
else
|
||||||
idle_scan_count = 0;
|
idle_scan_count = 0;
|
||||||
|
|
||||||
debug(3, "Scan Result: %d, Bad Scan Count: %d, Idle Scan Count: %d.", result, bad_result_count,
|
debug(3, "Scan Result: %d, Bad Scan Count: %d, Idle Scan Count: %d.", result,
|
||||||
idle_scan_count);
|
bad_result_count, idle_scan_count);
|
||||||
|
|
||||||
/* not used
|
/* not used
|
||||||
// decide if idle for too long
|
// decide if idle for too long
|
||||||
@@ -934,8 +937,6 @@ void dacp_monitor_start() {
|
|||||||
rc = pthread_cond_init(&dacp_server_information_cv, NULL);
|
rc = pthread_cond_init(&dacp_server_information_cv, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pthread_mutexattr_t mta;
|
pthread_mutexattr_t mta;
|
||||||
|
|
||||||
rc = pthread_mutexattr_init(&mta);
|
rc = pthread_mutexattr_init(&mta);
|
||||||
@@ -1248,7 +1249,7 @@ int dacp_get_volume(int32_t *the_actual_volume) {
|
|||||||
} else {
|
} else {
|
||||||
debug(1, "Unexpected return code %d from dacp_get_speaker_list.", http_response);
|
debug(1, "Unexpected return code %d from dacp_get_speaker_list.", http_response);
|
||||||
}
|
}
|
||||||
} else if (http_response != 400){
|
} else if (http_response != 400) {
|
||||||
debug(3, "Unexpected return code %d from dacp_get_client_volume.", http_response);
|
debug(3, "Unexpected return code %d from dacp_get_client_volume.", http_response);
|
||||||
}
|
}
|
||||||
if (the_actual_volume) {
|
if (the_actual_volume) {
|
||||||
|
|||||||
+34
-31
@@ -24,10 +24,10 @@
|
|||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
|
|||||||
|
|
||||||
shairport_sync_remote_control_set_client(shairportSyncRemoteControlSkeleton, argc->client_ip);
|
shairport_sync_remote_control_set_client(shairportSyncRemoteControlSkeleton, argc->client_ip);
|
||||||
|
|
||||||
|
// although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their
|
||||||
// although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their term).
|
// term).
|
||||||
if (argc->dacp_server_active) {
|
if (argc->dacp_server_active) {
|
||||||
shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
|
shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
|
||||||
} else {
|
} else {
|
||||||
@@ -166,7 +166,6 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
|
|||||||
shairportSyncAdvancedRemoteControlSkeleton, response);
|
shairportSyncAdvancedRemoteControlSkeleton, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (argc->shuffle_status) {
|
switch (argc->shuffle_status) {
|
||||||
case SS_NOT_AVAILABLE:
|
case SS_NOT_AVAILABLE:
|
||||||
new_status = FALSE;
|
new_status = FALSE;
|
||||||
@@ -188,8 +187,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
|
|||||||
// only set this if it's different
|
// only set this if it's different
|
||||||
if (current_status != new_status) {
|
if (current_status != new_status) {
|
||||||
debug(3, "Shuffle State should be changed");
|
debug(3, "Shuffle State should be changed");
|
||||||
shairport_sync_advanced_remote_control_set_shuffle(
|
shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
|
||||||
shairportSyncAdvancedRemoteControlSkeleton, new_status);
|
new_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the metadata array
|
// Build the metadata array
|
||||||
@@ -369,8 +368,9 @@ static gboolean on_handle_volume_down(ShairportSyncRemoteControl *skeleton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleton,
|
static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleton,
|
||||||
GDBusMethodInvocation *invocation, const gdouble volume,
|
GDBusMethodInvocation *invocation,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
const gdouble volume,
|
||||||
|
__attribute__((unused)) gpointer user_data) {
|
||||||
debug(2, "Set airplay volume to %.6f.", volume);
|
debug(2, "Set airplay volume to %.6f.", volume);
|
||||||
char command[256] = "";
|
char command[256] = "";
|
||||||
snprintf(command, sizeof(command), "setproperty?dmcp.device-volume=%.6f", volume);
|
snprintf(command, sizeof(command), "setproperty?dmcp.device-volume=%.6f", volume);
|
||||||
@@ -379,8 +379,6 @@ static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleto
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
|
gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
// debug(1, "\"notify_elapsed_time_callback\" called.");
|
// debug(1, "\"notify_elapsed_time_callback\" called.");
|
||||||
@@ -408,7 +406,7 @@ gboolean notify_delta_time_callback(ShairportSyncDiagnostics *skeleton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
gboolean notify_file_and_line_callback(ShairportSyncDiagnostics *skeleton,
|
gboolean notify_file_and_line_callback(ShairportSyncDiagnostics *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
// debug(1, "\"notify_file_and_line_callback\" called.");
|
// debug(1, "\"notify_file_and_line_callback\" called.");
|
||||||
if (shairport_sync_diagnostics_get_file_and_line(skeleton)) {
|
if (shairport_sync_diagnostics_get_file_and_line(skeleton)) {
|
||||||
config.debugger_show_file_and_line = 1;
|
config.debugger_show_file_and_line = 1;
|
||||||
@@ -463,12 +461,13 @@ gboolean notify_disable_standby_callback(ShairportSync *skeleton,
|
|||||||
|
|
||||||
#ifdef CONFIG_CONVOLUTION
|
#ifdef CONFIG_CONVOLUTION
|
||||||
gboolean notify_convolution_callback(ShairportSync *skeleton,
|
gboolean notify_convolution_callback(ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
// debug(1, "\"notify_convolution_callback\" called.");
|
// debug(1, "\"notify_convolution_callback\" called.");
|
||||||
if (shairport_sync_get_convolution(skeleton)) {
|
if (shairport_sync_get_convolution(skeleton)) {
|
||||||
debug(1, ">> activating convolution");
|
debug(1, ">> activating convolution");
|
||||||
config.convolution = 1;
|
config.convolution = 1;
|
||||||
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
config.convolver_valid =
|
||||||
|
convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
||||||
} else {
|
} else {
|
||||||
debug(1, ">> deactivating convolution");
|
debug(1, ">> deactivating convolution");
|
||||||
config.convolution = 0;
|
config.convolution = 0;
|
||||||
@@ -477,7 +476,7 @@ gboolean notify_convolution_callback(ShairportSync *skeleton,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
gboolean notify_convolution_callback(__attribute__((unused)) ShairportSync *skeleton,
|
gboolean notify_convolution_callback(__attribute__((unused)) ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
warn(">> Convolution support is not built in to this build of Shairport Sync.");
|
warn(">> Convolution support is not built in to this build of Shairport Sync.");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -485,7 +484,7 @@ gboolean notify_convolution_callback(__attribute__((unused)) ShairportSync *skel
|
|||||||
|
|
||||||
#ifdef CONFIG_CONVOLUTION
|
#ifdef CONFIG_CONVOLUTION
|
||||||
gboolean notify_convolution_gain_callback(ShairportSync *skeleton,
|
gboolean notify_convolution_gain_callback(ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
|
|
||||||
gdouble th = shairport_sync_get_convolution_gain(skeleton);
|
gdouble th = shairport_sync_get_convolution_gain(skeleton);
|
||||||
if ((th <= 0.0) && (th >= -100.0)) {
|
if ((th <= 0.0) && (th >= -100.0)) {
|
||||||
@@ -499,34 +498,38 @@ gboolean notify_convolution_gain_callback(ShairportSync *skeleton,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
gboolean notify_convolution_gain_callback(__attribute__((unused)) ShairportSync *skeleton,
|
gboolean notify_convolution_gain_callback(__attribute__((unused)) ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
warn(">> Convolution support is not built in to this build of Shairport Sync.");
|
warn(">> Convolution support is not built in to this build of Shairport Sync.");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_CONVOLUTION
|
#ifdef CONFIG_CONVOLUTION
|
||||||
gboolean notify_convolution_impulse_response_file_callback(ShairportSync *skeleton,
|
gboolean notify_convolution_impulse_response_file_callback(ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused))
|
||||||
|
gpointer user_data) {
|
||||||
char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
|
char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
|
||||||
if (config.convolution_ir_file)
|
if (config.convolution_ir_file)
|
||||||
free(config.convolution_ir_file);
|
free(config.convolution_ir_file);
|
||||||
config.convolution_ir_file = strdup(th);
|
config.convolution_ir_file = strdup(th);
|
||||||
debug(1, ">> setting configuration impulse response filter file to \"%s\".", config.convolution_ir_file);
|
debug(1, ">> setting configuration impulse response filter file to \"%s\".",
|
||||||
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
config.convolution_ir_file);
|
||||||
|
config.convolver_valid =
|
||||||
|
convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
gboolean notify_convolution_impulse_response_file_callback(__attribute__((unused)) ShairportSync *skeleton,
|
gboolean notify_convolution_impulse_response_file_callback(__attribute__((unused))
|
||||||
__attribute__((unused)) gpointer user_data) {
|
ShairportSync *skeleton,
|
||||||
__attribute__((unused)) char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
|
__attribute__((unused))
|
||||||
|
gpointer user_data) {
|
||||||
|
__attribute__((unused)) char *th =
|
||||||
|
(char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gboolean notify_loudness_callback(ShairportSync *skeleton,
|
gboolean notify_loudness_callback(ShairportSync *skeleton,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
// debug(1, "\"notify_loudness_callback\" called.");
|
// debug(1, "\"notify_loudness_callback\" called.");
|
||||||
if (shairport_sync_get_loudness(skeleton)) {
|
if (shairport_sync_get_loudness(skeleton)) {
|
||||||
debug(1, ">> activating loudness");
|
debug(1, ">> activating loudness");
|
||||||
@@ -781,7 +784,6 @@ static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInv
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
|
static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
|
|
||||||
@@ -823,8 +825,8 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
|
|||||||
G_CALLBACK(notify_convolution_gain_callback), NULL);
|
G_CALLBACK(notify_convolution_gain_callback), NULL);
|
||||||
g_signal_connect(shairportSyncSkeleton, "notify::convolution-impulse-response-file",
|
g_signal_connect(shairportSyncSkeleton, "notify::convolution-impulse-response-file",
|
||||||
G_CALLBACK(notify_convolution_impulse_response_file_callback), NULL);
|
G_CALLBACK(notify_convolution_impulse_response_file_callback), NULL);
|
||||||
g_signal_connect(shairportSyncSkeleton, "notify::loudness",
|
g_signal_connect(shairportSyncSkeleton, "notify::loudness", G_CALLBACK(notify_loudness_callback),
|
||||||
G_CALLBACK(notify_loudness_callback), NULL);
|
NULL);
|
||||||
g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
|
g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
|
||||||
G_CALLBACK(notify_loudness_threshold_callback), NULL);
|
G_CALLBACK(notify_loudness_threshold_callback), NULL);
|
||||||
g_signal_connect(shairportSyncSkeleton, "notify::drift-tolerance",
|
g_signal_connect(shairportSyncSkeleton, "notify::drift-tolerance",
|
||||||
@@ -879,7 +881,6 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
|
|||||||
g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-set-airplay-volume",
|
g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-set-airplay-volume",
|
||||||
G_CALLBACK(on_handle_set_airplay_volume), NULL);
|
G_CALLBACK(on_handle_set_airplay_volume), NULL);
|
||||||
|
|
||||||
|
|
||||||
g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "handle-set-volume",
|
g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "handle-set-volume",
|
||||||
G_CALLBACK(on_handle_set_volume), NULL);
|
G_CALLBACK(on_handle_set_volume), NULL);
|
||||||
|
|
||||||
@@ -975,9 +976,11 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
|
|||||||
shairport_sync_set_convolution(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
|
shairport_sync_set_convolution(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
|
||||||
}
|
}
|
||||||
if (config.convolution_ir_file)
|
if (config.convolution_ir_file)
|
||||||
shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton), config.convolution_ir_file);
|
shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
|
||||||
|
config.convolution_ir_file);
|
||||||
// else
|
// else
|
||||||
// shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton), NULL);
|
// shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
|
||||||
|
// NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
shairport_sync_set_version(SHAIRPORT_SYNC(shairportSyncSkeleton), PACKAGE_VERSION);
|
shairport_sync_set_version(SHAIRPORT_SYNC(shairportSyncSkeleton), PACKAGE_VERSION);
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIn
|
|||||||
if (dacpid) {
|
if (dacpid) {
|
||||||
dacpid += strlen("iTunes_Ctrl_");
|
dacpid += strlen("iTunes_Ctrl_");
|
||||||
while (*dacpid == '0')
|
while (*dacpid == '0')
|
||||||
dacpid++; // remove any leading zeroes
|
dacpid++; // remove any leading zeroes
|
||||||
if (strcmp(dacpid, dbs->dacp_id) == 0) {
|
if (strcmp(dacpid, dbs->dacp_id) == 0) {
|
||||||
debug(3, "resolve_callback: client dacp_id \"%s\" dacp port: %u.", dbs->dacp_id, port);
|
debug(3, "resolve_callback: client dacp_id \"%s\" dacp port: %u.", dbs->dacp_id, port);
|
||||||
#ifdef CONFIG_DACP_CLIENT
|
#ifdef CONFIG_DACP_CLIENT
|
||||||
|
|||||||
+1
-1
@@ -24,8 +24,8 @@
|
|||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mdns.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "mdns.h"
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <dns_sd.h>
|
#include <dns_sd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
+1
-1
@@ -25,8 +25,8 @@
|
|||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mdns.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "mdns.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|||||||
+43
-42
@@ -35,10 +35,10 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -173,39 +173,39 @@ char *metadata_write_image_file(const char *buf, int len) {
|
|||||||
// it will return a path to the image file allocated with malloc.
|
// it will return a path to the image file allocated with malloc.
|
||||||
// free it if you don't need it.
|
// free it if you don't need it.
|
||||||
|
|
||||||
char *path = NULL; // this will be what is returned
|
char *path = NULL; // this will be what is returned
|
||||||
if (strcmp(config.cover_art_cache_dir,"") != 0) { // an empty string means do not write the file
|
if (strcmp(config.cover_art_cache_dir, "") != 0) { // an empty string means do not write the file
|
||||||
|
|
||||||
uint8_t img_md5[16];
|
uint8_t img_md5[16];
|
||||||
// uint8_t ap_md5[16];
|
// uint8_t ap_md5[16];
|
||||||
|
|
||||||
#ifdef CONFIG_OPENSSL
|
#ifdef CONFIG_OPENSSL
|
||||||
MD5_CTX ctx;
|
MD5_CTX ctx;
|
||||||
MD5_Init(&ctx);
|
MD5_Init(&ctx);
|
||||||
MD5_Update(&ctx, buf, len);
|
MD5_Update(&ctx, buf, len);
|
||||||
MD5_Final(img_md5, &ctx);
|
MD5_Final(img_md5, &ctx);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_MBEDTLS
|
#ifdef CONFIG_MBEDTLS
|
||||||
#if MBEDTLS_VERSION_MINOR >= 7
|
#if MBEDTLS_VERSION_MINOR >= 7
|
||||||
mbedtls_md5_context tctx;
|
mbedtls_md5_context tctx;
|
||||||
mbedtls_md5_starts_ret(&tctx);
|
mbedtls_md5_starts_ret(&tctx);
|
||||||
mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len);
|
mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len);
|
||||||
mbedtls_md5_finish_ret(&tctx, img_md5);
|
mbedtls_md5_finish_ret(&tctx, img_md5);
|
||||||
#else
|
#else
|
||||||
mbedtls_md5_context tctx;
|
mbedtls_md5_context tctx;
|
||||||
mbedtls_md5_starts(&tctx);
|
mbedtls_md5_starts(&tctx);
|
||||||
mbedtls_md5_update(&tctx, (const unsigned char *)buf, len);
|
mbedtls_md5_update(&tctx, (const unsigned char *)buf, len);
|
||||||
mbedtls_md5_finish(&tctx, img_md5);
|
mbedtls_md5_finish(&tctx, img_md5);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_POLARSSL
|
#ifdef CONFIG_POLARSSL
|
||||||
md5_context tctx;
|
md5_context tctx;
|
||||||
md5_starts(&tctx);
|
md5_starts(&tctx);
|
||||||
md5_update(&tctx, (const unsigned char *)buf, len);
|
md5_update(&tctx, (const unsigned char *)buf, len);
|
||||||
md5_finish(&tctx, img_md5);
|
md5_finish(&tctx, img_md5);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char img_md5_str[33];
|
char img_md5_str[33];
|
||||||
memset(img_md5_str, 0, sizeof(img_md5_str));
|
memset(img_md5_str, 0, sizeof(img_md5_str));
|
||||||
@@ -232,8 +232,8 @@ char *metadata_write_image_file(const char *buf, int len) {
|
|||||||
// if it exists, we're done
|
// if it exists, we're done
|
||||||
char *prefix = "cover-";
|
char *prefix = "cover-";
|
||||||
|
|
||||||
size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) + 1 +
|
size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) +
|
||||||
strlen(ext);
|
1 + strlen(ext);
|
||||||
|
|
||||||
path = malloc(pl + 1);
|
path = malloc(pl + 1);
|
||||||
snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext);
|
snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext);
|
||||||
@@ -309,30 +309,28 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
|
|||||||
if (type == 'core') {
|
if (type == 'core') {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'mper': {
|
case 'mper': {
|
||||||
// get the 64-bit number as a uint64_t by reading two uint32_t s and combining them
|
// get the 64-bit number as a uint64_t by reading two uint32_t s and combining them
|
||||||
uint64_t vl = ntohl(*(uint32_t*)data); // get the high order 32 bits
|
uint64_t vl = ntohl(*(uint32_t *)data); // get the high order 32 bits
|
||||||
vl = vl << 32; // shift them into the correct location
|
vl = vl << 32; // shift them into the correct location
|
||||||
uint64_t ul = ntohl(*(uint32_t*)(data+sizeof(uint32_t))); // and the low order 32 bits
|
uint64_t ul = ntohl(*(uint32_t *)(data + sizeof(uint32_t))); // and the low order 32 bits
|
||||||
vl = vl + ul;
|
vl = vl + ul;
|
||||||
debug(2, "MH Item ID seen: \"%" PRIx64 "\" of length %u.", vl, length);
|
debug(2, "MH Item ID seen: \"%" PRIx64 "\" of length %u.", vl, length);
|
||||||
if (vl != metadata_store.item_id) {
|
if (vl != metadata_store.item_id) {
|
||||||
metadata_store.item_id = vl;
|
metadata_store.item_id = vl;
|
||||||
metadata_store.item_id_changed = 1;
|
metadata_store.item_id_changed = 1;
|
||||||
metadata_store.item_id_received = 1;
|
metadata_store.item_id_received = 1;
|
||||||
debug(2, "MH Item ID set to: \"%" PRIx64 "\"", metadata_store.item_id);
|
debug(2, "MH Item ID set to: \"%" PRIx64 "\"", metadata_store.item_id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
} break;
|
||||||
case 'astm': {
|
case 'astm': {
|
||||||
uint32_t ui = ntohl(*(uint32_t *)data);
|
uint32_t ui = ntohl(*(uint32_t *)data);
|
||||||
debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
|
debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
|
||||||
if (ui != metadata_store.songtime_in_milliseconds) {
|
if (ui != metadata_store.songtime_in_milliseconds) {
|
||||||
metadata_store.songtime_in_milliseconds = ui;
|
metadata_store.songtime_in_milliseconds = ui;
|
||||||
metadata_store.songtime_in_milliseconds_changed = 1;
|
metadata_store.songtime_in_milliseconds_changed = 1;
|
||||||
debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
|
debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
} break;
|
||||||
case 'asal':
|
case 'asal':
|
||||||
cs = strndup(data, length);
|
cs = strndup(data, length);
|
||||||
if (string_update(&metadata_store.album_name, &metadata_store.album_name_changed, cs)) {
|
if (string_update(&metadata_store.album_name, &metadata_store.album_name_changed, cs)) {
|
||||||
@@ -467,7 +465,8 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
|
|||||||
metadata_hub_modify_prolog();
|
metadata_hub_modify_prolog();
|
||||||
debug(2, "MH Picture received, length %u bytes.", length);
|
debug(2, "MH Picture received, length %u bytes.", length);
|
||||||
char uri[2048];
|
char uri[2048];
|
||||||
if ((length > 16) && (strcmp(config.cover_art_cache_dir,"")!=0)) { // if it's okay to write the file
|
if ((length > 16) &&
|
||||||
|
(strcmp(config.cover_art_cache_dir, "") != 0)) { // if it's okay to write the file
|
||||||
char *pathname = metadata_write_image_file(data, length);
|
char *pathname = metadata_write_image_file(data, length);
|
||||||
snprintf(uri, sizeof(uri), "file://%s", pathname);
|
snprintf(uri, sizeof(uri), "file://%s", pathname);
|
||||||
free(pathname);
|
free(pathname);
|
||||||
@@ -526,14 +525,16 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
|
|||||||
break;
|
break;
|
||||||
case 'pbeg':
|
case 'pbeg':
|
||||||
metadata_hub_modify_prolog();
|
metadata_hub_modify_prolog();
|
||||||
changed = ((metadata_store.player_state != PS_PLAYING) || (metadata_store.player_thread_active == 0));
|
changed = ((metadata_store.player_state != PS_PLAYING) ||
|
||||||
|
(metadata_store.player_thread_active == 0));
|
||||||
metadata_store.player_state = PS_PLAYING;
|
metadata_store.player_state = PS_PLAYING;
|
||||||
metadata_store.player_thread_active = 1;
|
metadata_store.player_thread_active = 1;
|
||||||
metadata_hub_modify_epilog(changed);
|
metadata_hub_modify_epilog(changed);
|
||||||
break;
|
break;
|
||||||
case 'pend':
|
case 'pend':
|
||||||
metadata_hub_modify_prolog();
|
metadata_hub_modify_prolog();
|
||||||
changed = ((metadata_store.player_state != PS_STOPPED) || (metadata_store.player_thread_active == 1));
|
changed = ((metadata_store.player_state != PS_STOPPED) ||
|
||||||
|
(metadata_store.player_thread_active == 1));
|
||||||
metadata_store.player_state = PS_STOPPED;
|
metadata_store.player_state = PS_STOPPED;
|
||||||
metadata_store.player_thread_active = 0;
|
metadata_store.player_thread_active = 0;
|
||||||
metadata_hub_modify_epilog(changed);
|
metadata_hub_modify_epilog(changed);
|
||||||
@@ -554,10 +555,10 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
|
|||||||
case 'pvol': {
|
case 'pvol': {
|
||||||
metadata_hub_modify_prolog();
|
metadata_hub_modify_prolog();
|
||||||
// Note: it's assumed that the config.airplay volume has already been correctly set.
|
// Note: it's assumed that the config.airplay volume has already been correctly set.
|
||||||
//int32_t actual_volume;
|
// int32_t actual_volume;
|
||||||
//int gv = dacp_get_volume(&actual_volume);
|
// int gv = dacp_get_volume(&actual_volume);
|
||||||
//metadata_hub_modify_prolog();
|
// metadata_hub_modify_prolog();
|
||||||
//if ((gv == 200) && (metadata_store.speaker_volume != actual_volume)) {
|
// if ((gv == 200) && (metadata_store.speaker_volume != actual_volume)) {
|
||||||
// metadata_store.speaker_volume = actual_volume;
|
// metadata_store.speaker_volume = actual_volume;
|
||||||
// changed = 1;
|
// changed = 1;
|
||||||
//}
|
//}
|
||||||
|
|||||||
+9
-8
@@ -23,9 +23,9 @@
|
|||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -48,12 +48,12 @@ double airplay_volume_to_mpris_volume(double sp) {
|
|||||||
sp = -30.0;
|
sp = -30.0;
|
||||||
if (sp > 0.0)
|
if (sp > 0.0)
|
||||||
sp = 0.0;
|
sp = 0.0;
|
||||||
sp = (sp/30.0)+1;
|
sp = (sp / 30.0) + 1;
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
double mpris_volume_to_airplay_volume(double sp) {
|
double mpris_volume_to_airplay_volume(double sp) {
|
||||||
sp = (sp-1.0)*30.0;
|
sp = (sp - 1.0) * 30.0;
|
||||||
if (sp < -30.0)
|
if (sp < -30.0)
|
||||||
sp = -30.0;
|
sp = -30.0;
|
||||||
if (sp > 0.0)
|
if (sp > 0.0)
|
||||||
@@ -64,7 +64,8 @@ double mpris_volume_to_airplay_volume(double sp) {
|
|||||||
void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
|
void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
|
||||||
// debug(1, "MPRIS metadata watcher called");
|
// debug(1, "MPRIS metadata watcher called");
|
||||||
char response[100];
|
char response[100];
|
||||||
media_player2_player_set_volume(mprisPlayerPlayerSkeleton, airplay_volume_to_mpris_volume(argc->airplay_volume));
|
media_player2_player_set_volume(mprisPlayerPlayerSkeleton,
|
||||||
|
airplay_volume_to_mpris_volume(argc->airplay_volume));
|
||||||
switch (argc->repeat_status) {
|
switch (argc->repeat_status) {
|
||||||
case RS_NOT_AVAILABLE:
|
case RS_NOT_AVAILABLE:
|
||||||
strcpy(response, "Not Available");
|
strcpy(response, "Not Available");
|
||||||
@@ -175,7 +176,8 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
|
|||||||
// Add in the Track ID based on the 'mper' metadata if it is non-zero
|
// Add in the Track ID based on the 'mper' metadata if it is non-zero
|
||||||
if (argc->item_id != 0) {
|
if (argc->item_id != 0) {
|
||||||
char trackidstring[128];
|
char trackidstring[128];
|
||||||
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "", argc->item_id);
|
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
|
||||||
|
argc->item_id);
|
||||||
GVariant *trackid = g_variant_new("o", trackidstring);
|
GVariant *trackid = g_variant_new("o", trackidstring);
|
||||||
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
|
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
|
||||||
}
|
}
|
||||||
@@ -276,8 +278,8 @@ static gboolean on_handle_play(MediaPlayer2Player *skeleton, GDBusMethodInvocati
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean on_handle_set_volume(MediaPlayer2Player *skeleton,
|
static gboolean on_handle_set_volume(MediaPlayer2Player *skeleton,
|
||||||
GDBusMethodInvocation *invocation, const gdouble volume,
|
GDBusMethodInvocation *invocation, const gdouble volume,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
double ap_volume = mpris_volume_to_airplay_volume(volume);
|
double ap_volume = mpris_volume_to_airplay_volume(volume);
|
||||||
debug(2, "Set mpris volume to %.6f, i.e. airplay volume to %.6f.", volume, ap_volume);
|
debug(2, "Set mpris volume to %.6f, i.e. airplay volume to %.6f.", volume, ap_volume);
|
||||||
char command[256] = "";
|
char command[256] = "";
|
||||||
@@ -334,7 +336,6 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam
|
|||||||
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-set-volume", G_CALLBACK(on_handle_set_volume),
|
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-set-volume", G_CALLBACK(on_handle_set_volume),
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
|
||||||
add_metadata_watcher(mpris_metadata_watcher, NULL);
|
add_metadata_watcher(mpris_metadata_watcher, NULL);
|
||||||
|
|
||||||
debug(1, "MPRIS service started at \"%s\" on the %s bus.", name,
|
debug(1, "MPRIS service started at \"%s\" on the %s bus.", name,
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ typedef struct audio_buffer_entry { // decoded audio packets
|
|||||||
uint16_t resend_request_number;
|
uint16_t resend_request_number;
|
||||||
signed short *data;
|
signed short *data;
|
||||||
seq_t sequence_number;
|
seq_t sequence_number;
|
||||||
uint64_t initialisation_time; // the time the packet was added or the time it was noticed the packet was missing
|
uint64_t initialisation_time; // the time the packet was added or the time it was noticed the
|
||||||
uint64_t resend_time; // time of last resend request or zero
|
// packet was missing
|
||||||
uint32_t given_timestamp; // for debugging and checking
|
uint64_t resend_time; // time of last resend request or zero
|
||||||
int length; // the length of the decoded data
|
uint32_t given_timestamp; // for debugging and checking
|
||||||
|
int length; // the length of the decoded data
|
||||||
} abuf_t;
|
} abuf_t;
|
||||||
|
|
||||||
// default buffer size
|
// default buffer size
|
||||||
|
|||||||
@@ -74,9 +74,13 @@ uint64_t local_to_remote_time_difference_now(rtsp_conn_info *conn) {
|
|||||||
|
|
||||||
uint64_t result = conn->local_to_remote_time_difference;
|
uint64_t result = conn->local_to_remote_time_difference;
|
||||||
if (conn->local_to_remote_time_gradient >= 1.0) {
|
if (conn->local_to_remote_time_gradient >= 1.0) {
|
||||||
result = conn->local_to_remote_time_difference + (uint64_t)((conn->local_to_remote_time_gradient - 1.0) * time_since_last_local_to_remote_time_difference_measurement);
|
result = conn->local_to_remote_time_difference +
|
||||||
|
(uint64_t)((conn->local_to_remote_time_gradient - 1.0) *
|
||||||
|
time_since_last_local_to_remote_time_difference_measurement);
|
||||||
} else {
|
} else {
|
||||||
result = conn->local_to_remote_time_difference - (uint64_t)((1.0 - conn->local_to_remote_time_gradient) * time_since_last_local_to_remote_time_difference_measurement);
|
result = conn->local_to_remote_time_difference -
|
||||||
|
(uint64_t)((1.0 - conn->local_to_remote_time_gradient) *
|
||||||
|
time_since_last_local_to_remote_time_difference_measurement);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -111,8 +115,7 @@ void *rtp_audio_receiver(void *arg) {
|
|||||||
|
|
||||||
uint64_t local_time_now_ns = get_absolute_time_in_ns();
|
uint64_t local_time_now_ns = get_absolute_time_in_ns();
|
||||||
if (time_of_previous_packet_ns) {
|
if (time_of_previous_packet_ns) {
|
||||||
float time_interval_us =
|
float time_interval_us = (local_time_now_ns - time_of_previous_packet_ns) * 0.001;
|
||||||
(local_time_now_ns - time_of_previous_packet_ns) * 0.001;
|
|
||||||
time_of_previous_packet_ns = local_time_now_ns;
|
time_of_previous_packet_ns = local_time_now_ns;
|
||||||
if (time_interval_us > longest_packet_time_interval_us)
|
if (time_interval_us > longest_packet_time_interval_us)
|
||||||
longest_packet_time_interval_us = time_interval_us;
|
longest_packet_time_interval_us = time_interval_us;
|
||||||
@@ -121,8 +124,9 @@ void *rtp_audio_receiver(void *arg) {
|
|||||||
stat_mean += stat_delta / stat_n;
|
stat_mean += stat_delta / stat_n;
|
||||||
stat_M2 += stat_delta * (time_interval_us - stat_mean);
|
stat_M2 += stat_delta * (time_interval_us - stat_mean);
|
||||||
if (stat_n % 2500 == 0) {
|
if (stat_n % 2500 == 0) {
|
||||||
debug(2, "Packet reception interval stats: mean, standard deviation and max for the last "
|
debug(2,
|
||||||
"2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
|
"Packet reception interval stats: mean, standard deviation and max for the last "
|
||||||
|
"2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
|
||||||
stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_packet_time_interval_us);
|
stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_packet_time_interval_us);
|
||||||
stat_n = 0;
|
stat_n = 0;
|
||||||
stat_mean = 0.0;
|
stat_mean = 0.0;
|
||||||
@@ -290,7 +294,7 @@ void *rtp_control_receiver(void *arg) {
|
|||||||
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
||||||
pn = nctohl(&packet[12]);
|
pn = nctohl(&packet[12]);
|
||||||
pn = pn * 1000000000;
|
pn = pn * 1000000000;
|
||||||
pn = pn >> 32; // this many nanoseconds from the fractional part
|
pn = pn >> 32; // this many nanoseconds from the fractional part
|
||||||
remote_time_of_sync = ps + pn;
|
remote_time_of_sync = ps + pn;
|
||||||
|
|
||||||
// debug(1,"Remote Sync Time: " PRIu64 "",remote_time_of_sync);
|
// debug(1,"Remote Sync Time: " PRIu64 "",remote_time_of_sync);
|
||||||
@@ -354,9 +358,10 @@ void *rtp_control_receiver(void *arg) {
|
|||||||
|
|
||||||
if (la != conn->latency) {
|
if (la != conn->latency) {
|
||||||
conn->latency = la;
|
conn->latency = la;
|
||||||
debug(3, "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
|
debug(3,
|
||||||
", minimum latency: %" PRIu32 ", maximum "
|
"New latency detected: %" PRIu32 ", sync latency: %" PRIu32
|
||||||
"latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
|
", minimum latency: %" PRIu32 ", maximum "
|
||||||
|
"latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
|
||||||
la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency,
|
la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency,
|
||||||
conn->maximum_latency, config.fixedLatencyOffset);
|
conn->maximum_latency, config.fixedLatencyOffset);
|
||||||
}
|
}
|
||||||
@@ -573,7 +578,7 @@ void *rtp_timing_receiver(void *arg) {
|
|||||||
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
||||||
pn = nctohl(&packet[20]);
|
pn = nctohl(&packet[20]);
|
||||||
pn = pn * 1000000000;
|
pn = pn * 1000000000;
|
||||||
pn = pn >> 32; // this many nanoseconds from the fractional part
|
pn = pn >> 32; // this many nanoseconds from the fractional part
|
||||||
distant_receive_time = ps + pn;
|
distant_receive_time = ps + pn;
|
||||||
|
|
||||||
// distant_transmit_time =
|
// distant_transmit_time =
|
||||||
@@ -583,7 +588,7 @@ void *rtp_timing_receiver(void *arg) {
|
|||||||
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
ps = ps * 1000000000; // this many nanoseconds from the whole seconds
|
||||||
pn = nctohl(&packet[28]);
|
pn = nctohl(&packet[28]);
|
||||||
pn = pn * 1000000000;
|
pn = pn * 1000000000;
|
||||||
pn = pn >> 32; // this many nanoseconds from the fractional part
|
pn = pn >> 32; // this many nanoseconds from the fractional part
|
||||||
distant_transmit_time = ps + pn;
|
distant_transmit_time = ps + pn;
|
||||||
|
|
||||||
uint64_t remote_processing_time = 0;
|
uint64_t remote_processing_time = 0;
|
||||||
@@ -777,7 +782,9 @@ void *rtp_timing_receiver(void *arg) {
|
|||||||
// debug(1,"local to remote time gradient is %12.2f ppm, based on %d
|
// debug(1,"local to remote time gradient is %12.2f ppm, based on %d
|
||||||
// samples.",conn->local_to_remote_time_gradient*1000000,sample_count);
|
// samples.",conn->local_to_remote_time_gradient*1000000,sample_count);
|
||||||
} else {
|
} else {
|
||||||
debug(2, "Time ping turnaround time: " PRIu64 " ns -- it looks like a timing ping was lost.",
|
debug(2,
|
||||||
|
"Time ping turnaround time: %" PRIu64
|
||||||
|
" ns -- it looks like a timing ping was lost.",
|
||||||
return_time);
|
return_time);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1029,7 +1036,7 @@ const int use_nominal_rate = 0; // specify whether to use the nominal input rate
|
|||||||
int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_conn_info *conn) {
|
int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_conn_info *conn) {
|
||||||
int result = 1;
|
int result = 1;
|
||||||
uint32_t fs = conn->input_rate;
|
uint32_t fs = conn->input_rate;
|
||||||
*frames = fs; // default value to return
|
*frames = fs; // default value to return
|
||||||
*time = 1000000000; // default value to return
|
*time = 1000000000; // default value to return
|
||||||
if ((conn->initial_reference_time) && (conn->initial_reference_timestamp)) {
|
if ((conn->initial_reference_time) && (conn->initial_reference_timestamp)) {
|
||||||
// uint32_t local_frames = conn->reference_timestamp - conn->initial_reference_timestamp;
|
// uint32_t local_frames = conn->reference_timestamp - conn->initial_reference_timestamp;
|
||||||
@@ -1178,22 +1185,20 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
|
|||||||
(struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) {
|
(struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) {
|
||||||
char em[1024];
|
char em[1024];
|
||||||
strerror_r(errno, em, sizeof(em));
|
strerror_r(errno, em, sizeof(em));
|
||||||
debug(2, "Error %d using sendto to request a resend: \"%s\".",
|
debug(2, "Error %d using sendto to request a resend: \"%s\".", errno, em);
|
||||||
errno, em);
|
|
||||||
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
|
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
|
||||||
} else {
|
} else {
|
||||||
conn->rtp_time_of_last_resend_request_error_ns = 0;
|
conn->rtp_time_of_last_resend_request_error_ns = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
debug(
|
debug(3, "Dropping resend request packet to simulate a bad network. Backing off for 0.3 "
|
||||||
3,
|
"second.");
|
||||||
"Dropping resend request packet to simulate a bad network. Backing off for 0.3 "
|
|
||||||
"second.");
|
|
||||||
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
|
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug(1, "Suppressing a resend request due to a resend sendto error in the last 0.3 seconds.");
|
debug(1,
|
||||||
|
"Suppressing a resend request due to a resend sendto error in the last 0.3 seconds.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if (!request_sent) {
|
// if (!request_sent) {
|
||||||
|
|||||||
@@ -286,15 +286,17 @@ void *player_watchdog_thread_code(void *arg) {
|
|||||||
uint64_t last_watchdog_bark_time = conn->watchdog_bark_time;
|
uint64_t last_watchdog_bark_time = conn->watchdog_bark_time;
|
||||||
debug_mutex_unlock(&conn->watchdog_mutex, 0);
|
debug_mutex_unlock(&conn->watchdog_mutex, 0);
|
||||||
if (last_watchdog_bark_time != 0) {
|
if (last_watchdog_bark_time != 0) {
|
||||||
uint64_t time_since_last_bark = (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000;
|
uint64_t time_since_last_bark =
|
||||||
|
(get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000;
|
||||||
uint64_t ct = config.timeout; // go from int to 64-bit int
|
uint64_t ct = config.timeout; // go from int to 64-bit int
|
||||||
|
|
||||||
if (time_since_last_bark >= ct) {
|
if (time_since_last_bark >= ct) {
|
||||||
conn->watchdog_barks++;
|
conn->watchdog_barks++;
|
||||||
if (conn->watchdog_barks == 1) {
|
if (conn->watchdog_barks == 1) {
|
||||||
// debuglev = 3; // tell us everything.
|
// debuglev = 3; // tell us everything.
|
||||||
debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone "
|
debug(1,
|
||||||
"of the heart\".",
|
"Connection %d: As Yeats almost said, \"Too long a silence / can make a stone "
|
||||||
|
"of the heart\".",
|
||||||
conn->connection_number);
|
conn->connection_number);
|
||||||
conn->stop = 1;
|
conn->stop = 1;
|
||||||
pthread_cancel(conn->thread);
|
pthread_cancel(conn->thread);
|
||||||
@@ -571,7 +573,7 @@ int msg_handle_line(rtsp_message **pmsg, char *line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
debug(3,"msg_handle_line fail");
|
debug(3, "msg_handle_line fail");
|
||||||
msg_free(pmsg);
|
msg_free(pmsg);
|
||||||
*pmsg = NULL;
|
*pmsg = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -614,7 +616,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
|
|||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", conn->connection_number);
|
debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!",
|
||||||
|
conn->connection_number);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (errno != ECONNRESET) {
|
if (errno != ECONNRESET) {
|
||||||
@@ -627,15 +630,15 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
|
|||||||
goto shutdown;
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* // this outputs the message received
|
/* // this outputs the message received
|
||||||
{
|
{
|
||||||
void *pt = malloc(nread+1);
|
void *pt = malloc(nread+1);
|
||||||
memset(pt, 0, nread+1);
|
memset(pt, 0, nread+1);
|
||||||
memcpy(pt, buf + inbuf, nread);
|
memcpy(pt, buf + inbuf, nread);
|
||||||
debug(1, "Incoming string on port: \"%s\"",pt);
|
debug(1, "Incoming string on port: \"%s\"",pt);
|
||||||
free(pt);
|
free(pt);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inbuf += nread;
|
inbuf += nread;
|
||||||
|
|
||||||
@@ -644,7 +647,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
|
|||||||
msg_size = msg_handle_line(the_packet, buf);
|
msg_size = msg_handle_line(the_packet, buf);
|
||||||
|
|
||||||
if (!(*the_packet)) {
|
if (!(*the_packet)) {
|
||||||
debug(1,"Connection %d: rtsp_read_request can't find an RTSP header.", conn->connection_number);
|
debug(1, "Connection %d: rtsp_read_request can't find an RTSP header.",
|
||||||
|
conn->connection_number);
|
||||||
reply = rtsp_read_request_response_bad_packet;
|
reply = rtsp_read_request_response_bad_packet;
|
||||||
goto shutdown;
|
goto shutdown;
|
||||||
}
|
}
|
||||||
@@ -851,9 +855,10 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *
|
|||||||
rtsp_message *resp) {
|
rtsp_message *resp) {
|
||||||
debug(3, "Connection %d: OPTIONS", conn->connection_number);
|
debug(3, "Connection %d: OPTIONS", conn->connection_number);
|
||||||
resp->respcode = 200;
|
resp->respcode = 200;
|
||||||
msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, "
|
msg_add_header(resp, "Public",
|
||||||
"PAUSE, FLUSH, TEARDOWN, "
|
"ANNOUNCE, SETUP, RECORD, "
|
||||||
"OPTIONS, GET_PARAMETER, SET_PARAMETER");
|
"PAUSE, FLUSH, TEARDOWN, "
|
||||||
|
"OPTIONS, GET_PARAMETER, SET_PARAMETER");
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
|
void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
|
||||||
@@ -994,8 +999,9 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
|||||||
msg_add_header(resp, "Session", "1");
|
msg_add_header(resp, "Session", "1");
|
||||||
|
|
||||||
resp->respcode = 200; // it all worked out okay
|
resp->respcode = 200; // it all worked out okay
|
||||||
debug(1, "Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: "
|
debug(1,
|
||||||
"%d, Timing: %d and Audio: %d.",
|
"Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: "
|
||||||
|
"%d, Timing: %d and Audio: %d.",
|
||||||
conn->connection_number, conn->dacp_id, &conn->client_ip_string,
|
conn->connection_number, conn->dacp_id, &conn->client_ip_string,
|
||||||
&conn->self_ip_string, conn->local_control_port, conn->local_timing_port,
|
&conn->self_ip_string, conn->local_control_port, conn->local_timing_port,
|
||||||
conn->local_audio_port);
|
conn->local_audio_port);
|
||||||
@@ -1590,7 +1596,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m
|
|||||||
char *ct = msg_get_header(req, "Content-Type");
|
char *ct = msg_get_header(req, "Content-Type");
|
||||||
|
|
||||||
if (ct) {
|
if (ct) {
|
||||||
// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
|
// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA
|
#ifdef CONFIG_METADATA
|
||||||
// It seems that the rtptime of the message is used as a kind of an ID that
|
// It seems that the rtptime of the message is used as a kind of an ID that
|
||||||
@@ -1888,7 +1894,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
|
|||||||
|
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
unsigned int max_param = sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]);
|
unsigned int max_param = sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]);
|
||||||
char* found;
|
char *found;
|
||||||
while ((found = strsep(&pfmtp, " \t")) != NULL && i < max_param) {
|
while ((found = strsep(&pfmtp, " \t")) != NULL && i < max_param) {
|
||||||
conn->stream.fmtp[i++] = atoi(found);
|
conn->stream.fmtp[i++] = atoi(found);
|
||||||
}
|
}
|
||||||
@@ -2370,8 +2376,9 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
|||||||
if (strcmp(req->method, "OPTIONS") !=
|
if (strcmp(req->method, "OPTIONS") !=
|
||||||
0) // the options message is very common, so don't log it until level 3
|
0) // the options message is very common, so don't log it until level 3
|
||||||
debug_level = 2;
|
debug_level = 2;
|
||||||
debug(debug_level, "Connection %d: Received an RTSP Packet of type \"%s\":",
|
debug(debug_level,
|
||||||
conn->connection_number, req->method),
|
"Connection %d: Received an RTSP Packet of type \"%s\":", conn->connection_number,
|
||||||
|
req->method),
|
||||||
debug_print_msg_headers(debug_level, req);
|
debug_print_msg_headers(debug_level, req);
|
||||||
|
|
||||||
apple_challenge(conn->fd, req, resp);
|
apple_challenge(conn->fd, req, resp);
|
||||||
@@ -2420,8 +2427,9 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
|||||||
if (conn->stop == 0) {
|
if (conn->stop == 0) {
|
||||||
int err = msg_write_response(conn->fd, resp);
|
int err = msg_write_response(conn->fd, resp);
|
||||||
if (err) {
|
if (err) {
|
||||||
debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the "
|
debug(1,
|
||||||
"connection.",
|
"Connection %d: Unable to write an RTSP message response. Terminating the "
|
||||||
|
"connection.",
|
||||||
conn->connection_number);
|
conn->connection_number);
|
||||||
struct linger so_linger;
|
struct linger so_linger;
|
||||||
so_linger.l_onoff = 1; // "true"
|
so_linger.l_onoff = 1; // "true"
|
||||||
@@ -2474,10 +2482,11 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
|||||||
if (reply == -1) {
|
if (reply == -1) {
|
||||||
char errorstring[1024];
|
char errorstring[1024];
|
||||||
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
|
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
|
||||||
debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, (char *)errorstring);
|
debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno,
|
||||||
|
(char *)errorstring);
|
||||||
} else if (reply != (ssize_t)strlen(response_text)) {
|
} else if (reply != (ssize_t)strlen(response_text)) {
|
||||||
debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", strlen(response_text),
|
debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.",
|
||||||
reply);
|
strlen(response_text), reply);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.",
|
debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.",
|
||||||
@@ -2690,7 +2699,7 @@ void rtsp_listen_loop(void) {
|
|||||||
debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
|
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
|
#ifdef AF_INET6
|
||||||
if (local_info->SAFAMILY == AF_INET6) {
|
if (local_info->SAFAMILY == AF_INET6) {
|
||||||
// IPv6:
|
// IPv6:
|
||||||
|
|
||||||
@@ -2707,7 +2716,7 @@ void rtsp_listen_loop(void) {
|
|||||||
debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.",
|
debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.",
|
||||||
conn->connection_number, remote_ip6, rport, ip6, tport);
|
conn->connection_number, remote_ip6, rport, ip6, tport);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
debug(1, "Error figuring out Shairport Sync's own IP number.");
|
debug(1, "Error figuring out Shairport Sync's own IP number.");
|
||||||
@@ -2735,8 +2744,8 @@ void rtsp_listen_loop(void) {
|
|||||||
pthread_cleanup_pop(1); // should never happen
|
pthread_cleanup_pop(1); // should never happen
|
||||||
} else {
|
} else {
|
||||||
warn("could not establish a service on port %d -- program terminating. Is another instance of "
|
warn("could not establish a service on port %d -- program terminating. Is another instance of "
|
||||||
"Shairport Sync running on this device?",
|
"Shairport Sync running on this device?",
|
||||||
config.port);
|
config.port);
|
||||||
}
|
}
|
||||||
// debug(1, "Oops -- fell out of the RTSP select loop");
|
// debug(1, "Oops -- fell out of the RTSP select loop");
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-17
@@ -18,11 +18,20 @@ general =
|
|||||||
// interpolation = "auto"; // aka "stuffing". Default is "auto". Alternatives are "basic" or "soxr". Choose "soxr" only if you have a reasonably fast processor and Shairport Sync has been built with "soxr" support.
|
// interpolation = "auto"; // aka "stuffing". Default is "auto". Alternatives are "basic" or "soxr". Choose "soxr" only if you have a reasonably fast processor and Shairport Sync has been built with "soxr" support.
|
||||||
// output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one.
|
// output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one.
|
||||||
// mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one.
|
// mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one.
|
||||||
|
// interface = "name"; // Use this advanced setting to specify the interface on which Shairport Sync should provide its service. Leave it commented out to get the default, which is to select the interface(s) automatically.
|
||||||
// port = 5000; // Listen for service requests on this port
|
// port = 5000; // Listen for service requests on this port
|
||||||
// udp_port_base = 6001; // start allocating UDP ports from this port number when needed
|
// udp_port_base = 6001; // start allocating UDP ports from this port number when needed
|
||||||
// udp_port_range = 10; // look for free ports in this number of places, starting at the UDP port base. Allow at least 10, though only three are needed in a steady state.
|
// udp_port_range = 10; // look for free ports in this number of places, starting at the UDP port base. Allow at least 10, though only three are needed in a steady state.
|
||||||
|
// regtype = "_raop._tcp"; // Use this advanced setting to set the service type and transport to be advertised by Zeroconf/Bonjour. Default is "_raop._tcp".
|
||||||
|
|
||||||
// drift_tolerance_in_seconds = 0.002; // allow a timing error of this number of seconds of drift away from exact synchronisation before attempting to correct it
|
// drift_tolerance_in_seconds = 0.002; // allow a timing error of this number of seconds of drift away from exact synchronisation before attempting to correct it
|
||||||
// resync_threshold_in_seconds = 0.050; // a synchronisation error greater than this number of seconds will cause resynchronisation; 0 disables it
|
// resync_threshold_in_seconds = 0.050; // a synchronisation error greater than this number of seconds will cause resynchronisation; 0 disables it
|
||||||
|
|
||||||
|
// playback_mode = "stereo"; // This can be "stereo", "mono", "reverse stereo", "both left" or "both right". Default is "stereo".
|
||||||
|
// alac_decoder = "hammerton"; // This can be "hammerton" or "apple". This advanced setting allows you to choose
|
||||||
|
// the original Shairport decoder by David Hammerton or the Apple Lossless Audio Codec (ALAC) decoder written by Apple.
|
||||||
|
// If you build Shairport Sync with the flag --with-apple-alac, the Apple ALAC decoder will be chosen by default.
|
||||||
|
|
||||||
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
|
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
|
||||||
// volume_range_db = 60 ; // use this advanced setting to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range.
|
// volume_range_db = 60 ; // use this advanced setting to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range.
|
||||||
// volume_max_db = 0.0 ; // use this advanced setting, which must have a decimal point in it, to set the maximum volume, in dB, you wish to use.
|
// volume_max_db = 0.0 ; // use this advanced setting, which must have a decimal point in it, to set the maximum volume, in dB, you wish to use.
|
||||||
@@ -36,25 +45,23 @@ general =
|
|||||||
// The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument.
|
// The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument.
|
||||||
// AirPlay volume goes from 0 to -30 and -144 means "mute".
|
// AirPlay volume goes from 0 to -30 and -144 means "mute".
|
||||||
|
|
||||||
// regtype = "_raop._tcp"; // Use this advanced setting to set the service type and transport to be advertised by Zeroconf/Bonjour. Default is "_raop._tcp".
|
// audio_backend_latency_offset_in_seconds = 0.0; // This is added to the latency requested by the player to delay or advance the output by a fixed amount.
|
||||||
// playback_mode = "stereo"; // This can be "stereo", "mono", "reverse stereo", "both left" or "both right". Default is "stereo".
|
// Use it, for example, to compensate for a fixed delay in the audio back end.
|
||||||
// alac_decoder = "hammerton"; // This can be "hammerton" or "apple". This advanced setting allows you to choose
|
// E.g. if the output device, e.g. a soundbar, takes 100 ms to process audio, set this to -0.1 to deliver the audio
|
||||||
// the original Shairport decoder by David Hammerton or the Apple Lossless Audio Codec (ALAC) decoder written by Apple.
|
// to the output device 100 ms early, allowing it time to process the audio and output it perfectly in sync.
|
||||||
// If you build Shairport Sync with the flag --with-apple-alac, the Apple ALAC decoder will be chosen by default.
|
// audio_backend_buffer_desired_length_in_seconds = 0.2; // If set too small, buffer underflow occurs on low-powered machines.
|
||||||
// interface = "name"; // Use this advanced setting to specify the interface on which Shairport Sync should provide its service. Leave it commented out to get the default, which is to select the interface(s) automatically.
|
// Too long and the response time to volume changes becomes annoying.
|
||||||
|
// Default is 0.2 seconds in the alsa backend, 0.35 seconds in the pa backend and 1.0 seconds otherwise.
|
||||||
// audio_backend_latency_offset_in_seconds = 0.0; // This is added to the latency requested by the player to delay or advance the output by a fixed amount. Use it, for example, to compensate for a fixed delay in the audio back end. E.g. if the output device, e.g. a soundbar, takes 100 ms to process audio, set this to -0.1 to deliver the audio to the output device 100 ms early, allowing it time to process the audio and output it perfectly in sync.
|
|
||||||
// audio_backend_buffer_desired_length_in_seconds = 0.2; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response time to volume changes becomes annoying. Default is 0.2 seconds in the alsa backend, 0.35 seconds in the pa backend and 1.0 seconds otherwise.
|
|
||||||
// audio_backend_buffer_interpolation_threshold_in_seconds = 0.075; // Advanced feature. If the buffer size drops below this, stop using time-consuming interpolation like soxr to avoid dropouts due to underrun.
|
// audio_backend_buffer_interpolation_threshold_in_seconds = 0.075; // Advanced feature. If the buffer size drops below this, stop using time-consuming interpolation like soxr to avoid dropouts due to underrun.
|
||||||
// audio_backend_silent_lead_in_time = "auto"; // This optional advanced setting, either "auto" or a positive number, sets the length of the period of silence that precedes the start of the audio. The default is "auto" -- the silent lead-in starts as soon as the player starts sending packets. Values greater than the latency are ignored. Values that are too low will affect initial synchronisation.
|
// audio_backend_silent_lead_in_time = "auto"; // This optional advanced setting, either "auto" or a positive number, sets the length of the period of silence that precedes the start of the audio.
|
||||||
// audio_backend_silent_lead_in_initial_period = 0.5; // This optional advanced setting sets the length, in seconds, of the first "block" of silence sent to the DAC -- some DACs need a fairly substantial block to prevent lots of initial underrun.
|
// The default is "auto" -- the silent lead-in starts as soon as the player starts sending packets.
|
||||||
// audio_backend_silent_lead_in_minimum_adjustments_to_make = 3; // This optional advanced setting sets the minimum number of times Shairport Sync will readjust the length of the silent lead in to ensure that the audio starts at precisely the correct time.
|
// Values greater than the latency are ignored. Values that are too low will affect initial synchronisation.
|
||||||
// audio_backend_silent_lead_in_adjustment_interval = 0.1; // This optional advanced setting sets the interval, in seconds, between adjustments of the silent lead in to ensure that the audio starts at precisely the correct time.
|
|
||||||
|
|
||||||
// dbus_service_bus = "system"; // The Shairport Sync dbus interface, if selected at compilation, will appear
|
// dbus_service_bus = "system"; // The Shairport Sync dbus interface, if selected at compilation, will appear
|
||||||
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
|
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
|
||||||
// mpris_service_bus = "system"; // The Shairport Sync mpris interface, if selected at compilation, will appear
|
// mpris_service_bus = "system"; // The Shairport Sync mpris interface, if selected at compilation, will appear
|
||||||
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
|
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
|
||||||
|
|
||||||
// resend_control_first_check_time = 0.10; // Use this optional advanced setting to set the wait time in seconds before deciding a packet is missing.
|
// resend_control_first_check_time = 0.10; // Use this optional advanced setting to set the wait time in seconds before deciding a packet is missing.
|
||||||
// resend_control_check_interval_time = 0.25; // Use this optional advanced setting to set the time in seconds between requests for a missing packet.
|
// resend_control_check_interval_time = 0.25; // Use this optional advanced setting to set the time in seconds between requests for a missing packet.
|
||||||
// resend_control_last_check_time = 0.10; // Use this optional advanced setting to set the latest time, in seconds, by which the last check should be done before the estimated time of a missing packet's transfer to the output buffer.
|
// resend_control_last_check_time = 0.10; // Use this optional advanced setting to set the latest time, in seconds, by which the last check should be done before the estimated time of a missing packet's transfer to the output buffer.
|
||||||
@@ -64,14 +71,14 @@ general =
|
|||||||
// Advanced parameters for controlling how Shairport Sync stays active and how it runs a session
|
// Advanced parameters for controlling how Shairport Sync stays active and how it runs a session
|
||||||
sessioncontrol =
|
sessioncontrol =
|
||||||
{
|
{
|
||||||
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
|
||||||
// run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
|
||||||
|
|
||||||
// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed.
|
// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed.
|
||||||
// run_this_before_entering_active_state = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
// run_this_before_entering_active_state = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
// run_this_after_exiting_active_state = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
// run_this_after_exiting_active_state = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
// active_state_timeout = 10.0; // wait for this number of seconds after play ends before leaving the active state, unless another play session begins.
|
// active_state_timeout = 10.0; // wait for this number of seconds after play ends before leaving the active state, unless another play session begins.
|
||||||
|
|
||||||
|
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
|
// run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
|
|
||||||
// run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a program on here to deal with it. An error code-string is passed as the last argument.
|
// run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a program on here to deal with it. An error code-string is passed as the last argument.
|
||||||
// Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem.
|
// Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem.
|
||||||
// You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning!
|
// You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning!
|
||||||
@@ -91,15 +98,19 @@ alsa =
|
|||||||
// output_device = "default"; // the name of the alsa output device. Use "shairport-sync -h" to discover the names of ALSA hardware devices. Use "alsamixer" or "aplay" to find out the names of devices, mixers, etc.
|
// output_device = "default"; // the name of the alsa output device. Use "shairport-sync -h" to discover the names of ALSA hardware devices. Use "alsamixer" or "aplay" to find out the names of devices, mixers, etc.
|
||||||
// mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software.
|
// mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software.
|
||||||
// mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this.
|
// mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this.
|
||||||
|
|
||||||
// output_rate = "auto"; // can be "auto", 44100, 88200, 176400 or 352800, but the device must have the capability.
|
// output_rate = "auto"; // can be "auto", 44100, 88200, 176400 or 352800, but the device must have the capability.
|
||||||
// output_format = "auto"; // can be "auto", "U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor.
|
// output_format = "auto"; // can be "auto", "U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor.
|
||||||
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no".
|
|
||||||
|
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no" This is really meant for troubleshooting.
|
||||||
|
|
||||||
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
|
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
|
||||||
// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
|
// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
|
||||||
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
|
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
|
||||||
// use_hardware_mute_if_available = "no"; // Use this optional advanced setting to control whether the hardware in the DAC is used for muting. Default is "no", for compatibility with other audio players.
|
// use_hardware_mute_if_available = "no"; // Use this optional advanced setting to control whether the hardware in the DAC is used for muting. Default is "no", for compatibility with other audio players.
|
||||||
// maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms.
|
// maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms.
|
||||||
// use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems.
|
// use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems.
|
||||||
|
|
||||||
// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never".
|
// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never".
|
||||||
// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device.
|
// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device.
|
||||||
// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked.
|
// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked.
|
||||||
|
|||||||
@@ -186,11 +186,10 @@ int main(int argc, char *argv[]) {
|
|||||||
g_signal_connect(proxy4, "notify::volume", G_CALLBACK(notify_volume_callback),
|
g_signal_connect(proxy4, "notify::volume", G_CALLBACK(notify_volume_callback),
|
||||||
"ShairportSync.AdvancedRemoteControl");
|
"ShairportSync.AdvancedRemoteControl");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
g_print("Starting test...\n");
|
g_print("Starting test...\n");
|
||||||
|
|
||||||
g_print("Using the RemoteControl interface, play for five seconds, pause for five seconds and then resume play...\n");
|
g_print("Using the RemoteControl interface, play for five seconds, pause for five seconds and "
|
||||||
|
"then resume play...\n");
|
||||||
g_print("Play...\n");
|
g_print("Play...\n");
|
||||||
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
|
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
@@ -200,24 +199,31 @@ int main(int argc, char *argv[]) {
|
|||||||
g_print("Play...\n");
|
g_print("Play...\n");
|
||||||
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
|
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
g_print("Using the RemoteControl interface, set AirPlay Volume (range -30 to 0) to -30, -20, -10, 0 and -15 for five seconds each...\n");
|
g_print("Using the RemoteControl interface, set AirPlay Volume (range -30 to 0) to -30, -20, "
|
||||||
|
"-10, 0 and -15 for five seconds each...\n");
|
||||||
g_print("Set AirPlay Volume (range -30 to 0) to -30\n");
|
g_print("Set AirPlay Volume (range -30 to 0) to -30\n");
|
||||||
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -30, NULL, NULL, 0);
|
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -30,
|
||||||
|
NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
g_print("Set AirPlay Volume (range -30 to 0) to -20\n");
|
g_print("Set AirPlay Volume (range -30 to 0) to -20\n");
|
||||||
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -20, NULL, NULL, 0);
|
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -20,
|
||||||
|
NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
g_print("Set AirPlay Volume (range -30 to 0) to -10\n");
|
g_print("Set AirPlay Volume (range -30 to 0) to -10\n");
|
||||||
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -10, NULL, NULL, 0);
|
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -10,
|
||||||
|
NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
g_print("Set AirPlay Volume (range -30 to 0) to -0\n");
|
g_print("Set AirPlay Volume (range -30 to 0) to -0\n");
|
||||||
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), 0, NULL, NULL, 0);
|
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), 0,
|
||||||
|
NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
g_print("Set AirPlay Volume (range -30 to 0) to -15\n");
|
g_print("Set AirPlay Volume (range -30 to 0) to -15\n");
|
||||||
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -15, NULL, NULL, 0);
|
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -15,
|
||||||
|
NULL, NULL, 0);
|
||||||
sleep(5);
|
sleep(5);
|
||||||
|
|
||||||
g_print("Using the AdvancedRemoteControl interface, set Volume to 20%%, 100%%, 40%% and 60%% for five seconds each...\n");
|
g_print("Using the AdvancedRemoteControl interface, set Volume to 20%%, 100%%, 40%% and 60%% for "
|
||||||
|
"five seconds each...\n");
|
||||||
g_print("Set Volume to 20%%\n");
|
g_print("Set Volume to 20%%\n");
|
||||||
shairport_sync_advanced_remote_control_call_set_volume(
|
shairport_sync_advanced_remote_control_call_set_volume(
|
||||||
SHAIRPORT_SYNC_ADVANCED_REMOTE_CONTROL(proxy4), 20, NULL, NULL, 0);
|
SHAIRPORT_SYNC_ADVANCED_REMOTE_CONTROL(proxy4), 20, NULL, NULL, 0);
|
||||||
|
|||||||
+104
-143
@@ -145,7 +145,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
|||||||
int number_of_iterations = 0;
|
int number_of_iterations = 0;
|
||||||
uint64_t soxr_start_time = get_absolute_time_in_ns();
|
uint64_t soxr_start_time = get_absolute_time_in_ns();
|
||||||
uint64_t loop_until_time =
|
uint64_t loop_until_time =
|
||||||
(uint64_t)1500000000 + soxr_start_time; // loop for a second and a half, max -- no need to be able to cancel it, do _don't even try_!
|
(uint64_t)1500000000 + soxr_start_time; // loop for a second and a half, max -- no need to be
|
||||||
|
// able to cancel it, do _don't even try_!
|
||||||
while (get_absolute_time_in_ns() < loop_until_time) {
|
while (get_absolute_time_in_ns() < loop_until_time) {
|
||||||
|
|
||||||
number_of_iterations++;
|
number_of_iterations++;
|
||||||
@@ -184,8 +185,7 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
|||||||
NULL, NULL); // Default configuration.
|
NULL, NULL); // Default configuration.
|
||||||
}
|
}
|
||||||
|
|
||||||
double soxr_execution_time_ns =
|
double soxr_execution_time_ns = (get_absolute_time_in_ns() - soxr_start_time) * 1.0;
|
||||||
(get_absolute_time_in_ns() - soxr_start_time) * 1.0;
|
|
||||||
// free(outbuffer);
|
// free(outbuffer);
|
||||||
// free(inbuffer);
|
// free(inbuffer);
|
||||||
config.soxr_delay_index = (int)(0.9 + soxr_execution_time_ns / (number_of_iterations * 1000000));
|
config.soxr_delay_index = (int)(0.9 + soxr_execution_time_ns / (number_of_iterations * 1000000));
|
||||||
@@ -388,7 +388,8 @@ int parse_options(int argc, char **argv) {
|
|||||||
|
|
||||||
config.resyncthreshold = 1.0 * fResyncthreshold / 44100;
|
config.resyncthreshold = 1.0 * fResyncthreshold / 44100;
|
||||||
config.tolerance = 1.0 * fTolerance / 44100;
|
config.tolerance = 1.0 * fTolerance / 44100;
|
||||||
config.audio_backend_silent_lead_in_time_auto = 1; // start outputting silence as soon as packets start arriving
|
config.audio_backend_silent_lead_in_time_auto =
|
||||||
|
1; // start outputting silence as soon as packets start arriving
|
||||||
config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if
|
config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if
|
||||||
// nothing else comes in first.
|
// nothing else comes in first.
|
||||||
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
|
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
|
||||||
@@ -399,17 +400,23 @@ int parse_options(int argc, char **argv) {
|
|||||||
// automatically.
|
// automatically.
|
||||||
config.volume_range_hw_priority =
|
config.volume_range_hw_priority =
|
||||||
0; // if combining software and hardware volume control, give the software priority
|
0; // if combining software and hardware volume control, give the software priority
|
||||||
// i.e. when reducing volume, reduce the sw first before reducing the software.
|
// i.e. when reducing volume, reduce the sw first before reducing the software.
|
||||||
// this is because some hw mixers mute at the bottom of their range, and they don't always advertise
|
// this is because some hw mixers mute at the bottom of their range, and they don't always
|
||||||
// this fact
|
// advertise this fact
|
||||||
config.resend_control_first_check_time = 0.10; // wait this many seconds before requesting the resending of a missing packet
|
config.resend_control_first_check_time =
|
||||||
config.resend_control_check_interval_time = 0.25; // wait this many seconds before again requesting the resending of a missing packet
|
0.10; // wait this many seconds before requesting the resending of a missing packet
|
||||||
config.resend_control_last_check_time = 0.10; // give up if the packet is still missing this close to when it's needed
|
config.resend_control_check_interval_time =
|
||||||
config.missing_port_dacp_scan_interval_seconds = 2.0; // check at this interval if no DACP port number is known
|
0.25; // wait this many seconds before again requesting the resending of a missing packet
|
||||||
|
config.resend_control_last_check_time =
|
||||||
|
0.10; // give up if the packet is still missing this close to when it's needed
|
||||||
|
config.missing_port_dacp_scan_interval_seconds =
|
||||||
|
2.0; // check at this interval if no DACP port number is known
|
||||||
|
|
||||||
config.minimum_free_buffer_headroom = 125; // leave approximately one second's worth of buffers free after calculating the effective latency.
|
config.minimum_free_buffer_headroom = 125; // leave approximately one second's worth of buffers
|
||||||
// e.g. if we have 1024 buffers or 352 frames = 8.17 seconds and we have a nominal latency of 2.0 seconds
|
// free after calculating the effective latency.
|
||||||
// then we can add an offset of 5.17 seconds and still leave a second's worth of buffers for unexpected circumstances
|
// e.g. if we have 1024 buffers or 352 frames = 8.17 seconds and we have a nominal latency of 2.0
|
||||||
|
// seconds then we can add an offset of 5.17 seconds and still leave a second's worth of buffers
|
||||||
|
// for unexpected circumstances
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA_HUB
|
#ifdef CONFIG_METADATA_HUB
|
||||||
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
|
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
|
||||||
@@ -419,9 +426,10 @@ int parse_options(int argc, char **argv) {
|
|||||||
1; // number of seconds between DACP server scans when playing nothing
|
1; // number of seconds between DACP server scans when playing nothing
|
||||||
config.scan_max_bad_response_count =
|
config.scan_max_bad_response_count =
|
||||||
5; // number of successive bad results to ignore before giving up
|
5; // number of successive bad results to ignore before giving up
|
||||||
//config.scan_max_inactive_count =
|
// config.scan_max_inactive_count =
|
||||||
// (365 * 24 * 60 * 60) / config.scan_interval_when_inactive; // number of scans to do before stopping if
|
// (365 * 24 * 60 * 60) / config.scan_interval_when_inactive; // number of scans to do before
|
||||||
// not made active again (not used)
|
// stopping if
|
||||||
|
// not made active again (not used)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// config_setting_t *setting;
|
// config_setting_t *setting;
|
||||||
@@ -772,42 +780,38 @@ int parse_options(int argc, char **argv) {
|
|||||||
die("Invalid alac_decoder option choice \"%s\". It should be \"hammerton\" or \"apple\"");
|
die("Invalid alac_decoder option choice \"%s\". It should be \"hammerton\" or \"apple\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get the resend control settings. */
|
/* Get the resend control settings. */
|
||||||
if (config_lookup_float(config.cfg, "general.resend_control_first_check_time",
|
if (config_lookup_float(config.cfg, "general.resend_control_first_check_time", &dvalue)) {
|
||||||
&dvalue)) {
|
|
||||||
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
||||||
config.resend_control_first_check_time = dvalue;
|
config.resend_control_first_check_time = dvalue;
|
||||||
else
|
else
|
||||||
warn("Invalid general resend_control_first_check_time setting \"%f\". It should "
|
warn("Invalid general resend_control_first_check_time setting \"%f\". It should "
|
||||||
"be "
|
"be "
|
||||||
"between 0.0 and 3.0, "
|
"between 0.0 and 3.0, "
|
||||||
"inclusive. The setting remains at %f seconds.",
|
"inclusive. The setting remains at %f seconds.",
|
||||||
dvalue, config.resend_control_first_check_time);
|
dvalue, config.resend_control_first_check_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_lookup_float(config.cfg, "general.resend_control_check_interval_time",
|
if (config_lookup_float(config.cfg, "general.resend_control_check_interval_time", &dvalue)) {
|
||||||
&dvalue)) {
|
|
||||||
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
||||||
config.resend_control_check_interval_time = dvalue;
|
config.resend_control_check_interval_time = dvalue;
|
||||||
else
|
else
|
||||||
warn("Invalid general resend_control_check_interval_time setting \"%f\". It should "
|
warn("Invalid general resend_control_check_interval_time setting \"%f\". It should "
|
||||||
"be "
|
"be "
|
||||||
"between 0.0 and 3.0, "
|
"between 0.0 and 3.0, "
|
||||||
"inclusive. The setting remains at %f seconds.",
|
"inclusive. The setting remains at %f seconds.",
|
||||||
dvalue, config.resend_control_check_interval_time);
|
dvalue, config.resend_control_check_interval_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_lookup_float(config.cfg, "general.resend_control_last_check_time",
|
if (config_lookup_float(config.cfg, "general.resend_control_last_check_time", &dvalue)) {
|
||||||
&dvalue)) {
|
|
||||||
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
||||||
config.resend_control_last_check_time = dvalue;
|
config.resend_control_last_check_time = dvalue;
|
||||||
else
|
else
|
||||||
warn("Invalid general resend_control_last_check_time setting \"%f\". It should "
|
warn("Invalid general resend_control_last_check_time setting \"%f\". It should "
|
||||||
"be "
|
"be "
|
||||||
"between 0.0 and 3.0, "
|
"between 0.0 and 3.0, "
|
||||||
"inclusive. The setting remains at %f seconds.",
|
"inclusive. The setting remains at %f seconds.",
|
||||||
dvalue, config.resend_control_last_check_time);
|
dvalue, config.resend_control_last_check_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_lookup_float(config.cfg, "general.missing_port_dacp_scan_interval_seconds",
|
if (config_lookup_float(config.cfg, "general.missing_port_dacp_scan_interval_seconds",
|
||||||
@@ -816,50 +820,16 @@ int parse_options(int argc, char **argv) {
|
|||||||
config.missing_port_dacp_scan_interval_seconds = dvalue;
|
config.missing_port_dacp_scan_interval_seconds = dvalue;
|
||||||
else
|
else
|
||||||
warn("Invalid general missing_port_dacp_scan_interval_seconds setting \"%f\". It should "
|
warn("Invalid general missing_port_dacp_scan_interval_seconds setting \"%f\". It should "
|
||||||
"be "
|
"be "
|
||||||
"between 0.0 and 300.0, "
|
"between 0.0 and 300.0, "
|
||||||
"inclusive. The setting remains at %f seconds.",
|
"inclusive. The setting remains at %f seconds.",
|
||||||
dvalue, config.missing_port_dacp_scan_interval_seconds);
|
dvalue, config.missing_port_dacp_scan_interval_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the default latency. Deprecated! */
|
/* Get the default latency. Deprecated! */
|
||||||
if (config_lookup_int(config.cfg, "latencies.default", &value))
|
if (config_lookup_int(config.cfg, "latencies.default", &value))
|
||||||
config.userSuppliedLatency = value;
|
config.userSuppliedLatency = value;
|
||||||
|
|
||||||
/* Get the lead-in silence settings. */
|
|
||||||
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_initial_period",
|
|
||||||
&dvalue)) {
|
|
||||||
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
|
||||||
config.lead_in_silence_initial_period = dvalue;
|
|
||||||
else
|
|
||||||
warn("Invalid general audio_backend_silent_lead_in_initial_period setting \"%f\". It should "
|
|
||||||
"be "
|
|
||||||
"between 0.0 and 3.0, "
|
|
||||||
"inclusive. The setting remains at %f seconds.",
|
|
||||||
dvalue, config.lead_in_silence_initial_period);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_lookup_int(config.cfg, "general.audio_backend_silent_lead_in_minimum_adjustments_to_make", &value)) {
|
|
||||||
if (value < 0)
|
|
||||||
die("Invalid audio_backend_silent_lead_in_minimum_adjustments_to_make range \"%d\". It must be positive. The setting remains at %d.",
|
|
||||||
value, config.lead_in_silence_minimum_adjustments_to_make);
|
|
||||||
else
|
|
||||||
config.lead_in_silence_minimum_adjustments_to_make = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_adjustment_interval",
|
|
||||||
&dvalue)) {
|
|
||||||
if ((dvalue >= 0.0) && (dvalue <= 3.0))
|
|
||||||
config.lead_in_silence_adjustment_interval = dvalue;
|
|
||||||
else
|
|
||||||
warn("Invalid general audio_backend_silent_lead_in_adjustment_interval setting \"%f\". It should "
|
|
||||||
"be "
|
|
||||||
"between 0.0 and 3.0, "
|
|
||||||
"inclusive. The setting remains at %f seconds.",
|
|
||||||
dvalue, config.lead_in_silence_adjustment_interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA
|
#ifdef CONFIG_METADATA
|
||||||
/* Get the metadata setting. */
|
/* Get the metadata setting. */
|
||||||
config.metadata_enabled = 1; // if metadata support is included, then enable it by default
|
config.metadata_enabled = 1; // if metadata support is included, then enable it by default
|
||||||
@@ -1014,7 +984,8 @@ int parse_options(int argc, char **argv) {
|
|||||||
|
|
||||||
if (config_lookup_string(config.cfg, "dsp.convolution_ir_file", &str)) {
|
if (config_lookup_string(config.cfg, "dsp.convolution_ir_file", &str)) {
|
||||||
config.convolution_ir_file = strdup(str);
|
config.convolution_ir_file = strdup(str);
|
||||||
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
config.convolver_valid =
|
||||||
|
convolver_init(config.convolution_ir_file, config.convolution_max_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.convolution && config.convolution_ir_file == NULL) {
|
if (config.convolution && config.convolution_ir_file == NULL) {
|
||||||
@@ -1210,7 +1181,7 @@ int parse_options(int argc, char **argv) {
|
|||||||
|
|
||||||
poptFreeContext(optCon);
|
poptFreeContext(optCon);
|
||||||
|
|
||||||
// here, we are finally finished reading the options
|
// here, we are finally finished reading the options
|
||||||
|
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
if ((daemonisewith) && (daemonisewithout))
|
if ((daemonisewith) && (daemonisewithout))
|
||||||
@@ -1224,8 +1195,9 @@ int parse_options(int argc, char **argv) {
|
|||||||
#else
|
#else
|
||||||
/* Check if we are called with -d or --daemon or -j or justDaemoniseNoPIDFile options*/
|
/* Check if we are called with -d or --daemon or -j or justDaemoniseNoPIDFile options*/
|
||||||
if ((daemonisewith != 0) || (daemonisewithout != 0)) {
|
if ((daemonisewith != 0) || (daemonisewithout != 0)) {
|
||||||
fprintf(stderr, "%s was built without libdaemon, so does not support daemonisation using the "
|
fprintf(stderr,
|
||||||
"-d, --daemon, -j or --justDaemoniseNoPIDFile options\n",
|
"%s was built without libdaemon, so does not support daemonisation using the "
|
||||||
|
"-d, --daemon, -j or --justDaemoniseNoPIDFile options\n",
|
||||||
config.appName);
|
config.appName);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -1248,8 +1220,6 @@ int parse_options(int argc, char **argv) {
|
|||||||
char hostname[100];
|
char hostname[100];
|
||||||
gethostname(hostname, 100);
|
gethostname(hostname, 100);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
char *i0;
|
char *i0;
|
||||||
if (raw_service_name == NULL)
|
if (raw_service_name == NULL)
|
||||||
i0 = strdup("%H"); // this is the default it the Service Name wasn't specified
|
i0 = strdup("%H"); // this is the default it the Service Name wasn't specified
|
||||||
@@ -1320,25 +1290,24 @@ const char *pid_file_proc(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void exit_function() {
|
void exit_function() {
|
||||||
|
|
||||||
// the following is to ensure that if libdaemon has been included
|
// the following is to ensure that if libdaemon has been included
|
||||||
// that most of this code will be skipped when the parent process is exiting
|
// that most of this code will be skipped when the parent process is exiting
|
||||||
// exec
|
// exec
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
if (this_is_the_daemon_process) { //this is the daemon that is exiting
|
if (this_is_the_daemon_process) { // this is the daemon that is exiting
|
||||||
#endif
|
#endif
|
||||||
debug(1, "exit function called...");
|
debug(1, "exit function called...");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Actually, there is no terminate_mqtt() function.
|
Actually, there is no terminate_mqtt() function.
|
||||||
#ifdef CONFIG_MQTT
|
#ifdef CONFIG_MQTT
|
||||||
if (config.mqtt_enabled) {
|
if (config.mqtt_enabled) {
|
||||||
terminate_mqtt();
|
terminate_mqtt();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
||||||
|
|
||||||
@@ -1349,62 +1318,61 @@ Actually, there is no stop_mpris_service() function.
|
|||||||
#endif
|
#endif
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_DBUS_INTERFACE
|
#ifdef CONFIG_DBUS_INTERFACE
|
||||||
stop_dbus_service();
|
stop_dbus_service();
|
||||||
#endif
|
#endif
|
||||||
if (g_main_loop) {
|
if (g_main_loop) {
|
||||||
debug(2, "Stopping DBUS Loop Thread");
|
debug(2, "Stopping DBUS Loop Thread");
|
||||||
g_main_loop_quit(g_main_loop);
|
g_main_loop_quit(g_main_loop);
|
||||||
pthread_join(dbus_thread, NULL);
|
pthread_join(dbus_thread, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_DACP_CLIENT
|
#ifdef CONFIG_DACP_CLIENT
|
||||||
debug(2, "Stopping DACP Monitor");
|
debug(2, "Stopping DACP Monitor");
|
||||||
dacp_monitor_stop();
|
dacp_monitor_stop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA_HUB
|
#ifdef CONFIG_METADATA_HUB
|
||||||
debug(2, "Stopping metadata hub");
|
debug(2, "Stopping metadata hub");
|
||||||
metadata_hub_stop();
|
metadata_hub_stop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA
|
#ifdef CONFIG_METADATA
|
||||||
metadata_stop(); // close down the metadata pipe
|
metadata_stop(); // close down the metadata pipe
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
activity_monitor_stop(0);
|
activity_monitor_stop(0);
|
||||||
|
|
||||||
if ((config.output) && (config.output->deinit)) {
|
if ((config.output) && (config.output->deinit)) {
|
||||||
debug(2, "Deinitialise the audio backend.");
|
debug(2, "Deinitialise the audio backend.");
|
||||||
config.output->deinit();
|
config.output->deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SOXR
|
#ifdef CONFIG_SOXR
|
||||||
// be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
|
// be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
|
||||||
pthread_join(soxr_time_check_thread, NULL);
|
pthread_join(soxr_time_check_thread, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (conns)
|
||||||
|
free(conns); // make sure the connections have been deleted first
|
||||||
|
|
||||||
if (conns)
|
if (config.service_name)
|
||||||
free(conns); // make sure the connections have been deleted first
|
free(config.service_name);
|
||||||
|
|
||||||
if (config.service_name)
|
|
||||||
free(config.service_name);
|
|
||||||
|
|
||||||
#ifdef CONFIG_CONVOLUTION
|
#ifdef CONFIG_CONVOLUTION
|
||||||
if (config.convolution_ir_file)
|
if (config.convolution_ir_file)
|
||||||
free(config.convolution_ir_file);
|
free(config.convolution_ir_file);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (config.regtype)
|
if (config.regtype)
|
||||||
free(config.regtype);
|
free(config.regtype);
|
||||||
|
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
daemon_retval_send(0);
|
daemon_retval_send(0);
|
||||||
daemon_pid_file_remove();
|
daemon_pid_file_remove();
|
||||||
daemon_signal_done();
|
daemon_signal_done();
|
||||||
if (config.computed_piddir)
|
if (config.computed_piddir)
|
||||||
free(config.computed_piddir);
|
free(config.computed_piddir);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1421,7 +1389,8 @@ Actually, there is no stop_mpris_service() function.
|
|||||||
|
|
||||||
void handle_sigchld(__attribute__((unused)) int sig) {
|
void handle_sigchld(__attribute__((unused)) int sig) {
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
|
while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {
|
||||||
|
}
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1500,7 +1469,7 @@ int main(int argc, char **argv) {
|
|||||||
// config.userSuppliedLatency = 0; // zero means none supplied
|
// config.userSuppliedLatency = 0; // zero means none supplied
|
||||||
|
|
||||||
config.debugger_show_file_and_line =
|
config.debugger_show_file_and_line =
|
||||||
1; // by default, log the file and line of the originating message
|
1; // by default, log the file and line of the originating message
|
||||||
config.debugger_show_relative_time =
|
config.debugger_show_relative_time =
|
||||||
1; // by default, log the time back to the previous debug message
|
1; // by default, log the time back to the previous debug message
|
||||||
config.resyncthreshold = 0.05; // 50 ms
|
config.resyncthreshold = 0.05; // 50 ms
|
||||||
@@ -1532,9 +1501,6 @@ int main(int argc, char **argv) {
|
|||||||
config.output_rate_auto_requested = 1; // default auto select format
|
config.output_rate_auto_requested = 1; // default auto select format
|
||||||
config.decoders_supported =
|
config.decoders_supported =
|
||||||
1 << decoder_hammerton; // David Hammerton's decoder supported by default
|
1 << decoder_hammerton; // David Hammerton's decoder supported by default
|
||||||
config.lead_in_silence_initial_period = 0.5; // the size of the first block of silence to send to the DAC
|
|
||||||
config.lead_in_silence_minimum_adjustments_to_make = 3; // make at least this number of checks and timing adjustments
|
|
||||||
config.lead_in_silence_adjustment_interval = 0.025; // make checks and adjustments at this interval
|
|
||||||
#ifdef CONFIG_APPLE_ALAC
|
#ifdef CONFIG_APPLE_ALAC
|
||||||
config.decoders_supported += 1 << decoder_apple_alac;
|
config.decoders_supported += 1 << decoder_apple_alac;
|
||||||
config.use_apple_decoder = 1; // use the ALAC decoder by default if support has been included
|
config.use_apple_decoder = 1; // use the ALAC decoder by default if support has been included
|
||||||
@@ -1766,8 +1732,9 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
/* Print out options */
|
/* Print out options */
|
||||||
debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off");
|
debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off");
|
||||||
debug(1, "diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped "
|
debug(1,
|
||||||
"deliberately.",
|
"diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped "
|
||||||
|
"deliberately.",
|
||||||
config.diagnostic_drop_packet_fraction);
|
config.diagnostic_drop_packet_fraction);
|
||||||
debug(1, "statistics_requester status is %d.", config.statistics_requested);
|
debug(1, "statistics_requester status is %d.", config.statistics_requested);
|
||||||
#if CONFIG_LIBDAEMON
|
#if CONFIG_LIBDAEMON
|
||||||
@@ -1790,8 +1757,8 @@ int main(int argc, char **argv) {
|
|||||||
debug(1, "mdns backend \"%s\".", config.mdns_name);
|
debug(1, "mdns backend \"%s\".", config.mdns_name);
|
||||||
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
|
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
|
||||||
debug(1, "interpolation setting is \"%s\".",
|
debug(1, "interpolation setting is \"%s\".",
|
||||||
config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr"
|
config.packet_stuffing == ST_basic ? "basic"
|
||||||
: "auto");
|
: config.packet_stuffing == ST_soxr ? "soxr" : "auto");
|
||||||
debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold);
|
debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold);
|
||||||
debug(1, "resync time is %f seconds.", config.resyncthreshold);
|
debug(1, "resync time is %f seconds.", config.resyncthreshold);
|
||||||
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
|
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
|
||||||
@@ -1805,8 +1772,9 @@ int main(int argc, char **argv) {
|
|||||||
debug(1, "volume_max_db is not set");
|
debug(1, "volume_max_db is not set");
|
||||||
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.",
|
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.",
|
||||||
config.volume_range_db);
|
config.volume_range_db);
|
||||||
debug(1, "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used "
|
debug(1,
|
||||||
"first) is %d.",
|
"volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used "
|
||||||
|
"first) is %d.",
|
||||||
config.volume_range_hw_priority);
|
config.volume_range_hw_priority);
|
||||||
debug(1, "playback_mode is %d (0-stereo, 1-mono, 1-reverse_stereo, 2-both_left, 3-both_right).",
|
debug(1, "playback_mode is %d (0-stereo, 1-mono, 1-reverse_stereo, 2-both_left, 3-both_right).",
|
||||||
config.playback_mode);
|
config.playback_mode);
|
||||||
@@ -1828,15 +1796,8 @@ int main(int argc, char **argv) {
|
|||||||
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
if (config.audio_backend_silent_lead_in_time_auto == 1)
|
||||||
debug(1, "audio backend silence lead-in time is \"auto\".");
|
debug(1, "audio backend silence lead-in time is \"auto\".");
|
||||||
else
|
else
|
||||||
debug(1, "audio backend silence lead-in time is %f seconds.",
|
debug(1, "audio backend silence lead-in time is %f seconds.",
|
||||||
config.audio_backend_silent_lead_in_time);
|
config.audio_backend_silent_lead_in_time);
|
||||||
|
|
||||||
debug(1, "audio backend silent lead-in initial period is %f seconds.",
|
|
||||||
config.lead_in_silence_initial_period);
|
|
||||||
debug(1, "audio backend silent lead-in minimum adjustments to make is %d.", config.lead_in_silence_minimum_adjustments_to_make);
|
|
||||||
debug(1, "audio backend silent lead-in adjustment interval is %f seconds.",
|
|
||||||
config.lead_in_silence_adjustment_interval);
|
|
||||||
|
|
||||||
debug(1, "zeroconf regtype is \"%s\".", config.regtype);
|
debug(1, "zeroconf regtype is \"%s\".", config.regtype);
|
||||||
debug(1, "decoders_supported field is %d.", config.decoders_supported);
|
debug(1, "decoders_supported field is %d.", config.decoders_supported);
|
||||||
debug(1, "use_apple_decoder is %d.", config.use_apple_decoder);
|
debug(1, "use_apple_decoder is %d.", config.use_apple_decoder);
|
||||||
|
|||||||
+4
-3
@@ -1466,7 +1466,7 @@ int close_pipe(int s) {
|
|||||||
|
|
||||||
// main loop to receive, process and send out MDNS replies
|
// main loop to receive, process and send out MDNS replies
|
||||||
// also handles MDNS service announces
|
// also handles MDNS service announces
|
||||||
void* main_loop(struct mdnsd *svr) {
|
void *main_loop(struct mdnsd *svr) {
|
||||||
fd_set sockfd_set;
|
fd_set sockfd_set;
|
||||||
int max_fd = svr->sockfd;
|
int max_fd = svr->sockfd;
|
||||||
char notify_buf[2]; // buffer for reading of notify_pipe
|
char notify_buf[2]; // buffer for reading of notify_pipe
|
||||||
@@ -1724,7 +1724,7 @@ struct mdnsd *mdnsd_start() {
|
|||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
if (pthread_create(&tid, &attr, (void * (*)(void *))&main_loop, (void *)server) != 0) {
|
if (pthread_create(&tid, &attr, (void *(*)(void *)) & main_loop, (void *)server) != 0) {
|
||||||
pthread_mutex_destroy(&server->data_lock);
|
pthread_mutex_destroy(&server->data_lock);
|
||||||
free(server);
|
free(server);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -1737,7 +1737,8 @@ void mdnsd_stop(struct mdnsd *s) {
|
|||||||
assert(s != NULL);
|
assert(s != NULL);
|
||||||
|
|
||||||
struct timeval tv = {
|
struct timeval tv = {
|
||||||
.tv_sec = 0, .tv_usec = 500 * 1000,
|
.tv_sec = 0,
|
||||||
|
.tv_usec = 500 * 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
s->stop_flag = 1;
|
s->stop_flag = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user