From 936103f462bef0af2c6fb8eb3495b8f3aa1c9186 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 12 Feb 2019 23:43:54 +0100 Subject: [PATCH] [outputs] Move device_add/rm to outputs and get rid of advertised flag --- src/outputs.c | 196 ++++++++++++++++++++++++++++--- src/outputs.h | 8 +- src/outputs/alsa.c | 1 - src/outputs/cast.c | 2 - src/outputs/dummy.c | 1 - src/outputs/fifo.c | 1 - src/outputs/pulse.c | 1 - src/outputs/raop.c | 2 - src/player.c | 279 +++++++------------------------------------- 9 files changed, 226 insertions(+), 265 deletions(-) diff --git a/src/outputs.c b/src/outputs.c index 342bfa05..34080528 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -35,6 +35,7 @@ #include "misc.h" #include "transcode.h" #include "listener.h" +#include "db.h" #include "player.h" //TODO remove me when player_pmap is removed again #include "outputs.h" @@ -122,6 +123,22 @@ callback_remove(struct output_device *device) } } +static void +callback_remove_all(enum output_types type) +{ + struct output_device *device; + + for (device = output_device_list; device; device = device->next) + { + if (type != device->type) + continue; + + outputs_device_cb_set(device, NULL); + + callback_remove(device); + } +} + static int callback_add(struct output_device *device, output_status_cb cb) { @@ -162,22 +179,6 @@ callback_add(struct output_device *device, output_status_cb cb) return callback_id; }; -static void -callback_remove_all(enum output_types type) -{ - struct output_device *device; - - for (device = output_device_list; device; device = device->next) - { - if (type != device->type) - continue; - - outputs_device_cb_set(device, NULL); - - callback_remove(device); - } -} - static void deferred_cb(int fd, short what, void *arg) { @@ -213,6 +214,15 @@ deferred_cb(int fd, short what, void *arg) } } +static void +device_stop_cb(struct output_device *device, enum output_device_state status) +{ + if (status == OUTPUT_STATE_FAILED) + DPRINTF(E_WARN, L_PLAYER, "Failed to stop device\n"); + else + DPRINTF(E_INFO, L_PLAYER, "Device stopped properly\n"); +} + static enum transcode_profile quality_to_xcode(struct media_quality *quality) { @@ -338,6 +348,41 @@ buffer_drain(struct output_buffer *obuf) } } +static void +device_list_sort(void) +{ + struct output_device *device; + struct output_device *next; + struct output_device *prev; + int swaps; + + // Swap sorting since even the most inefficient sorting should do fine here + do + { + swaps = 0; + prev = NULL; + for (device = output_device_list; device && device->next; device = device->next) + { + next = device->next; + if ( (outputs_priority(device) > outputs_priority(next)) || + (outputs_priority(device) == outputs_priority(next) && strcasecmp(device->name, next->name) > 0) ) + { + if (device == output_device_list) + output_device_list = next; + if (prev) + prev->next = next; + + device->next = next->next; + next->next = device; + swaps++; + } + prev = device; + } + } + while (swaps > 0); +} + + /* ----------------------------------- API ---------------------------------- */ struct output_device * @@ -489,6 +534,125 @@ outputs_listener_notify(void) /* ---------------------------- Called by player ---------------------------- */ +int +outputs_device_add(struct output_device *add, bool new_deselect, int default_volume) +{ + struct output_device *device; + char *keep_name; + int ret; + + for (device = output_device_list; device; device = device->next) + { + if (device->id == add->id) + break; + } + + // New device + if (!device) + { + device = add; + + keep_name = strdup(device->name); + ret = db_speaker_get(device, device->id); + if (ret < 0) + { + device->selected = 0; + device->volume = default_volume; + } + + free(device->name); + device->name = keep_name; + + if (new_deselect) + device->selected = 0; + + device->next = output_device_list; + output_device_list = device; + } + // Update to a device already in the list + else + { + if (add->v4_address) + { + free(device->v4_address); + + device->v4_address = add->v4_address; + device->v4_port = add->v4_port; + + // Address is ours now + add->v4_address = NULL; + } + + if (add->v6_address) + { + free(device->v6_address); + + device->v6_address = add->v6_address; + device->v6_port = add->v6_port; + + // Address is ours now + add->v6_address = NULL; + } + + free(device->name); + device->name = add->name; + add->name = NULL; + + device->has_password = add->has_password; + device->password = add->password; + + outputs_device_free(add); + } + + device_list_sort(); + + listener_notify(LISTENER_SPEAKER); + + return 0; +} + +void +outputs_device_remove(struct output_device *remove) +{ + struct output_device *device; + struct output_device *prev; + int ret; + + // Device stop should be able to handle that we invalidate the device, even + // if it is an async stop. It might call outputs_device_session_remove(), but + // that just won't do anything since the id will be unknown. + if (remove->session) + outputs_device_stop(remove, device_stop_cb); + + prev = NULL; + for (device = output_device_list; device; device = device->next) + { + if (device == remove) + break; + + prev = device; + } + + if (!device) + return; + + // Save device volume + ret = db_speaker_save(remove); + if (ret < 0) + DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", remove->type_name, remove->name); + + DPRINTF(E_INFO, L_PLAYER, "Removing %s device '%s'; stopped advertising\n", remove->type_name, remove->name); + + if (!prev) + output_device_list = remove->next; + else + prev->next = remove->next; + + outputs_device_free(remove); + + listener_notify(LISTENER_SPEAKER); +} + int outputs_device_start(struct output_device *device, output_status_cb cb) { diff --git a/src/outputs.h b/src/outputs.h index 9997cfea..ef2fddea 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -114,7 +114,7 @@ struct output_device // Misc device flags unsigned selected:1; - unsigned advertised:1; +// unsigned advertised:1; unsigned has_password:1; unsigned has_video:1; unsigned requires_auth:1; @@ -266,6 +266,12 @@ outputs_listener_notify(void); /* ---------------------------- Called by player ---------------------------- */ +int +outputs_device_add(struct output_device *add, bool new_deselect, int default_volume); + +void +outputs_device_remove(struct output_device *remove); + int outputs_device_start(struct output_device *device, output_status_cb cb); diff --git a/src/outputs/alsa.c b/src/outputs/alsa.c index 35a8a46c..a27454d5 100644 --- a/src/outputs/alsa.c +++ b/src/outputs/alsa.c @@ -1028,7 +1028,6 @@ alsa_init(void) device->name = strdup(nickname); device->type = OUTPUT_TYPE_ALSA; device->type_name = outputs_name(device->type); - device->advertised = 1; device->has_video = 0; DPRINTF(E_INFO, L_LAUDIO, "Adding ALSA device '%s' with name '%s'\n", card_name, nickname); diff --git a/src/outputs/cast.c b/src/outputs/cast.c index b4940740..5323e809 100644 --- a/src/outputs/cast.c +++ b/src/outputs/cast.c @@ -1299,8 +1299,6 @@ cast_device_cb(const char *name, const char *type, const char *domain, const cha DPRINTF(E_INFO, L_CAST, "Adding Chromecast device '%s'\n", name); - device->advertised = 1; - switch (family) { case AF_INET: diff --git a/src/outputs/dummy.c b/src/outputs/dummy.c index 591eb18d..8cf40ad2 100644 --- a/src/outputs/dummy.c +++ b/src/outputs/dummy.c @@ -203,7 +203,6 @@ dummy_init(void) device->name = strdup(nickname); device->type = OUTPUT_TYPE_DUMMY; device->type_name = outputs_name(device->type); - device->advertised = 1; device->has_video = 0; DPRINTF(E_INFO, L_LAUDIO, "Adding dummy output device '%s'\n", nickname); diff --git a/src/outputs/fifo.c b/src/outputs/fifo.c index ccb31e9b..904a1643 100644 --- a/src/outputs/fifo.c +++ b/src/outputs/fifo.c @@ -505,7 +505,6 @@ fifo_init(void) device->name = strdup(nickname); device->type = OUTPUT_TYPE_FIFO; device->type_name = outputs_name(device->type); - device->advertised = 1; device->has_video = 0; device->extra_device_info = path; DPRINTF(E_INFO, L_FIFO, "Adding fifo output device '%s' with path '%s'\n", nickname, path); diff --git a/src/outputs/pulse.c b/src/outputs/pulse.c index 2bd455d7..42e9f044 100644 --- a/src/outputs/pulse.c +++ b/src/outputs/pulse.c @@ -428,7 +428,6 @@ sinklist_cb(pa_context *ctx, const pa_sink_info *info, int eol, void *userdata) device->name = strdup(name); device->type = OUTPUT_TYPE_PULSE; device->type_name = outputs_name(device->type); - device->advertised = 1; device->extra_device_info = strdup(info->name); player_device_add(device); diff --git a/src/outputs/raop.c b/src/outputs/raop.c index 512b6578..688cae1f 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -4712,8 +4712,6 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha free(et); } - rd->advertised = 1; - switch (family) { case AF_INET: diff --git a/src/player.c b/src/player.c index 6e474fd6..6c028316 100644 --- a/src/player.c +++ b/src/player.c @@ -1203,198 +1203,37 @@ playback_cb(int fd, short what, void *arg) /* ----------------- Output device handling (add/remove etc) ---------------- */ -static void -device_list_sort(void) -{ - struct output_device *device; - struct output_device *next; - struct output_device *prev; - int swaps; - - // Swap sorting since even the most inefficient sorting should do fine here - do - { - swaps = 0; - prev = NULL; - for (device = output_device_list; device && device->next; device = device->next) - { - next = device->next; - if ( (outputs_priority(device) > outputs_priority(next)) || - (outputs_priority(device) == outputs_priority(next) && strcasecmp(device->name, next->name) > 0) ) - { - if (device == output_device_list) - output_device_list = next; - if (prev) - prev->next = next; - - device->next = next->next; - next->next = device; - swaps++; - } - prev = device; - } - } - while (swaps > 0); -} - -static void -device_remove(struct output_device *remove) -{ - struct output_device *device; - struct output_device *prev; - int ret; - - prev = NULL; - for (device = output_device_list; device; device = device->next) - { - if (device == remove) - break; - - prev = device; - } - - if (!device) - return; - - // Save device volume - ret = db_speaker_save(remove); - if (ret < 0) - DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", remove->type_name, remove->name); - - DPRINTF(E_INFO, L_PLAYER, "Removing %s device '%s'; stopped advertising\n", remove->type_name, remove->name); - - // Make sure device isn't selected anymore - if (remove->selected) - speaker_deselect_output(remove); - - if (!prev) - output_device_list = remove->next; - else - prev->next = remove->next; - - outputs_device_free(remove); -} - -static int -device_check(struct output_device *check) -{ - struct output_device *device; - - for (device = output_device_list; device; device = device->next) - { - if (device == check) - break; - } - - return (device) ? 0 : -1; -} - static enum command_state device_add(void *arg, int *retval) { - union player_arg *cmdarg; - struct output_device *add; - struct output_device *device; - char *keep_name; - int ret; + union player_arg *cmdarg = arg; + struct output_device *device = cmdarg->device; + bool new_deselect; + int default_volume; - cmdarg = arg; - add = cmdarg->device; + default_volume = (master_volume >= 0) ? master_volume : PLAYER_DEFAULT_VOLUME; - for (device = output_device_list; device; device = device->next) - { - if (device->id == add->id) - break; - } + // Never turn on new devices during playback + new_deselect = (player_state == PLAY_PLAYING); - // New device - if (!device) - { - device = add; + *retval = outputs_device_add(device, new_deselect, default_volume); - keep_name = strdup(device->name); - ret = db_speaker_get(device, device->id); - if (ret < 0) - { - device->selected = 0; - device->volume = (master_volume >= 0) ? master_volume : PLAYER_DEFAULT_VOLUME; - } + if (device->selected) + speaker_select_output(device); - free(device->name); - device->name = keep_name; - - if (device->selected && (player_state != PLAY_PLAYING)) - speaker_select_output(device); - else - device->selected = 0; - - device->next = output_device_list; - output_device_list = device; - } - // Update to a device already in the list - else - { - device->advertised = 1; - - if (add->v4_address) - { - if (device->v4_address) - free(device->v4_address); - - device->v4_address = add->v4_address; - device->v4_port = add->v4_port; - - // Address is ours now - add->v4_address = NULL; - } - - if (add->v6_address) - { - if (device->v6_address) - free(device->v6_address); - - device->v6_address = add->v6_address; - device->v6_port = add->v6_port; - - // Address is ours now - add->v6_address = NULL; - } - - if (device->name) - free(device->name); - device->name = add->name; - add->name = NULL; - - device->has_password = add->has_password; - device->password = add->password; - - outputs_device_free(add); - } - - device_list_sort(); - - listener_notify(LISTENER_SPEAKER); - - *retval = 0; return COMMAND_END; } static enum command_state device_remove_family(void *arg, int *retval) { - union player_arg *cmdarg; + union player_arg *cmdarg = arg; struct output_device *remove; struct output_device *device; - cmdarg = arg; remove = cmdarg->device; - for (device = output_device_list; device; device = device->next) - { - if (device->id == remove->id) - break; - } - + device = outputs_device_get(remove->id); if (!device) { DPRINTF(E_WARN, L_PLAYER, "The %s device '%s' stopped advertising, but not in our list\n", remove->type_name, remove->name); @@ -1421,16 +1260,16 @@ device_remove_family(void *arg, int *retval) if (!device->v4_address && !device->v6_address) { - device->advertised = 0; + // Make sure device isn't selected anymore + if (device->selected) + speaker_deselect_output(device); - if (!device->session) - device_remove(device); + // Will also stop sessions on the device, if any + outputs_device_remove(device); } outputs_device_free(remove); - listener_notify(LISTENER_SPEAKER); - *retval = 0; return COMMAND_END; } @@ -1471,12 +1310,7 @@ device_metadata_send(void *arg, int *retval) static void device_streaming_cb(struct output_device *device, enum output_device_state status) { - int ret; - - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_streaming_cb (status %d)\n", outputs_name(device->type), status); - - ret = device_check(device); - if (ret < 0) + if (!device) { DPRINTF(E_LOG, L_PLAYER, "Output device disappeared during streaming!\n"); @@ -1484,6 +1318,8 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu return; } + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_streaming_cb (status %d)\n", outputs_name(device->type), status); + if (status == OUTPUT_STATE_FAILED) { DPRINTF(E_LOG, L_PLAYER, "The %s device '%s' FAILED\n", device->type_name, device->name); @@ -1493,9 +1329,6 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu if (player_state == PLAY_PLAYING) speaker_deselect_output(device); - if (!device->advertised) - device_remove(device); - if (output_sessions == 0) playback_abort(); } @@ -1504,9 +1337,6 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' stopped\n", device->type_name, device->name); output_sessions--; - - if (!device->advertised) - device_remove(device); } else outputs_device_cb_set(device, device_streaming_cb); @@ -1515,6 +1345,12 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu static void device_command_cb(struct output_device *device, enum output_device_state status) { + if (!device) + { + DPRINTF(E_LOG, L_PLAYER, "Output device disappeared before command completion!\n"); + goto out; + } + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_command_cb (status %d)\n", outputs_name(device->type), status); outputs_device_cb_set(device, device_streaming_cb); @@ -1530,6 +1366,7 @@ device_command_cb(struct output_device *device, enum output_device_state status) input_buffer_full_cb(player_playback_start); } + out: commands_exec_end(cmdbase, 0); } @@ -1537,16 +1374,12 @@ static void device_shutdown_cb(struct output_device *device, enum output_device_state status) { int retval; - int ret; - - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_shutdown_cb (status %d)\n", outputs_name(device->type), status); if (output_sessions) output_sessions--; retval = commands_exec_returnvalue(cmdbase); - ret = device_check(device); - if (ret < 0) + if (!device) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared before shutdown completion!\n"); @@ -1555,8 +1388,7 @@ device_shutdown_cb(struct output_device *device, enum output_device_state status goto out; } - if (!device->advertised) - device_remove(device); + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_shutdown_cb (status %d)\n", outputs_name(device->type), status); out: /* cur_cmd->ret already set @@ -1566,39 +1398,23 @@ device_shutdown_cb(struct output_device *device, enum output_device_state status commands_exec_end(cmdbase, retval); } -static void -device_lost_cb(struct output_device *device, enum output_device_state status) -{ - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_lost_cb (status %d)\n", outputs_name(device->type), status); - - // We lost that device during startup for some reason, not much we can do here - if (status == OUTPUT_STATE_FAILED) - DPRINTF(E_WARN, L_PLAYER, "Failed to stop lost device\n"); - else - DPRINTF(E_INFO, L_PLAYER, "Lost device stopped properly\n"); -} - static void device_activate_cb(struct output_device *device, enum output_device_state status) { int retval; - int ret; - - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_activate_cb (status %d)\n", outputs_name(device->type), status); retval = commands_exec_returnvalue(cmdbase); - ret = device_check(device); - if (ret < 0) + if (!device) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during startup!\n"); - outputs_device_stop(device, device_lost_cb); - if (retval != -2) retval = -1; goto out; } + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_activate_cb (status %d)\n", outputs_name(device->type), status); + if (status == OUTPUT_STATE_PASSWORD) { status = OUTPUT_STATE_FAILED; @@ -1609,9 +1425,6 @@ device_activate_cb(struct output_device *device, enum output_device_state status { speaker_deselect_output(device); - if (!device->advertised) - device_remove(device); - if (retval != -2) retval = -1; goto out; @@ -1634,13 +1447,9 @@ static void device_probe_cb(struct output_device *device, enum output_device_state status) { int retval; - int ret; - - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_probe_cb (status %d)\n", outputs_name(device->type), status); retval = commands_exec_returnvalue(cmdbase); - ret = device_check(device); - if (ret < 0) + if (!device) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during probe!\n"); @@ -1649,6 +1458,8 @@ device_probe_cb(struct output_device *device, enum output_device_state status) goto out; } + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_probe_cb (status %d)\n", outputs_name(device->type), status); + if (status == OUTPUT_STATE_PASSWORD) { status = OUTPUT_STATE_FAILED; @@ -1659,9 +1470,6 @@ device_probe_cb(struct output_device *device, enum output_device_state status) { speaker_deselect_output(device); - if (!device->advertised) - device_remove(device); - if (retval != -2) retval = -1; goto out; @@ -1680,23 +1488,19 @@ static void device_restart_cb(struct output_device *device, enum output_device_state status) { int retval; - int ret; - - DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_restart_cb (status %d)\n", outputs_name(device->type), status); retval = commands_exec_returnvalue(cmdbase); - ret = device_check(device); - if (ret < 0) + if (!device) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during restart!\n"); - outputs_device_stop(device, device_lost_cb); - if (retval != -2) retval = -1; goto out; } + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_restart_cb (status %d)\n", outputs_name(device->type), status); + if (status == OUTPUT_STATE_PASSWORD) { status = OUTPUT_STATE_FAILED; @@ -1707,9 +1511,6 @@ device_restart_cb(struct output_device *device, enum output_device_state status) { speaker_deselect_output(device); - if (!device->advertised) - device_remove(device); - if (retval != -2) retval = -1; goto out; @@ -1733,8 +1534,6 @@ player_pmap(void *p) return "device_activate_cb"; else if (p == device_streaming_cb) return "device_streaming_cb"; - else if (p == device_lost_cb) - return "device_lost_cb"; else if (p == device_command_cb) return "device_command_cb"; else if (p == device_shutdown_cb) @@ -2479,7 +2278,7 @@ speaker_enumerate(void *arg, int *retval) for (device = output_device_list; device; device = device->next) { - if (device->advertised || device->selected) + if (device->selected) { device_to_speaker_info(&spk, device); spk_enum->cb(&spk, spk_enum->arg);