From d6536a8e798c73c85fb5eb1e6c3f1d57e0dff4a7 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Fri, 30 Dec 2022 18:36:33 +0000 Subject: [PATCH] Call the unfixable error if the output device can't be opened or disappears. Clang-format. --- audio_alsa.c | 39 +++++++++++++++++++++++-------- audio_stdout.c | 3 +-- common.h | 6 +++-- dbus-service.c | 3 ++- metadata_hub.c | 3 ++- metadata_hub.h | 8 +++---- nqptp-shm-structures.h | 3 +-- player.c | 32 +++++++++++++------------ player.h | 3 +-- rtsp.c | 53 +++++++++++++++++++++--------------------- 10 files changed, 87 insertions(+), 66 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 9a37f3f6..3c8cebd0 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -444,13 +444,19 @@ static int actual_open_alsa_device(int do_auto_setup) { 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]; - strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); - die("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, - alsa_out_dev); + if (config.unfixable_error_reported == 0) { + config.unfixable_error_reported = 1; + char messageString[1024]; + messageString[0] = '\0'; + snprintf(messageString, sizeof(messageString), "output_device_error %d", -ret); + if (config.cmd_unfixable) { + command_execute(config.cmd_unfixable, messageString, 1); + } else { + die("an unrecoverable error, \"output_device_error %d\", has been " + "detected.", -ret); + } + } } alsa_handle_status = ret; frames_sent_break_occurred = 1; @@ -1795,10 +1801,23 @@ static int do_play(void *buf, int samples) { } } } else { - char errorstring[1024]; - strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); - debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret, - (char *)errorstring, samples); + + if (ret == -ENODEV) { + if (config.unfixable_error_reported == 0) { + config.unfixable_error_reported = 1; + if (config.cmd_unfixable) { + command_execute(config.cmd_unfixable, "output_device_error 19", 1); + } else { + die("an unrecoverable error, \"output_device_error 19\", has been " + "detected."); + } + } + } else { + char errorstring[1024]; + strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); + debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret, + (char *)errorstring, samples); + } } } } diff --git a/audio_stdout.c b/audio_stdout.c index 2a54f072..ef9df61d 100644 --- a/audio_stdout.c +++ b/audio_stdout.c @@ -39,11 +39,10 @@ static int fd = -1; static int warned = 0; - static void start(__attribute__((unused)) int sample_rate, __attribute__((unused)) int sample_format) { fd = STDOUT_FILENO; - warned = 0; + warned = 0; } static int play(void *buf, int samples, __attribute__((unused)) int sample_type, diff --git a/common.h b/common.h index 639c1c0f..66c02ca6 100644 --- a/common.h +++ b/common.h @@ -37,7 +37,8 @@ typedef enum { typedef enum { TOE_normal, TOE_emergency, - TOE_dbus // a request was made on a D-Bus interface (the native D-Bus or MPRIS interfaces)-- don't wait for the dbus thread to exit + TOE_dbus // a request was made on a D-Bus interface (the native D-Bus or MPRIS interfaces)-- don't + // wait for the dbus thread to exit } type_of_exit_type; #define sps_extra_code_output_stalled 32768 @@ -160,7 +161,7 @@ typedef struct { int mqtt_enable_remote; char *mqtt_empty_payload_substitute; #endif - uint8_t ap1_prefix[6]; + uint8_t ap1_prefix[6]; uint8_t hw_addr[8]; // only needs 6 but 8 is handy when converting this to a number int port; int udp_port_base; @@ -313,6 +314,7 @@ typedef struct { char *airplay_pi; // UUID in the Bonjour advertisement and the GETINFO Plist char *nqptp_shared_memory_interface_name; // client name for nqptp service #endif + int unfixable_error_reported; // only report once. } shairport_cfg; // accessors to config for multi-thread access diff --git a/dbus-service.c b/dbus-service.c index 4cfc8254..70792248 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -111,7 +111,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) th = shairport_sync_get_first_frame_position(shairportSyncSkeleton); if ((th == NULL) || (strcasecmp(th, argc->first_frame_position_string) != 0)) { // debug(1, "First frame position string should be changed"); - shairport_sync_set_first_frame_position(shairportSyncSkeleton, argc->first_frame_position_string); + shairport_sync_set_first_frame_position(shairportSyncSkeleton, + argc->first_frame_position_string); } } diff --git a/metadata_hub.c b/metadata_hub.c index e39752fe..796e0927 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -598,7 +598,8 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin if (string_update(&metadata_store.first_frame_position_string, &metadata_store.first_frame_position_string_changed, cs)) { changed = 1; - debug(2, "MH First Frame Position String set to: \"%s\"", metadata_store.first_frame_position_string); + debug(2, "MH First Frame Position String set to: \"%s\"", + metadata_store.first_frame_position_string); } free(cs); break; diff --git a/metadata_hub.h b/metadata_hub.h index 8235c6e4..94b3acaa 100644 --- a/metadata_hub.h +++ b/metadata_hub.h @@ -48,7 +48,7 @@ typedef struct metadata_bundle { char *server_ip; // IP number used by Shairport Sync int server_ip_changed; - + char *stream_type; // Realtime or Buffered int stream_type_changed; @@ -79,7 +79,7 @@ typedef struct metadata_bundle { char *cover_art_pathname; int cover_art_pathname_changed; - + uint64_t item_id; // seems to be a track ID -- see itemid in DACP.c int item_id_changed; int item_id_is_valid; @@ -89,11 +89,10 @@ typedef struct metadata_bundle { int item_composite_id_changed; int item_composite_id_is_valid; - int song_data_kind; int song_data_kind_changed; int song_data_kind_is_valid; - + char *track_name; int track_name_changed; @@ -139,7 +138,6 @@ typedef struct metadata_bundle { uint32_t songtime_in_milliseconds; int songtime_in_milliseconds_changed; int songtime_in_milliseconds_is_valid; - // end diff --git a/nqptp-shm-structures.h b/nqptp-shm-structures.h index 251d06c1..5982a730 100644 --- a/nqptp-shm-structures.h +++ b/nqptp-shm-structures.h @@ -50,8 +50,7 @@ // When the clock is inactive, it can stop running. This causes the offset to decrease. // NQPTP clock smoothing would treat this as a network delay, causing true sync to be lost. // To avoid this, when the clock goes from inactive to active, -// NQPTP resets clock smoothing to the new offset. - +// NQPTP resets clock smoothing to the new offset. #include #include diff --git a/player.c b/player.c index b920971c..0898feda 100644 --- a/player.c +++ b/player.c @@ -106,7 +106,7 @@ int64_t first_frame_early_bias = 8; // default buffer size // needs to be a power of 2 because of the way BUFIDX(seqno) works -//#define BUFFER_FRAMES 512 +// #define BUFFER_FRAMES 512 #define MAX_PACKET 2048 // DAC buffer occupancy stuff @@ -531,9 +531,9 @@ void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timesta (uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000); uint64_t resend_repeat_interval = (uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000); - uint64_t minimum_remaining_time = (uint64_t)( - (config.resend_control_last_check_time + config.audio_backend_buffer_desired_length) * - (uint64_t)1000000000); + uint64_t minimum_remaining_time = (uint64_t)((config.resend_control_last_check_time + + config.audio_backend_buffer_desired_length) * + (uint64_t)1000000000); uint64_t latency_time = (uint64_t)(conn->latency * (uint64_t)1000000000); latency_time = latency_time / (uint64_t)conn->input_rate; @@ -1271,14 +1271,13 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { } else { if (resp == sps_extra_code_output_stalled) { - if (conn->unfixable_error_reported == 0) { - conn->unfixable_error_reported = 1; + if (config.unfixable_error_reported == 0) { + config.unfixable_error_reported = 1; if (config.cmd_unfixable) { command_execute(config.cmd_unfixable, "output_device_stalled", 1); } else { die("an unrecoverable error, \"output_device_stalled\", has been " - "detected.", - conn->connection_number); + "detected."); } } } else { @@ -1838,7 +1837,8 @@ void player_thread_cleanup_handler(void *arg) { void *player_thread_func(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; #ifdef CONFIG_METADATA - uint64_t time_of_last_metadata_progress_update = 0; // the assignment is to stop a compiler warning... + uint64_t time_of_last_metadata_progress_update = + 0; // the assignment is to stop a compiler warning... #endif uint64_t previous_frames_played = 0; // initialised to avoid a "possibly uninitialised" warning uint64_t previous_raw_measurement_time = @@ -1982,15 +1982,16 @@ void *player_thread_func(void *arg) { debug(3, "Output frame bytes is %d.", conn->output_bytes_per_frame); - conn->dac_buffer_queue_minimum_length = (uint64_t)( - config.audio_backend_buffer_interpolation_threshold_in_seconds * config.output_rate); + conn->dac_buffer_queue_minimum_length = + (uint64_t)(config.audio_backend_buffer_interpolation_threshold_in_seconds * + config.output_rate); debug(3, "dac_buffer_queue_minimum_length is %" PRIu64 " frames.", conn->dac_buffer_queue_minimum_length); conn->session_corrections = 0; conn->connection_state_to_output = get_requested_connection_state_to_output(); // this is about half a minute -//#define trend_interval 3758 +// #define trend_interval 3758 // this is about 8 seconds #define trend_interval 1003 @@ -2629,8 +2630,8 @@ void *player_thread_func(void *arg) { } else { current_delay = 0; if ((resp == sps_extra_code_output_stalled) && - (conn->unfixable_error_reported == 0)) { - conn->unfixable_error_reported = 1; + (config.unfixable_error_reported == 0)) { + config.unfixable_error_reported = 1; if (config.cmd_unfixable) { warn("Connection %d: An unfixable error has been detected -- output device is " "stalled. Executing the " @@ -2780,7 +2781,8 @@ void *player_thread_func(void *arg) { send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1); #endif if (config.statistics_requested) - inform("Connection %d: Playback started at frame %" PRId64 " -- Classic AirPlay (\"AirPlay 1\").", + inform("Connection %d: Playback started at frame %" PRId64 + " -- Classic AirPlay (\"AirPlay 1\").", conn->connection_number, inframe->given_timestamp); #endif } diff --git a/player.h b/player.h index 1c18567a..14e352ff 100644 --- a/player.h +++ b/player.h @@ -168,8 +168,7 @@ typedef struct { volatile int stop; volatile int running; volatile uint64_t watchdog_bark_time; - volatile int watchdog_barks; // number of times the watchdog has timed out and done something - int unfixable_error_reported; // set when an unfixable error command has been executed. + volatile int watchdog_barks; // number of times the watchdog has timed out and done something uint64_t playstart; uint64_t connection_start_time; // the time the device is selected, which could be a long time diff --git a/rtsp.c b/rtsp.c index 02c6c975..ac3ce652 100644 --- a/rtsp.c +++ b/rtsp.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,6 @@ #include #include #include -#include #include @@ -665,8 +665,8 @@ void *player_watchdog_thread_code(void *arg) { conn->stop = 1; pthread_cancel(conn->thread); } else if (conn->watchdog_barks == 3) { - if ((config.cmd_unfixable) && (conn->unfixable_error_reported == 0)) { - conn->unfixable_error_reported = 1; + if ((config.cmd_unfixable) && (config.unfixable_error_reported == 0)) { + config.unfixable_error_reported = 1; command_execute(config.cmd_unfixable, "unable_to_cancel_play_session", 1); } else { die("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.", @@ -1223,10 +1223,10 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time uint64_t time_to_wait_to = get_absolute_time_in_ns(); ; time_to_wait_to = time_to_wait_to + wait_time; - + int flags = 1; if (setsockopt(conn->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags))) { - debug(1,"can't enable keepalive checking on the RTSP socket"); + debug(1, "can't enable keepalive checking on the RTSP socket"); } // remaining_time will be zero if wait_time is zero @@ -1356,7 +1356,7 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes // Note: the socket will be closed when the thread exits goto shutdown; } - + // An ETIMEDOUT error usually means keepalive has failed. if (nread < 0) { @@ -2070,7 +2070,7 @@ struct pairings { uint8_t public_key[32]; struct pairings *next; -} * pairings; +} *pairings; static struct pairings *pairing_find(const char *device_id) { for (struct pairings *pairing = pairings; pairing; pairing = pairing->next) { @@ -3165,7 +3165,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) conn->connection_number, get_category_string(conn->airplay_stream_category)); if (conn->airplay_stream_category == ptp_stream) { ptp_send_control_message_string("B"); // signify play is "B"eginning - + // get stream[0] plist_t stream0 = plist_array_get_item(streams, 0); @@ -5502,39 +5502,40 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) { if (getsockname(conn->fd, (struct sockaddr *)&conn->local, &size_of_reply) == 0) { // Thanks to https://holmeshe.me/network-essentials-setsockopt-SO_KEEPALIVE/ for this. - - // Turn on keepalive stuff -- wait for keepidle + (keepcnt * keepinttvl time) seconds before giving up - // An ETIMEOUT error is returned if the keepalive check fails + + // Turn on keepalive stuff -- wait for keepidle + (keepcnt * keepinttvl time) seconds + // before giving up An ETIMEOUT error is returned if the keepalive check fails int keepAliveIdleTime = 10; // wait this many seconds before checking for a dropped client - int keepAliveCount = 5; // check this many times - int keepAliveInterval = 1; // wait this many seconds between checks - + int keepAliveCount = 5; // check this many times + int keepAliveInterval = 1; // wait this many seconds between checks #if defined COMPILE_FOR_BSD || defined COMPILE_FOR_OSX - #define SOL_OPTION IPPROTO_TCP +#define SOL_OPTION IPPROTO_TCP #else - #define SOL_OPTION SOL_TCP +#define SOL_OPTION SOL_TCP #endif #ifdef COMPILE_FOR_OSX - #define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPALIVE +#define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPALIVE #else - #define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPIDLE +#define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPIDLE #endif - - if (setsockopt(conn->fd, SOL_OPTION, KEEP_ALIVE_OR_IDLE_OPTION, (void *)&keepAliveIdleTime, sizeof(keepAliveIdleTime))) { - debug(1,"can't set the keepidle wait time"); + if (setsockopt(conn->fd, SOL_OPTION, KEEP_ALIVE_OR_IDLE_OPTION, + (void *)&keepAliveIdleTime, sizeof(keepAliveIdleTime))) { + debug(1, "can't set the keepidle wait time"); } - if (setsockopt(conn->fd, SOL_OPTION, TCP_KEEPCNT, (void *)&keepAliveCount, sizeof(keepAliveCount))) { - debug(1,"can't set the keepidle missing count"); + if (setsockopt(conn->fd, SOL_OPTION, TCP_KEEPCNT, (void *)&keepAliveCount, + sizeof(keepAliveCount))) { + debug(1, "can't set the keepidle missing count"); } - if (setsockopt(conn->fd, SOL_OPTION , TCP_KEEPINTVL, (void *)&keepAliveInterval, sizeof(keepAliveInterval))) { - debug(1,"can't set the keepidle missing count interval"); + if (setsockopt(conn->fd, SOL_OPTION, TCP_KEEPINTVL, (void *)&keepAliveInterval, + sizeof(keepAliveInterval))) { + debug(1, "can't set the keepidle missing count interval"); }; - + // initialise the connection info void *client_addr = NULL, *self_addr = NULL; conn->connection_ip_family = conn->local.SAFAMILY;