diff options
-rw-r--r-- | audio_alsa.c | 29 | ||||
-rw-r--r-- | common.c | 24 | ||||
-rw-r--r-- | common.h | 4 | ||||
-rw-r--r-- | player.c | 40 | ||||
-rw-r--r-- | player.h | 3 | ||||
-rw-r--r-- | rtsp.c | 11 |
6 files changed, 81 insertions, 30 deletions
diff --git a/audio_alsa.c b/audio_alsa.c index d1fe42a..71a4e7e 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -76,8 +76,8 @@ static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER; // for tracking how long the output device has stalled uint64_t stall_monitor_start_time; // zero if not initialised / not started / zeroed by flush -long stall_monitor_frame_count; // set to delay at start of time, incremented by any writes -int stall_monitor_error_notified; +long stall_monitor_frame_count; // set to delay at start of time, incremented by any writes +int stall_monitor_error_notified; static snd_output_t *output = NULL; static unsigned int desired_sample_rate; @@ -940,11 +940,10 @@ static void start(int i_sample_rate, int i_sample_format) { frame_index = 0; measurement_data_is_valid = 0; - + stall_monitor_start_time = 0; stall_monitor_frame_count = 0; stall_monitor_error_notified = 0; - } // assuming pthread cancellation is disabled @@ -1062,20 +1061,23 @@ int delay(long *the_delay) { pthread_setcancelstate(oldState, NULL); } debug(3, "Total playing time to get delay of %d frames: %8.2f us.", *the_delay, - (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); - + (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); + if ((reply == 0) && (*the_delay != 0)) { uint64_t stall_monitor_time_now = get_absolute_time_in_fp(); if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *the_delay)) { // hasn't outputted anything since the last call to delay() int64_t time_stalled = stall_monitor_time_now - stall_monitor_start_time; int64_t stall_monitor_error_threshold = 200000; // microseconds; - stall_monitor_error_threshold = (stall_monitor_error_threshold << 32) / 1000000; // now in fp form + stall_monitor_error_threshold = + (stall_monitor_error_threshold << 32) / 1000000; // now in fp form if (time_stalled > stall_monitor_error_threshold) { if (stall_monitor_error_notified == 0) { - debug(1, "alsa output device stalled -- no output in %8.2f us.",(1000000.0 * stall_monitor_error_threshold) / (uint64_t)0x100000000); + debug(1, "alsa output device stalled -- no output in %8.2f us.", + (1000000.0 * stall_monitor_error_threshold) / (uint64_t)0x100000000); stall_monitor_error_notified = 1; } + reply = sps_extra_errno_output_stalled; } } else { // is outputting stuff, so restart the monitoring here @@ -1084,7 +1086,7 @@ int delay(long *the_delay) { } } else { // if there is an error or the delay is zero (from which it is assumed there is no output) - stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush + stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes } return reply; @@ -1114,7 +1116,8 @@ static int play(void *buf, int samples) { pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1); overall_time_before = get_absolute_time_in_fp(); ret = actual_open_alsa_device(); - debug(3, "Opening time: %8.2f us.", (1000000.0 * (get_absolute_time_in_fp() - overall_time_before ) / (uint64_t)0x100000000)); + debug(3, "Opening time: %8.2f us.", + (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); if (ret == 0) { if (audio_alsa.volume) do_volume(set_volume); @@ -1231,8 +1234,8 @@ static int play(void *buf, int samples) { pthread_cleanup_pop(0); // release the mutex } pthread_setcancelstate(oldState, NULL); - debug(3, "Total playing time to write %d samples: %8.2f us.", - samples, (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); + debug(3, "Total playing time to write %d samples: %8.2f us.", samples, + (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); return ret; } @@ -1265,7 +1268,7 @@ static void flush(void) { pthread_cleanup_pop(0); // release the mutex pthread_setcancelstate(oldState, NULL); debug(3, "Total playing time to flush: %8.2f us.", - (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); + (1000000.0 * (get_absolute_time_in_fp() - overall_time_before) / (uint64_t)0x100000000)); } static void stop(void) { @@ -739,23 +739,31 @@ void command_start(void) { } } } -void command_execute(const char *command) { +void command_execute(const char *command, const char *extra_argument) { // this has a cancellation point if waiting is enabled if (command) { + char new_command_buffer[1024]; + char *full_command = (char *)command; + if (extra_argument != NULL) { + memset(new_command_buffer, 0, sizeof(new_command_buffer)); + snprintf(new_command_buffer, sizeof(new_command_buffer), "%s %s", command, extra_argument); + full_command = new_command_buffer; + } + /*Spawn a child to run the program.*/ pid_t pid = fork(); if (pid == 0) { /* child process */ int argC; char **argV; - if (poptParseArgvString(command, &argC, (const char ***)&argV) != + if (poptParseArgvString(full_command, &argC, (const char ***)&argV) != 0) // note that argV should be free()'d after use, but we expect this fork to exit // eventually. - debug(1, "Can't decipher command arguments in \"%s\".", command); + debug(1, "Can't decipher command arguments in \"%s\".", full_command); else { - // debug(1,"Executing command %s",command); + // debug(1,"Executing command %s",full_command); execv(argV[0], argV); - warn("Execution of command \"%s\" failed to start", command); - debug(1, "Error executing command \"%s\".", command); + warn("Execution of command \"%s\" failed to start", full_command); + debug(1, "Error executing command \"%s\".", full_command); exit(127); /* only if execv fails */ } } else { @@ -763,8 +771,8 @@ void command_execute(const char *command) { process to finish */ pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */ if (rc != pid) { - warn("Execution of command \"%s\" returned an error.", command); - debug(1, "Command \"%s\" finished with error %d", command, errno); + warn("Execution of command \"%s\" returned an error.", full_command); + debug(1, "Command \"%s\" finished with error %d", full_command, errno); } } // debug(1,"Continue after on-unfixable command"); @@ -30,6 +30,8 @@ enum dbus_session_type { } dbt_type; #endif +#define sps_extra_errno_output_stalled 32768 + enum endian_type { SS_LITTLE_ENDIAN = 0, SS_PDP_ENDIAN, @@ -300,7 +302,7 @@ int config_set_lookup_bool(config_t *cfg, char *where, int *dst); void command_start(void); void command_stop(void); -void command_execute(const char *command); +void command_execute(const char *command, const char *extra_argument); void command_set_volume(double volume); int mkpath(const char *path, mode_t mode); @@ -1075,7 +1075,23 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { 1; // even if we haven't sent silence because it's zero frames long... } } else { - // debug(1, "Unable to get dac delay."); + if ((resp == sps_extra_errno_output_stalled) && + (conn->unfixable_error_reported == 0)) { + conn->unfixable_error_reported = 1; + if (config.cmd_unfixable) { + warn("Connection %d: An unfixable error has been detected -- output device " + "is stalled. Executing the " + "\"run_this_if_an_unfixable_error_is_detected\" command.", + conn->connection_number); + command_execute(config.cmd_unfixable, "output_device_stalled"); + } else { + warn("Connection %d: An unfixable error has been detected -- output device " + "is stalled. \"No " + "run_this_if_an_unfixable_error_is_detected\" command provided -- " + "nothing done.", + conn->connection_number); + } + } } } else { // no delay function on back end -- just send the prefiller silence @@ -1955,7 +1971,24 @@ void *player_thread_func(void *arg) { minimum_dac_queue_size = current_delay; // update for display later } } else { - debug(2, "Delay error %d when checking running latency.", resp); + if ((resp == sps_extra_errno_output_stalled) && + (conn->unfixable_error_reported == 0)) { + conn->unfixable_error_reported = 1; + if (config.cmd_unfixable) { + warn("Connection %d: An unfixable error has been detected -- output device is " + "stalled. Executing the " + "\"run_this_if_an_unfixable_error_is_detected\" command.", + conn->connection_number); + command_execute(config.cmd_unfixable, "output_device_stalled"); + } else { + warn("Connection %d: An unfixable error has been detected -- output device is " + "stalled. \"No " + "run_this_if_an_unfixable_error_is_detected\" command provided -- nothing " + "done.", + conn->connection_number); + } + } else + debug(2, "Delay error %d when checking running latency.", resp); } } @@ -2029,7 +2062,8 @@ void *player_thread_func(void *arg) { debug(2, "Large positive sync error: %" PRId64 ".", sync_error); int64_t local_frames_to_drop = sync_error / conn->output_sample_ratio; uint32_t frames_to_drop_sized = local_frames_to_drop; - do_flush(inframe->given_timestamp + frames_to_drop_sized, conn); // this will reset_input_flow_metrics anyway + do_flush(inframe->given_timestamp + frames_to_drop_sized, + conn); // this will reset_input_flow_metrics anyway } else if ((sync_error < 0) && ((-sync_error) > filler_length)) { debug(2, "Large negative sync error: %" PRId64 " with should_be_frame_32 of %" PRIu32 @@ -85,7 +85,8 @@ 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 + 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. time_t playstart; pthread_t thread, timer_requester, rtp_audio_thread, rtp_control_thread, rtp_timing_thread, @@ -291,13 +291,16 @@ void *player_watchdog_thread_code(void *arg) { conn->stop = 1; pthread_cancel(conn->thread); } else if (conn->watchdog_barks == 3) { - if (config.cmd_unfixable) { - warn("Connection %d: An unfixable error has been detected. Executing the " + if ((config.cmd_unfixable) && (conn->unfixable_error_reported == 0)) { + conn->unfixable_error_reported = 1; + warn("Connection %d: An unfixable error has been detected -- can't stop a play " + "session. Executing the " "\"run_this_if_an_unfixable_error_is_detected\" command.", conn->connection_number); - command_execute(config.cmd_unfixable); + command_execute(config.cmd_unfixable, "unable_to_cancel_play_session"); } else { - warn("Connection %d: An unfixable error has been detected. \"No " + warn("Connection %d: An unfixable error has been detected -- can't stop a play " + "session. \"No " "run_this_if_an_unfixable_error_is_detected\" command provided -- nothing done.", conn->connection_number); } |