Connect up the stall response to the external command calling -- add a single argument to the unfixable error call.
This commit is contained in:
+16
-13
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user