Merge pull request #545 from ccutrer/dynamic_output_device

Read `stdout` from the on-start command to choose ALSA output device

Thanks for this. It looks well-implemented, but it also is very special-purpose. When a more general solution comes along that provides this functionality, I hope you won't mind if this code is removed...
This commit is contained in:
Mike Brady
2017-06-25 15:01:12 +01:00
committed by GitHub
4 changed files with 55 additions and 3 deletions
+4
View 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");
+39 -2
View File
@@ -80,6 +80,10 @@
#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");
}
+1 -1
View File
@@ -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;
+11
View 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)
@@ -1277,6 +1287,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);