]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
read stdout from the on-start command to choose ALSA output device 545/head
authorCody Cutrer <cody@instructure.com>
Sun, 25 Jun 2017 01:27:37 +0000 (19:27 -0600)
committerCody Cutrer <cody@instructure.com>
Sun, 25 Jun 2017 01:46:03 +0000 (19:46 -0600)
refs GH-452

audio_alsa.c
common.c
common.h
shairport.c

index 30183505418a0e718054a5f846a93621d1b96339..e0dae55838a3ba4ac45841733e8204dd127418ca 100644 (file)
@@ -109,6 +109,10 @@ static void help(void) {
          "    *) default option\n");
 }
 
+void set_alsa_out_dev(char *dev) {
+  alsa_out_dev = dev;
+}
+
 int open_mixer() {
   if (hardware_mixer) {
     debug(2, "Open Mixer");
index c13f1857449e5cf484306b805243940d68b69cad..fedff7274efb1af78ff21d0c7b319a790e257c90 100644 (file)
--- a/common.c
+++ b/common.c
 
 #include <libdaemon/dlog.h>
 
+#ifdef CONFIG_ALSA
+void set_alsa_out_dev(char *);
+#endif
+
 // true if Shairport Sync is supposed to be sending output to the output device, false otherwise
 
 static volatile int requested_connection_state_to_output = 1;
@@ -504,11 +508,30 @@ void command_set_volume(double volume) {
 
 void command_start(void) {
   if (config.cmd_start) {
+    pid_t pid;
+    int pipes[2];
+
+    if (config.cmd_start_returns_output && pipe(pipes) != 0) {
+      warn("Unable to allocate pipe for popen of start command.");
+      debug(1, "pipe finished with error %d", errno);
+      return;
+    }
     /*Spawn a child to run the program.*/
-    pid_t pid = fork();
+    pid = fork();
     if (pid == 0) { /* child process */
       int argC;
       char **argV;
+
+      if (config.cmd_start_returns_output) {
+        close(pipes[0]);
+        if (dup2(pipes[1], 1) < 0) {
+          warn("Unable to reopen pipe as stdout for popen of start command");
+          debug(1, "dup2 finished with error %d", errno);
+          close(pipes[1]);
+          return;
+        }
+      }
+
       // debug(1,"on-start command found.");
       if (poptParseArgvString(config.cmd_start, &argC, (const char ***)&argV) !=
           0) // note that argV should be free()'d after use, but we expect this fork to exit
@@ -522,13 +545,27 @@ void command_start(void) {
         exit(127); /* only if execv fails */
       }
     } else {
-      if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for
+      if (config.cmd_blocking || config.cmd_start_returns_output) { /* pid!=0 means parent process and if blocking is true, wait for
                                     process to finish */
         pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */
         if (rc != pid) {
           warn("Execution of on-start command returned an error.");
           debug(1, "on-start command %s finished with error %d", config.cmd_start, errno);
         }
+        if (config.cmd_start_returns_output) {
+          static char buffer[256];
+          int len;
+          close(pipes[1]);
+          len = read(pipes[0], buffer, 255);
+          close(pipes[0]);
+          buffer[len] = '\0';
+          if (buffer[len - 1] == '\n')
+            buffer[len - 1] = '\0'; // strip trailing newlines
+          debug(1, "received '%s' as the device to use from the on-start command", buffer);
+#ifdef CONFIG_ALSA
+          set_alsa_out_dev(buffer);
+#endif
+        }
       }
       // debug(1,"Continue after on-start command");
     }
index f425c15e00722552790690c5f04a1302e9a14eac..2cfeaba7d53f10f131a156c0606b939c15fa4303 100644 (file)
--- a/common.h
+++ b/common.h
@@ -116,7 +116,7 @@ typedef struct {
   int statistics_requested, use_negotiated_latencies;
   enum playback_mode_type playback_mode;
   char *cmd_start, *cmd_stop, *cmd_set_volume;
-  int cmd_blocking;
+  int cmd_blocking, cmd_start_returns_output;
   double tolerance; // allow this much drift before attempting to correct it
   enum stuffing_type packet_stuffing;
   int decoders_supported;
index 664ed35758f2ae33a525b11191161315cb072c2f..b0b316512bfb12a5907de5d1fcdb2539436ebb64 100644 (file)
@@ -694,6 +694,16 @@ int parse_options(int argc, char **argv) {
               "\"yes\" or \"no\"");
       }
 
+      if (config_lookup_string(config.cfg, "sessioncontrol.before_play_begins_returns_output", &str)) {
+        if (strcasecmp(str, "no") == 0)
+          config.cmd_start_returns_output = 0;
+        else if (strcasecmp(str, "yes") == 0)
+          config.cmd_start_returns_output = 1;
+        else
+          die("Invalid session control before_play_begins_returns_output option choice \"%s\". It should be "
+              "\"yes\" or \"no\"");
+      }
+
       if (config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) {
         config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0
         if (strcasecmp(str, "no") == 0)
@@ -1276,6 +1286,7 @@ int main(int argc, char **argv) {
   debug(1, "on-start action is \"%s\".", config.cmd_start);
   debug(1, "on-stop action is \"%s\".", config.cmd_stop);
   debug(1, "wait-cmd status is %d.", config.cmd_blocking);
+  debug(1, "on-start returns output is %d.", config.cmd_start_returns_output);
   debug(1, "mdns backend \"%s\".", config.mdns_name);
   debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
   debug(2, "AirPlayLatency is %d.", config.AirPlayLatency);