From: Mike Brady Date: Mon, 21 Jan 2019 18:20:39 +0000 (+0000) Subject: add initial script-execution functionality to active mode. Make is possible to wait... X-Git-Tag: 3.3RC0~66^2~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e181f937f056374da0e5b9dd7e4dc07f9c262be;p=thirdparty%2Fshairport-sync.git add initial script-execution functionality to active mode. Make is possible to wait for start and stop scripts. --- diff --git a/activity_monitor.c b/activity_monitor.c index 3393e95d..aea75cd9 100644 --- a/activity_monitor.c +++ b/activity_monitor.c @@ -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); } diff --git a/common.h b/common.h index 505c426b..29ec460d 100644 --- 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; diff --git a/player.c b/player.c index 6548b381..0de214c4 100644 --- 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); diff --git a/shairport.c b/shairport.c index 382536ec..8d43629e 100644 --- a/shairport.c +++ b/shairport.c @@ -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; }