]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
add initial script-execution functionality to active mode. Make is possible to wait...
authorMike Brady <mikebrady@eircom.net>
Mon, 21 Jan 2019 18:20:39 +0000 (18:20 +0000)
committerMike Brady <mikebrady@eircom.net>
Mon, 21 Jan 2019 18:20:39 +0000 (18:20 +0000)
activity_monitor.c
common.h
player.c
shairport.c

index 3393e95d552ac91db6d8e9a15abe5f702f9ee28f..aea75cd9a32c940feb8539772836f6d16dc49f9d 100644 (file)
@@ -48,23 +48,44 @@ pthread_t activity_monitor_thread;
 pthread_mutex_t activity_monitor_mutex;
 pthread_cond_t activity_monitor_cv;
 
-void activity_monitor_signify_activity(int active) {
-  pthread_mutex_lock(&activity_monitor_mutex);
-  player_state = active == 0 ? ps_inactive : ps_active;
-  pthread_cond_signal(&activity_monitor_cv);
-  pthread_mutex_unlock(&activity_monitor_mutex);
+void going_active(int block) {
+  debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" : "out");
+  if (config.cmd_active_start)
+    command_execute(config.cmd_active_start, "", block);
 }
 
-void going_active() {
-  pthread_mutex_unlock(&activity_monitor_mutex);  
-  debug(1, "activity_monitor: state transitioning to \"active\"");
-  pthread_mutex_lock(&activity_monitor_mutex);
+void going_inactive(int block) {
+   debug(1, "activity_monitor: state transitioning to \"inactive\" with%s blocking", block ? "" : "out");
+  if (config.cmd_active_stop)
+    command_execute(config.cmd_active_stop, "", block);
 }
 
-void going_inactive() {
-  pthread_mutex_unlock(&activity_monitor_mutex);  
-  debug(1, "activity_monitor: state transitioning to \"inactive\"");
+void activity_monitor_signify_activity(int active) {
   pthread_mutex_lock(&activity_monitor_mutex);
+  player_state = active == 0 ? ps_inactive : ps_active;
+  // Now, although we could simply let the state machine in the activity monitor thread
+  // look after eveything, we will change state here in two situations:
+  // 1. If the state machine is am_inactive and the player is ps_active
+  // we will change the state to am_active and execute the going_active() function.
+  // 2. If the state machine is am_active and the player is ps_inactive and
+  // the activity_idle_timeout is 0, then we will change the state to am_inactive and
+  // execute the going_inactive() function.
+  //
+  // The reason for all this is that we might want to perform the attached scripts
+  // and wait for them to complete before continuing. If they were perfomed in the
+  // activity monitor thread, then we coldn't wait for them to complete.
+  
+  // Thus, the only time the thread will execute a going_... function is when a non-zero
+  // timeout actually matures.
+  
+  if ((state == am_inactive) && (player_state == ps_active)) {
+    going_active(config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
+  } else if ((state == am_active) && (player_state == ps_inactive) && (config.active_mode_timeout == 0.0)) {
+    going_inactive(config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
+  }
+  
+  pthread_cond_signal(&activity_monitor_cv); 
+  pthread_mutex_unlock(&activity_monitor_mutex);
 }
 
 void activity_thread_cleanup_handler(__attribute__((unused)) void *arg) {
@@ -110,19 +131,19 @@ void *activity_monitor_thread_code(void *arg) {
        while (player_state != ps_active)
           pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);               
         state = am_active;
-        going_active();                
+        // going_active(); // this is done in activity_monitor_signify_activity         
       break;
       case am_active:
        debug(1,"am_state: am_active");
         while (player_state != ps_inactive)
           pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
-        if (config.active_timeout == 0.0) {
+        if (config.active_mode_timeout == 0.0) {
           state = am_inactive;
-          going_inactive();
+          // going_inactive(); // this is done in activity_monitor_signify_activity
         } else {
           state = am_timing_out;
 
-          uint64_t time_to_wait_for_wakeup_fp = (uint64_t)(config.active_timeout *1000000); // resolution of microseconds
+          uint64_t time_to_wait_for_wakeup_fp = (uint64_t)(config.active_mode_timeout *1000000); // resolution of microseconds
           time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp << 32;
           time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp / 1000000;
           
@@ -156,7 +177,9 @@ void *activity_monitor_thread_code(void *arg) {
           state = am_active; // player has gone active -- do nothing, because it's still active
         else if (rc == ETIMEDOUT) {
             state = am_inactive;
-            going_inactive();        
+            pthread_mutex_unlock(&activity_monitor_mutex);
+            going_inactive(0); // don't wait for completion -- it makes no sense   
+            pthread_mutex_lock(&activity_monitor_mutex);
         }  else {
             // activity monitor was woken up in the state am_timing_out, but not by a timeout and player is not in ps_active state
             debug(1, "activity monitor was woken up in the state am_timing_out, but didn't change state");
@@ -175,7 +198,6 @@ void *activity_monitor_thread_code(void *arg) {
 
 void activity_monitor_start() {
   debug(1,"activity_monitor_start");
-  config.active_timeout = 5.5; // hack
   pthread_create(&activity_monitor_thread, NULL, activity_monitor_thread_code, NULL);
 }
 
index 505c426b442fdc3e49a32833839135947b316137..29ec460d81caaae5f579157bcd823987f9ce856a 100644 (file)
--- a/common.h
+++ b/common.h
@@ -152,6 +152,7 @@ typedef struct {
   int statistics_requested, use_negotiated_latencies;
   enum playback_mode_type playback_mode;
   char *cmd_start, *cmd_stop, *cmd_set_volume, *cmd_unfixable;
+  char *cmd_active_start, *cmd_active_stop;
   int cmd_blocking, cmd_start_returns_output;
   double tolerance; // allow this much drift before attempting to correct it
   enum stuffing_type packet_stuffing;
@@ -177,7 +178,7 @@ typedef struct {
   double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any
                                        // fixed latency there might be in the audio path
   double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
-  double active_timeout; // the amount of time from when play ends to when the system does into the "inactive".
+  double active_mode_timeout; // the amount of time from when play ends to when the system leaves into the "active" mode.
   uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
                             // native range.
   enum sps_format_t output_format;
index 6548b381458fea6c43cbf353054481cef59a4994..0de214c4d17cf00f5426935484f0d0317a788470 100644 (file)
--- a/player.c
+++ b/player.c
@@ -1488,12 +1488,10 @@ void player_thread_cleanup_handler(void *arg) {
   clear_reference_timestamp(conn);
   conn->rtp_running = 0;
   pthread_setcancelstate(oldState, NULL);
-  activity_monitor_signify_activity(0); // inactive
 }
 
 void *player_thread_func(void *arg) {
-    activity_monitor_signify_activity(1); // active
-  rtsp_conn_info *conn = (rtsp_conn_info *)arg;
+   rtsp_conn_info *conn = (rtsp_conn_info *)arg;
   // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg);
   conn->packet_count = 0;
   conn->packet_count_since_flush = 0;
@@ -2802,6 +2800,7 @@ int player_play(rtsp_conn_info *conn) {
   if (config.buffer_start_fill > BUFFER_FRAMES)
     die("specified buffer starting fill %d > buffer size %d", config.buffer_start_fill,
         BUFFER_FRAMES);
+  activity_monitor_signify_activity(1); // active, and should be before play's command hook, command_start()
   command_start();
   pthread_t *pt = malloc(sizeof(pthread_t));
   if (pt == NULL)
@@ -2850,6 +2849,7 @@ int player_stop(rtsp_conn_info *conn) {
 #endif
     // debuglev = dl;
     command_stop();
+    activity_monitor_signify_activity(0); // inactive, and should be after command_stop()
     return 0;
   } else {
     debug(3, "Connection %d: player thread already deleted.", conn->connection_number);
index 382536ec8653611d900bfdf0dfe3d90a86e49187..8d43629e31f6d90c83f1f9cb674779ff1d1b0c46 100644 (file)
@@ -331,6 +331,7 @@ int parse_options(int argc, char **argv) {
                                  // nothing else comes in first.
   config.fixedLatencyOffset = 11025; // this sounds like it works properly.
   config.diagnostic_drop_packet_fraction = 0.0;
+  config.active_mode_timeout = 10.0;
 
 #ifdef CONFIG_METADATA_HUB
   config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
@@ -722,7 +723,24 @@ int parse_options(int argc, char **argv) {
         config.cmd_stop = (char *)str;
       }
 
-      if (config_lookup_string(config.cfg,
+      if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_entering_active_mode", &str)) {
+        config.cmd_active_start = (char *)str;
+      }
+
+      if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_exiting_active_mode", &str)) {
+        config.cmd_active_stop = (char *)str;
+      }
+
+      if (config_lookup_float(config.cfg, "sessioncontrol.active_mode_timeout", &dvalue)) {
+        if (dvalue < 0.0)
+          warn("Invalid value \"%f\" for sessioncontrol.active_mode_timeout. It must be positive. The default of %f will be used instead.",
+              dvalue, config.active_mode_timeout);
+        else
+          config.active_mode_timeout = dvalue;
+      }
+
+
+    if (config_lookup_string(config.cfg,
                                "sessioncontrol.run_this_if_an_unfixable_error_is_detected", &str)) {
         config.cmd_unfixable = (char *)str;
       }