]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Connect up the stall response to the external command calling -- add a single argumen...
authorMike Brady <mikebrady@eircom.net>
Mon, 10 Dec 2018 22:28:52 +0000 (22:28 +0000)
committerMike Brady <mikebrady@eircom.net>
Mon, 10 Dec 2018 22:28:52 +0000 (22:28 +0000)
audio_alsa.c
common.c
common.h
player.c
player.h
rtsp.c

index d1fe42a1dcc160516ccf3267c271af476c0113f3..71a4e7eec1426f9dc217c520f1fe54e6fc6dc6cf 100644 (file)
@@ -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) {
index 7cadf231ed6540696de549273fcd39977afe2ecb..053abbe3f1130ab182ff777a178159ae2bf08c8c 100644 (file)
--- a/common.c
+++ b/common.c
@@ -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");
index abf19e71c6c74d53a15b36ff2d60272c76c16dee..9e2d88f3adcdaf0ef17c171cca781ad21eeb74a7 100644 (file)
--- a/common.h
+++ b/common.h
@@ -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);
index 5d60687bcc416c8fd23b9053a6d2fd25f8efd7bb..ec7d2adc57a143809bfd5937d192be7d2daa1efa 100644 (file)
--- a/player.c
+++ b/player.c
@@ -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
index 9346ef2b8e00eb634ededeac58d47c7938016a6f..413aa8da015db346b7e7a453d00065d6343c5311 100644 (file)
--- a/player.h
+++ b/player.h
@@ -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,
diff --git a/rtsp.c b/rtsp.c
index 7921f70ba74c4ae185549898e2d8ff7fee26655c..04369ee17c5fa6c089311b868b3e5bc0592c2ec8 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -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);
             }