From 129343c165bd56830988f5c6254ec1259fdda432 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 20 Jul 2022 18:04:42 +0100 Subject: [PATCH] Don't quit if the ALSA output device is or becomes busy and resume when it becomes available again. --- audio_alsa.c | 33 +++++++++++++++++++++------------ player.c | 6 ++++-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index effdc436..9673da5f 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1,7 +1,7 @@ /* * libalsa output driver. This file is part of Shairport. * Copyright (c) Muffinman, Skaman 2013 - * Copyright (c) Mike Brady 2014 -- 2021 + * Copyright (c) Mike Brady 2014 -- 2022 * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -131,6 +131,7 @@ yndk_type precision_delay_available_status = YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay snd_pcm_t *alsa_handle = NULL; +int alsa_handle_status = ENODEV; // if alsa_handle is NULL, this should say why with a unix error code static snd_pcm_hw_params_t *alsa_params = NULL; static snd_pcm_sw_params_t *alsa_swparams = NULL; static snd_ctl_t *ctl = NULL; @@ -416,8 +417,17 @@ int actual_open_alsa_device(int do_auto_setup) { audio_alsa.delay = NULL; ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); - if (ret < 0) { - if (ret == -ENOENT) { + if (ret == 0) { + if (alsa_handle_status == -EBUSY) + warn("The output device \"%s\" is no longer busy and will be used by Shairport Sync.", alsa_out_dev); + alsa_handle_status = ret; // all cool + } else { + alsa_handle = NULL; // to be sure to be sure + if (ret == -EBUSY) { + if (alsa_handle_status != -EBUSY) + warn("The output device \"%s\" is busy and can't be used by Shairport Sync at present.", alsa_out_dev); + debug(2, "the alsa output_device \"%s\" is busy.", alsa_out_dev); + } else if (ret == -ENOENT) { die("the alsa output_device \"%s\" can not be found.", alsa_out_dev); } else { char errorstring[1024]; @@ -425,6 +435,8 @@ int actual_open_alsa_device(int do_auto_setup) { die("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, alsa_out_dev); } + alsa_handle_status = ret; + frames_sent_break_occurred = 1; return ret; } @@ -1413,7 +1425,7 @@ static void start(__attribute__((unused)) int i_sample_rate, int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, yndk_type *using_update_timestamps) { - int ret = ENODEV; + int ret = alsa_handle_status; if (using_update_timestamps) *using_update_timestamps = YNDK_NO; @@ -1451,7 +1463,7 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, snd_pcm_sframes_t delay_temp = 0; if (using_update_timestamps) *using_update_timestamps = YNDK_DONT_KNOW; - int ret = ENODEV; + int ret = alsa_handle_status; snd_pcm_status_t *alsa_snd_pcm_status; snd_pcm_status_alloca(&alsa_snd_pcm_status); @@ -1601,8 +1613,7 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, } } else { - debug(1, "alsa_handle is NULL in precision_delay_and_status!"); - // already set to ret = ENODEV; + debug(2, "alsa_handle is NULL in precision_delay_and_status!"); } if (delay != NULL) *delay = delay_temp; @@ -1631,10 +1642,7 @@ int delay(long *the_delay) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); - if (alsa_handle == NULL) - ret = ENODEV; - else - ret = delay_and_status(&state, &my_delay, NULL); + ret = delay_and_status(&state, &my_delay, NULL); debug_mutex_unlock(&alsa_mutex, 0); pthread_cleanup_pop(0); @@ -1666,7 +1674,7 @@ int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); if (alsa_handle == NULL) { - ret = ENODEV; + ret = alsa_handle_status; } else { *raw_measurement_time = get_absolute_time_in_ns(); // this is not conditioned ("disciplined") by NTP @@ -1834,6 +1842,7 @@ int do_close() { if ((derr = snd_pcm_close(alsa_handle))) debug(1, "Error %d (\"%s\") closing the output device.", derr, snd_strerror(derr)); alsa_handle = NULL; + alsa_handle_status = ENODEV; // no device open } else { debug(1, "alsa: do_close() -- output device already closed."); } diff --git a/player.c b/player.c index b24395bb..1b24e77f 100644 --- a/player.c +++ b/player.c @@ -2544,8 +2544,10 @@ void *player_thread_func(void *arg) { "done.", conn->connection_number); } - } else - debug(1, "Delay error %d when checking running latency.", resp); + } else { + if (resp != -EBUSY) // delay errors can be reported if the device is (hopefully temporarily) busy + debug(1, "Delay error %d when checking running latency.", resp); + } } }