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) {
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;
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");
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);
}
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;
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;
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;
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)
#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);
// 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";
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;
}