From a7e8476996b7fb41ee8c57d58cb204d2515958c8 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 12 Feb 2019 21:25:27 +0100 Subject: [PATCH] [outputs] Refactor outputs some more, eg change callback system --- src/input.c | 3 - src/outputs.c | 457 ++++++++++++++++++++++++++++++++------------ src/outputs.h | 71 ++++--- src/outputs/alsa.c | 2 +- src/outputs/cast.c | 3 +- src/outputs/dummy.c | 96 +++------- src/outputs/fifo.c | 95 +++------ src/outputs/pulse.c | 2 +- src/outputs/raop.c | 184 ++++++++++-------- src/player.c | 88 +++++---- src/player.h | 6 +- 11 files changed, 589 insertions(+), 418 deletions(-) diff --git a/src/input.c b/src/input.c index cda5f13b..42173fba 100644 --- a/src/input.c +++ b/src/input.c @@ -267,9 +267,6 @@ input_write(struct evbuffer *evbuf, struct media_quality *quality, short flags) return 0; } - // Change of quality. Note, the marker is placed at the last position of the - // last byte we wrote, even though that of course doesn't have the new quality - // yet. Not intuitive, but input_read() will understand. if (quality && !quality_is_equal(quality, &input_buffer.cur_write_quality)) { input_buffer.cur_write_quality = *quality; diff --git a/src/outputs.c b/src/outputs.c index c0eb861f..342bfa05 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -29,9 +29,13 @@ #include #include +#include + #include "logger.h" #include "misc.h" #include "transcode.h" +#include "listener.h" +#include "player.h" //TODO remove me when player_pmap is removed again #include "outputs.h" extern struct output_definition output_raop; @@ -48,6 +52,9 @@ extern struct output_definition output_pulse; extern struct output_definition output_cast; #endif +/* From player.c */ +extern struct event_base *evbase_player; + // Must be in sync with enum output_types static struct output_definition *outputs[] = { &output_raop, @@ -66,6 +73,24 @@ static struct output_definition *outputs[] = { NULL }; +#define OUTPUTS_MAX_CALLBACKS 64 + +struct outputs_callback_queue +{ + output_status_cb cb; + struct output_device *device; + + // We have received the callback with the result from the backend + bool ready; + + // We store a device_id to avoid the risk of dangling device pointer + uint64_t device_id; + enum output_device_state state; +}; + +struct outputs_callback_queue outputs_cb_queue[OUTPUTS_MAX_CALLBACKS]; +struct event *outputs_deferredev; + struct output_quality_subscription { int count; @@ -79,6 +104,115 @@ static bool output_got_new_subscription; /* ------------------------------- MISC HELPERS ----------------------------- */ +static void +callback_remove(struct output_device *device) +{ + int callback_id; + + if (!device) + return; + + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + { + if (outputs_cb_queue[callback_id].device == device) + { + DPRINTF(E_DBG, L_PLAYER, "Removing callback to %s, id %d\n", player_pmap(outputs_cb_queue[callback_id].cb), callback_id); + memset(&outputs_cb_queue[callback_id], 0, sizeof(struct outputs_callback_queue)); + } + } +} + +static int +callback_add(struct output_device *device, output_status_cb cb) +{ + int callback_id; + + if (!cb) + return -1; + + // We will replace any previously registered callbacks, since that's what the + // player expects + callback_remove(device); + + // Find a free slot in the queue + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + { + if (outputs_cb_queue[callback_id].cb == NULL) + break; + } + + if (callback_id == ARRAY_SIZE(outputs_cb_queue)) + { + DPRINTF(E_LOG, L_PLAYER, "Output callback queue is full! (size is %d)\n", OUTPUTS_MAX_CALLBACKS); + return -1; + } + + outputs_cb_queue[callback_id].cb = cb; + outputs_cb_queue[callback_id].device = device; // Don't dereference this later, it might become invalid! + + DPRINTF(E_DBG, L_PLAYER, "Registered callback to %s with id %d\n", player_pmap(cb), callback_id); + + int active = 0; + for (int i = 0; i < ARRAY_SIZE(outputs_cb_queue); i++) + if (outputs_cb_queue[i].cb) + active++; + + DPRINTF(E_DBG, L_PLAYER, "Number of active callbacks: %d\n", active); + + 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) +{ + struct output_device *device; + output_status_cb cb; + enum output_device_state state; + int callback_id; + + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + { + if (outputs_cb_queue[callback_id].ready) + { + // Must copy before making callback, since you never know what the + // callback might result in (could call back in) + cb = outputs_cb_queue[callback_id].cb; + state = outputs_cb_queue[callback_id].state; + + // Will be NULL if the device has disappeared + device = outputs_device_get(outputs_cb_queue[callback_id].device_id); + + memset(&outputs_cb_queue[callback_id], 0, sizeof(struct outputs_callback_queue)); + + DPRINTF(E_DBG, L_PLAYER, "Making deferred callback to %s, id was %d\n", player_pmap(cb), callback_id); + + cb(device, state); + } + } + + for (int i = 0; i < ARRAY_SIZE(outputs_cb_queue); i++) + { + if (outputs_cb_queue[i].cb) + DPRINTF(E_DBG, L_PLAYER, "%d. Active callback: %s\n", i, player_pmap(outputs_cb_queue[i].cb)); + } +} + static enum transcode_profile quality_to_xcode(struct media_quality *quality) { @@ -206,6 +340,155 @@ buffer_drain(struct output_buffer *obuf) /* ----------------------------------- API ---------------------------------- */ +struct output_device * +outputs_device_get(uint64_t device_id) +{ + struct output_device *device; + + for (device = output_device_list; device; device = device->next) + { + if (device_id == device->id) + return device; + } + + DPRINTF(E_LOG, L_PLAYER, "Output device with id %" PRIu64 " has disappeared from our list\n", device_id); + return NULL; +} + +/* ----------------------- Called by backend modules ------------------------ */ + +// Sessions free their sessions themselves, but should not touch the device, +// since they can't know for sure that it is still valid in memory +int +outputs_device_session_add(uint64_t device_id, void *session) +{ + struct output_device *device; + + device = outputs_device_get(device_id); + if (!device) + return -1; + + device->session = session; + return 0; +} + +void +outputs_device_session_remove(uint64_t device_id) +{ + struct output_device *device; + + device = outputs_device_get(device_id); + if (device) + device->session = NULL; + + return; +} + +int +outputs_quality_subscribe(struct media_quality *quality) +{ + int i; + + // If someone else is already subscribing to this quality we just increase the + // reference count. + for (i = 0; output_quality_subscriptions[i].count > 0; i++) + { + if (!quality_is_equal(quality, &output_quality_subscriptions[i].quality)) + continue; + + output_quality_subscriptions[i].count++; + + DPRINTF(E_DBG, L_PLAYER, "Subscription request for quality %d/%d/%d (now %d subscribers)\n", + quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); + + return 0; + } + + if (i >= (ARRAY_SIZE(output_quality_subscriptions) - 1)) + { + DPRINTF(E_LOG, L_PLAYER, "Bug! The number of different quality levels requested by outputs is too high\n"); + return -1; + } + + output_quality_subscriptions[i].quality = *quality; + output_quality_subscriptions[i].count++; + + DPRINTF(E_DBG, L_PLAYER, "Subscription request for quality %d/%d/%d (now %d subscribers)\n", + quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); + + // Better way of signaling this? + output_got_new_subscription = true; + + return 0; +} + +void +outputs_quality_unsubscribe(struct media_quality *quality) +{ + int i; + + // Find subscription + for (i = 0; output_quality_subscriptions[i].count > 0; i++) + { + if (quality_is_equal(quality, &output_quality_subscriptions[i].quality)) + break; + } + + if (output_quality_subscriptions[i].count == 0) + { + DPRINTF(E_LOG, L_PLAYER, "Bug! Unsubscription request for a quality level that there is no subscription for\n"); + return; + } + + output_quality_subscriptions[i].count--; + + DPRINTF(E_DBG, L_PLAYER, "Unsubscription request for quality %d/%d/%d (now %d subscribers)\n", + quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); + + if (output_quality_subscriptions[i].count > 0) + return; + + transcode_encode_cleanup(&output_quality_subscriptions[i].encode_ctx); + + // Shift elements + for (; i < ARRAY_SIZE(output_quality_subscriptions) - 1; i++) + output_quality_subscriptions[i] = output_quality_subscriptions[i + 1]; +} + +// Output backends call back through the below wrapper to make sure that: +// 1. Callbacks are always deferred +// 2. The callback never has a dangling pointer to a device (a device that has been removed from our list) +void +outputs_cb(int callback_id, uint64_t device_id, enum output_device_state state) +{ + if (callback_id < 0) + return; + + if (!(callback_id < ARRAY_SIZE(outputs_cb_queue)) || !outputs_cb_queue[callback_id].cb) + { + DPRINTF(E_LOG, L_PLAYER, "Bug! Output backend called us with an illegal callback id (%d)\n", callback_id); + return; + } + + DPRINTF(E_DBG, L_PLAYER, "Callback request received, id is %i\n", callback_id); + + outputs_cb_queue[callback_id].ready = true; + outputs_cb_queue[callback_id].device_id = device_id; + outputs_cb_queue[callback_id].state = state; + event_active(outputs_deferredev, 0, 0); +} + +// Maybe not so great, seems it would be better if integrated into the callback +// mechanism so that the notifications where at least deferred +void +outputs_listener_notify(void) +{ + listener_notify(LISTENER_SPEAKER); +} + + +/* ---------------------------- Called by player ---------------------------- */ + int outputs_device_start(struct output_device *device, output_status_cb cb) { @@ -218,7 +501,7 @@ outputs_device_start(struct output_device *device, output_status_cb cb) return -1; } - return outputs[device->type]->device_start(device, cb); + return outputs[device->type]->device_start(device, callback_add(device, cb)); } int @@ -233,7 +516,7 @@ outputs_device_stop(struct output_device *device, output_status_cb cb) return -1; } - return outputs[device->type]->device_stop(device, cb); + return outputs[device->type]->device_stop(device, callback_add(device, cb)); } int @@ -248,7 +531,46 @@ outputs_device_probe(struct output_device *device, output_status_cb cb) return -1; } - return outputs[device->type]->device_probe(device, cb); + return outputs[device->type]->device_probe(device, callback_add(device, cb)); +} + +int +outputs_device_volume_set(struct output_device *device, output_status_cb cb) +{ + if (outputs[device->type]->disabled || !outputs[device->type]->device_volume_set) + return -1; + + return outputs[device->type]->device_volume_set(device, callback_add(device, cb)); +} + +int +outputs_device_volume_to_pct(struct output_device *device, const char *volume) +{ + if (outputs[device->type]->disabled || !outputs[device->type]->device_volume_to_pct) + return -1; + + return outputs[device->type]->device_volume_to_pct(device, volume); +} + +int +outputs_device_quality_set(struct output_device *device, struct media_quality *quality, output_status_cb cb) +{ + if (outputs[device->type]->disabled || !outputs[device->type]->device_quality_set) + return -1; + + return outputs[device->type]->device_quality_set(device, quality, callback_add(device, cb)); +} + +void +outputs_device_cb_set(struct output_device *device, output_status_cb cb) +{ + if (outputs[device->type]->disabled || !outputs[device->type]->device_cb_set) + return; + + if (!device->session) + return; + + outputs[device->type]->device_cb_set(device, callback_add(device, cb)); } void @@ -274,52 +596,6 @@ outputs_device_free(struct output_device *device) free(device); } -int -outputs_device_volume_set(struct output_device *device, output_status_cb cb) -{ - if (outputs[device->type]->disabled) - return -1; - - if (outputs[device->type]->device_volume_set) - return outputs[device->type]->device_volume_set(device, cb); - else - return -1; -} - -int -outputs_device_volume_to_pct(struct output_device *device, const char *volume) -{ - if (outputs[device->type]->disabled) - return -1; - - if (outputs[device->type]->device_volume_to_pct) - return outputs[device->type]->device_volume_to_pct(device, volume); - else - return -1; -} - -int -outputs_device_quality_set(struct output_device *device, struct media_quality *quality) -{ - if (outputs[device->type]->disabled) - return -1; - - if (outputs[device->type]->quality_set) - return outputs[device->type]->quality_set(device, quality); - else - return -1; -} - -void -outputs_device_set_cb(struct output_device *device, output_status_cb cb) -{ - if (outputs[device->type]->disabled) - return; - - if (outputs[device->type]->device_set_cb) - outputs[device->type]->device_set_cb(device, cb); -} - void outputs_playback_stop(void) { @@ -363,11 +639,13 @@ outputs_flush(output_status_cb cb) ret = 0; for (i = 0; outputs[i]; i++) { - if (outputs[i]->disabled) + if (outputs[i]->disabled || !outputs[i]->flush) continue; - if (outputs[i]->flush) - ret += outputs[i]->flush(cb); + // Clear callback register for all devices belonging to outputs[i] + callback_remove_all(outputs[i]->type); + + ret += outputs[i]->flush(callback_add(NULL, cb)); } return ret; @@ -489,77 +767,6 @@ outputs_authorize(enum output_types type, const char *pin) outputs[type]->authorize(pin); } -int -outputs_quality_subscribe(struct media_quality *quality) -{ - int i; - - // If someone else is already subscribing to this quality we just increase the - // reference count. - for (i = 0; output_quality_subscriptions[i].count > 0; i++) - { - if (!quality_is_equal(quality, &output_quality_subscriptions[i].quality)) - continue; - - output_quality_subscriptions[i].count++; - - DPRINTF(E_DBG, L_PLAYER, "Subscription request for quality %d/%d/%d (now %d subscribers)\n", - quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); - - return 0; - } - - if (i >= (ARRAY_SIZE(output_quality_subscriptions) - 1)) - { - DPRINTF(E_LOG, L_PLAYER, "Bug! The number of different quality levels requested by outputs is too high\n"); - return -1; - } - - output_quality_subscriptions[i].quality = *quality; - output_quality_subscriptions[i].count++; - - DPRINTF(E_DBG, L_PLAYER, "Subscription request for quality %d/%d/%d (now %d subscribers)\n", - quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); - - // Better way of signaling this? - output_got_new_subscription = true; - - return 0; -} - -void -outputs_quality_unsubscribe(struct media_quality *quality) -{ - int i; - - // Find subscription - for (i = 0; output_quality_subscriptions[i].count > 0; i++) - { - if (quality_is_equal(quality, &output_quality_subscriptions[i].quality)) - break; - } - - if (output_quality_subscriptions[i].count == 0) - { - DPRINTF(E_LOG, L_PLAYER, "Bug! Unsubscription request for a quality level that there is no subscription for\n"); - return; - } - - output_quality_subscriptions[i].count--; - - DPRINTF(E_DBG, L_PLAYER, "Unsubscription request for quality %d/%d/%d (now %d subscribers)\n", - quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); - - if (output_quality_subscriptions[i].count > 0) - return; - - transcode_encode_cleanup(&output_quality_subscriptions[i].encode_ctx); - - // Shift elements - for (; i < ARRAY_SIZE(output_quality_subscriptions) - 1; i++) - output_quality_subscriptions[i] = output_quality_subscriptions[i + 1]; -} - int outputs_priority(struct output_device *device) { @@ -579,6 +786,8 @@ outputs_init(void) int ret; int i; + CHECK_NULL(L_PLAYER, outputs_deferredev = evtimer_new(evbase_player, deferred_cb, NULL)); + no_output = 1; for (i = 0; outputs[i]; i++) { @@ -615,6 +824,8 @@ outputs_deinit(void) { int i; + evtimer_del(outputs_deferredev); + for (i = 0; outputs[i]; i++) { if (outputs[i]->disabled) diff --git a/src/outputs.h b/src/outputs.h index c6763b49..9997cfea 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -21,7 +21,6 @@ * Here is the sequence of commands from the player to the outputs, and the * callback from the output once the command has been executed. Commands marked * with * may make multiple callbacks if multiple sessions are affected. - * (TODO should callbacks always be deferred?) * * PLAYER OUTPUT PLAYER CB * speaker_activate -> device_start -> device_activate_cb @@ -150,6 +149,7 @@ struct output_metadata { enum output_types type; void *metadata; + struct output_metadata *next; }; @@ -169,7 +169,6 @@ struct output_buffer struct output_frame frames[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 1]; } output_buffer; - typedef void (*output_status_cb)(struct output_device *device, enum output_device_state status); struct output_definition @@ -195,38 +194,37 @@ struct output_definition void (*deinit)(void); // Prepare a playback session on device and call back - int (*device_start)(struct output_device *device, output_status_cb cb); + int (*device_start)(struct output_device *device, int callback_id); // Close a session prepared by device_start and call back - int (*device_stop)(struct output_device *device, output_status_cb cb); + int (*device_stop)(struct output_device *device, int callback_id); // Test the connection to a device and call back - int (*device_probe)(struct output_device *device, output_status_cb cb); - - // Free the private device data - void (*device_free_extra)(struct output_device *device); + int (*device_probe)(struct output_device *device, int callback_id); // Set the volume and call back - int (*device_volume_set)(struct output_device *device, output_status_cb cb); + int (*device_volume_set)(struct output_device *device, int callback_id); // Convert device internal representation of volume to our pct scale int (*device_volume_to_pct)(struct output_device *device, const char *volume); // Request a change of quality from the device - int (*quality_set)(struct output_device *device, struct media_quality *quality); + int (*device_quality_set)(struct output_device *device, struct media_quality *quality, int callback_id); // Change the call back associated with a device - void (*device_set_cb)(struct output_device *device, output_status_cb cb); + void (*device_cb_set)(struct output_device *device, int callback_id); + + // Free the private device data + void (*device_free_extra)(struct output_device *device); // Start/stop playback on devices that were started - void (*playback_start)(uint64_t next_pkt, struct timespec *ts); void (*playback_stop)(void); // Write stream data to the output devices void (*write)(struct output_buffer *buffer); // Flush all sessions, the return must be number of sessions pending the flush - int (*flush)(output_status_cb cb); + int (*flush)(int callback_id); // Authorize an output with a pin-code (probably coming from the filescanner) void (*authorize)(const char *pin); @@ -238,6 +236,36 @@ struct output_definition void (*metadata_prune)(uint64_t rtptime); }; +// Our main list of devices, not for use by backend modules +struct output_device *output_device_list; + +/* ------------------------------- General use ------------------------------ */ + +struct output_device * +outputs_device_get(uint64_t device_id); + +/* ----------------------- Called by backend modules ------------------------ */ + +int +outputs_device_session_add(uint64_t device_id, void *session); + +void +outputs_device_session_remove(uint64_t device_id); + +int +outputs_quality_subscribe(struct media_quality *quality); + +void +outputs_quality_unsubscribe(struct media_quality *quality); + +void +outputs_cb(int callback_id, uint64_t device_id, enum output_device_state); + +void +outputs_listener_notify(void); + +/* ---------------------------- Called by player ---------------------------- */ + int outputs_device_start(struct output_device *device, output_status_cb cb); @@ -247,21 +275,20 @@ outputs_device_stop(struct output_device *device, output_status_cb cb); int outputs_device_probe(struct output_device *device, output_status_cb cb); -void -outputs_device_free(struct output_device *device); - int outputs_device_volume_set(struct output_device *device, output_status_cb cb); int outputs_device_volume_to_pct(struct output_device *device, const char *value); -// TODO should this function have a callback? int -outputs_device_quality_set(struct output_device *device, struct media_quality *quality); +outputs_device_quality_set(struct output_device *device, struct media_quality *quality, output_status_cb cb); void -outputs_device_set_cb(struct output_device *device, output_status_cb cb); +outputs_device_cb_set(struct output_device *device, output_status_cb cb); + +void +outputs_device_free(struct output_device *device); void outputs_playback_stop(void); @@ -290,12 +317,6 @@ outputs_metadata_free(struct output_metadata *omd); void outputs_authorize(enum output_types type, const char *pin); -int -outputs_quality_subscribe(struct media_quality *quality); - -void -outputs_quality_unsubscribe(struct media_quality *quality); - int outputs_priority(struct output_device *device); diff --git a/src/outputs/alsa.c b/src/outputs/alsa.c index 9294fcff..35a8a46c 100644 --- a/src/outputs/alsa.c +++ b/src/outputs/alsa.c @@ -196,7 +196,7 @@ alsa_session_make(struct output_device *device, output_status_cb cb) as->next = sessions; sessions = as; - device->session = as; + outputs_device_session_add(device, as); return as; diff --git a/src/outputs/cast.c b/src/outputs/cast.c index 7ddb7b65..b4940740 100644 --- a/src/outputs/cast.c +++ b/src/outputs/cast.c @@ -1420,7 +1420,8 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb) cs->next = sessions; sessions = cs; - device->session = cs; + // cs is now the official session for the device + outputs_device_session_add(device, cs); proto = gnutls_protocol_get_name(gnutls_protocol_get_version(cs->tls_session)); diff --git a/src/outputs/dummy.c b/src/outputs/dummy.c index 78f24553..591eb18d 100644 --- a/src/outputs/dummy.c +++ b/src/outputs/dummy.c @@ -33,8 +33,7 @@ #include #include -#include - +#include "misc.h" #include "conffile.h" #include "logger.h" #include "player.h" @@ -44,22 +43,12 @@ struct dummy_session { enum output_device_state state; - struct event *deferredev; - output_status_cb defer_cb; - - struct output_device *device; - output_status_cb status_cb; + uint64_t device_id; + int callback_id; }; -/* From player.c */ -extern struct event_base *evbase_player; - struct dummy_session *sessions; -/* Forwards */ -static void -defer_cb(int fd, short what, void *arg); - /* ---------------------------- SESSION HANDLING ---------------------------- */ static void @@ -68,8 +57,6 @@ dummy_session_free(struct dummy_session *ds) if (!ds) return; - event_free(ds->deferredev); - free(ds); } @@ -79,38 +66,25 @@ dummy_session_cleanup(struct dummy_session *ds) // Normally some here code to remove from linked list - here we just say: sessions = NULL; - ds->device->session = NULL; + outputs_device_session_remove(ds->device_id); dummy_session_free(ds); } static struct dummy_session * -dummy_session_make(struct output_device *device, output_status_cb cb) +dummy_session_make(struct output_device *device, int callback_id) { struct dummy_session *ds; - ds = calloc(1, sizeof(struct dummy_session)); - if (!ds) - { - DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy session (as)\n"); - return NULL; - } - - ds->deferredev = evtimer_new(evbase_player, defer_cb, ds); - if (!ds->deferredev) - { - DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy deferred event\n"); - free(ds); - return NULL; - } + CHECK_NULL(L_LAUDIO, ds = calloc(1, sizeof(struct dummy_session))); ds->state = OUTPUT_STATE_CONNECTED; - ds->device = device; - ds->status_cb = cb; + ds->device_id = device->id; + ds->callback_id = callback_id; sessions = ds; - device->session = ds; + outputs_device_session_add(device->id, ds); return ds; } @@ -118,37 +92,24 @@ dummy_session_make(struct output_device *device, output_status_cb cb) /* ---------------------------- STATUS HANDLERS ----------------------------- */ -// Maps our internal state to the generic output state and then makes a callback -// to the player to tell that state -static void -defer_cb(int fd, short what, void *arg) -{ - struct dummy_session *ds = arg; - - if (ds->defer_cb) - ds->defer_cb(ds->device, ds->state); - - if (ds->state == OUTPUT_STATE_STOPPED) - dummy_session_cleanup(ds); -} - static void dummy_status(struct dummy_session *ds) { - ds->defer_cb = ds->status_cb; - event_active(ds->deferredev, 0, 0); - ds->status_cb = NULL; + outputs_cb(ds->callback_id, ds->device_id, ds->state); + + if (ds->state == OUTPUT_STATE_STOPPED) + dummy_session_cleanup(ds); } /* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */ static int -dummy_device_start(struct output_device *device, output_status_cb cb) +dummy_device_start(struct output_device *device, int callback_id) { struct dummy_session *ds; - ds = dummy_session_make(device, cb); + ds = dummy_session_make(device, callback_id); if (!ds) return -1; @@ -158,26 +119,28 @@ dummy_device_start(struct output_device *device, output_status_cb cb) } static int -dummy_device_stop(struct output_device *device, output_status_cb cb) +dummy_device_stop(struct output_device *device, int callback_id) { struct dummy_session *ds = device->session; + ds->callback_id = callback_id; ds->state = OUTPUT_STATE_STOPPED; + dummy_status(ds); return 0; } static int -dummy_device_probe(struct output_device *device, output_status_cb cb) +dummy_device_probe(struct output_device *device, int callback_id) { struct dummy_session *ds; - ds = dummy_session_make(device, cb); + ds = dummy_session_make(device, callback_id); if (!ds) return -1; - ds->status_cb = cb; + ds->callback_id = callback_id; ds->state = OUTPUT_STATE_STOPPED; dummy_status(ds); @@ -186,25 +149,25 @@ dummy_device_probe(struct output_device *device, output_status_cb cb) } static int -dummy_device_volume_set(struct output_device *device, output_status_cb cb) +dummy_device_volume_set(struct output_device *device, int callback_id) { struct dummy_session *ds = device->session; if (!ds) return 0; - ds->status_cb = cb; + ds->callback_id = callback_id; dummy_status(ds); return 1; } static void -dummy_device_set_cb(struct output_device *device, output_status_cb cb) +dummy_device_cb_set(struct output_device *device, int callback_id) { struct dummy_session *ds = device->session; - ds->status_cb = cb; + ds->callback_id = callback_id; } static void @@ -234,12 +197,7 @@ dummy_init(void) nickname = cfg_getstr(cfg_audio, "nickname"); - device = calloc(1, sizeof(struct output_device)); - if (!device) - { - DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy device\n"); - return -1; - } + CHECK_NULL(L_LAUDIO, device = calloc(1, sizeof(struct output_device))); device->id = 0; device->name = strdup(nickname); @@ -273,6 +231,6 @@ struct output_definition output_dummy = .device_stop = dummy_device_stop, .device_probe = dummy_device_probe, .device_volume_set = dummy_device_volume_set, - .device_set_cb = dummy_device_set_cb, + .device_cb_set = dummy_device_cb_set, .playback_stop = dummy_playback_stop, }; diff --git a/src/outputs/fifo.c b/src/outputs/fifo.c index f0660772..ccb31e9b 100644 --- a/src/outputs/fifo.c +++ b/src/outputs/fifo.c @@ -31,8 +31,6 @@ #include #include -#include - #include "misc.h" #include "conffile.h" #include "logger.h" @@ -95,22 +93,12 @@ struct fifo_session int created; - struct event *deferredev; - output_status_cb defer_cb; - - struct output_device *device; - output_status_cb status_cb; + uint64_t device_id; + int callback_id; }; -/* From player.c */ -extern struct event_base *evbase_player; - static struct fifo_session *sessions; -/* Forwards */ -static void -defer_cb(int fd, short what, void *arg); - /* ---------------------------- FIFO HANDLING ---------------------------- */ @@ -242,8 +230,6 @@ fifo_session_free(struct fifo_session *fifo_session) if (!fifo_session) return; - event_free(fifo_session->deferredev); - free(fifo_session); free_buffer(); } @@ -254,29 +240,21 @@ fifo_session_cleanup(struct fifo_session *fifo_session) // Normally some here code to remove from linked list - here we just say: sessions = NULL; - fifo_session->device->session = NULL; + outputs_device_session_remove(fifo_session->device_id); fifo_session_free(fifo_session); } static struct fifo_session * -fifo_session_make(struct output_device *device, output_status_cb cb) +fifo_session_make(struct output_device *device, int callback_id) { struct fifo_session *fifo_session; CHECK_NULL(L_FIFO, fifo_session = calloc(1, sizeof(struct fifo_session))); - fifo_session->deferredev = evtimer_new(evbase_player, defer_cb, fifo_session); - if (!fifo_session->deferredev) - { - DPRINTF(E_LOG, L_FIFO, "Out of memory for fifo deferred event\n"); - free(fifo_session); - return NULL; - } - fifo_session->state = OUTPUT_STATE_CONNECTED; - fifo_session->device = device; - fifo_session->status_cb = cb; + fifo_session->device_id = device->id; + fifo_session->callback_id = callback_id; fifo_session->created = 0; fifo_session->path = device->extra_device_info; @@ -285,7 +263,7 @@ fifo_session_make(struct output_device *device, output_status_cb cb) sessions = fifo_session; - device->session = fifo_session; + outputs_device_session_add(device->id, fifo_session); return fifo_session; } @@ -293,33 +271,19 @@ fifo_session_make(struct output_device *device, output_status_cb cb) /* ---------------------------- STATUS HANDLERS ----------------------------- */ -// Maps our internal state to the generic output state and then makes a callback -// to the player to tell that state -static void -defer_cb(int fd, short what, void *arg) -{ - struct fifo_session *ds = arg; - - if (ds->defer_cb) - ds->defer_cb(ds->device, ds->state); - - if (ds->state == OUTPUT_STATE_STOPPED) - fifo_session_cleanup(ds); -} - static void fifo_status(struct fifo_session *fifo_session) { - fifo_session->defer_cb = fifo_session->status_cb; - event_active(fifo_session->deferredev, 0, 0); - fifo_session->status_cb = NULL; -} + outputs_cb(fifo_session->callback_id, fifo_session->device_id, fifo_session->state); + if (fifo_session->state == OUTPUT_STATE_STOPPED) + fifo_session_cleanup(fifo_session); +} /* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */ static int -fifo_device_start(struct output_device *device, output_status_cb cb) +fifo_device_start(struct output_device *device, int callback_id) { struct fifo_session *fifo_session; int ret; @@ -328,7 +292,7 @@ fifo_device_start(struct output_device *device, output_status_cb cb) if (ret < 0) return -1; - fifo_session = fifo_session_make(device, cb); + fifo_session = fifo_session_make(device, callback_id); if (!fifo_session) return -1; @@ -342,13 +306,13 @@ fifo_device_start(struct output_device *device, output_status_cb cb) } static int -fifo_device_stop(struct output_device *device, output_status_cb cb) +fifo_device_stop(struct output_device *device, int callback_id) { struct fifo_session *fifo_session = device->session; outputs_quality_unsubscribe(&fifo_quality); - fifo_session->status_cb = cb; + fifo_session->callback_id = callback_id; fifo_close(fifo_session); free_buffer(); @@ -360,12 +324,12 @@ fifo_device_stop(struct output_device *device, output_status_cb cb) } static int -fifo_device_probe(struct output_device *device, output_status_cb cb) +fifo_device_probe(struct output_device *device, int callback_id) { struct fifo_session *fifo_session; int ret; - fifo_session = fifo_session_make(device, cb); + fifo_session = fifo_session_make(device, callback_id); if (!fifo_session) return -1; @@ -378,7 +342,7 @@ fifo_device_probe(struct output_device *device, output_status_cb cb) fifo_close(fifo_session); - fifo_session->status_cb = cb; + fifo_session->callback_id = callback_id; fifo_session->state = OUTPUT_STATE_STOPPED; fifo_status(fifo_session); @@ -387,25 +351,25 @@ fifo_device_probe(struct output_device *device, output_status_cb cb) } static int -fifo_device_volume_set(struct output_device *device, output_status_cb cb) +fifo_device_volume_set(struct output_device *device, int callback_id) { struct fifo_session *fifo_session = device->session; if (!fifo_session) return 0; - fifo_session->status_cb = cb; + fifo_session->callback_id = callback_id; fifo_status(fifo_session); return 1; } static void -fifo_device_set_cb(struct output_device *device, output_status_cb cb) +fifo_device_cb_set(struct output_device *device, int callback_id) { struct fifo_session *fifo_session = device->session; - fifo_session->status_cb = cb; + fifo_session->callback_id = callback_id; } static void @@ -423,7 +387,7 @@ fifo_playback_stop(void) } static int -fifo_flush(output_status_cb cb) +fifo_flush(int callback_id) { struct fifo_session *fifo_session = sessions; @@ -433,7 +397,7 @@ fifo_flush(output_status_cb cb) fifo_empty(fifo_session); free_buffer(); - fifo_session->status_cb = cb; + fifo_session->callback_id = callback_id; fifo_session->state = OUTPUT_STATE_CONNECTED; fifo_status(fifo_session); return 1; @@ -448,7 +412,7 @@ fifo_write(struct output_buffer *obuf) ssize_t bytes; int i; - if (!fifo_session || !fifo_session->device->selected) + if (!fifo_session) return; for (i = 0; obuf->frames[i].buffer; i++) @@ -535,12 +499,7 @@ fifo_init(void) memset(&buffer, 0, sizeof(struct fifo_buffer)); - device = calloc(1, sizeof(struct output_device)); - if (!device) - { - DPRINTF(E_LOG, L_FIFO, "Out of memory for fifo device\n"); - return -1; - } + CHECK_NULL(L_FIFO, device = calloc(1, sizeof(struct output_device))); device->id = 100; device->name = strdup(nickname); @@ -574,7 +533,7 @@ struct output_definition output_fifo = .device_stop = fifo_device_stop, .device_probe = fifo_device_probe, .device_volume_set = fifo_device_volume_set, - .device_set_cb = fifo_device_set_cb, + .device_cb_set = fifo_device_cb_set, .playback_stop = fifo_playback_stop, .write = fifo_write, .flush = fifo_flush, diff --git a/src/outputs/pulse.c b/src/outputs/pulse.c index 0a962105..2bd455d7 100644 --- a/src/outputs/pulse.c +++ b/src/outputs/pulse.c @@ -160,7 +160,7 @@ pulse_session_make(struct output_device *device, output_status_cb cb) ps->next = sessions; sessions = ps; - device->session = ps; + outputs_device_session_add(device, ps); return ps; } diff --git a/src/outputs/raop.c b/src/outputs/raop.c index f8fb64e8..512b6578 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 Espen Jürgensen + * Copyright (C) 2012-2019 Espen Jürgensen * Copyright (C) 2010-2011 Julien BLACHE * * RAOP AirTunes v2 @@ -180,6 +180,9 @@ struct raop_master_session struct raop_session { + uint64_t device_id; + int callback_id; + struct raop_master_session *master_session; struct evrtsp_connection *ctrl; @@ -212,9 +215,6 @@ struct raop_session int volume; uint64_t start_rtptime; - struct output_device *device; - output_status_cb status_cb; - /* AirTunes v2 */ unsigned short server_port; unsigned short control_port; @@ -355,7 +355,7 @@ static struct raop_session *raop_sessions; // Forwards static int -raop_device_start(struct output_device *rd, output_status_cb cb); +raop_device_start(struct output_device *rd, int callback_id); /* ------------------------------- MISC HELPERS ----------------------------- */ @@ -1089,7 +1089,7 @@ raop_add_headers(struct raop_session *rs, struct evrtsp_request *req, enum evrts // We set Active-Remote as 32 bit unsigned decimal, as at least my device // can't handle any larger. Must be aligned with volume_byactiveremote(). - snprintf(buf, sizeof(buf), "%" PRIu32, (uint32_t)rs->device->id); + snprintf(buf, sizeof(buf), "%" PRIu32, (uint32_t)rs->device_id); evrtsp_add_header(req->output_headers, "Active-Remote", buf); if (rs->session) @@ -1725,7 +1725,6 @@ raop_send_req_pin_start(struct raop_session *rs, evrtsp_req_cb cb, const char *l static void raop_status(struct raop_session *rs) { - output_status_cb status_cb = rs->status_cb; enum output_device_state state; switch (rs->state) @@ -1749,16 +1748,16 @@ raop_status(struct raop_session *rs) state = OUTPUT_STATE_STREAMING; break; default: - DPRINTF(E_LOG, L_RAOP, "Bug! Unhandled state in cast_status()\n"); + DPRINTF(E_LOG, L_RAOP, "Bug! Unhandled state in raop_status()\n"); state = OUTPUT_STATE_FAILED; } - rs->status_cb = NULL; - if (status_cb) - status_cb(rs->device, state); + outputs_cb(rs->callback_id, rs->device_id, state); + rs->callback_id = -1; + // Ugly... fixme... if (rs->state == RAOP_STATE_UNVERIFIED) - player_speaker_status_trigger(); + outputs_listener_notify(); } static struct raop_master_session * @@ -1895,7 +1894,7 @@ session_cleanup(struct raop_session *rs) s->next = rs->next; } - rs->device->session = NULL; + outputs_device_session_remove(rs->device_id); session_free(rs); } @@ -1947,23 +1946,17 @@ static void session_teardown_cb(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; - struct output_device *rd = rs->device; - output_status_cb status_cb = rs->status_cb; rs->reqs_in_flight--; if (!req || req->response_code != RTSP_OK) DPRINTF(E_LOG, L_RAOP, "TEARDOWN request failed in session shutdown: %d %s\n", req->response_code, req->response_code_line); - // Clean up session before giving status to the user (good to get rid of it if he - // calls back into us - e.g. tries to make a new session in the callback) + rs->state = OUTPUT_STATE_STOPPED; + + raop_status(rs); + session_cleanup(rs); - - // Can't use raop_status() here, sadly - if (status_cb) - status_cb(rd, OUTPUT_STATE_STOPPED); - - return; } static int @@ -1982,7 +1975,7 @@ session_teardown(struct raop_session *rs, const char *log_caller) } static struct raop_session * -session_make(struct output_device *rd, int family, output_status_cb cb, bool only_probe) +session_make(struct output_device *rd, int family, int callback_id, bool only_probe) { struct raop_session *rs; struct raop_extra *re; @@ -2023,8 +2016,9 @@ session_make(struct output_device *rd, int family, output_status_cb cb, bool onl rs->reqs_in_flight = 0; rs->cseq = 1; - rs->device = rd; - rs->status_cb = cb; + rs->device_id = rd->id; + rs->callback_id = callback_id; + rs->server_fd = -1; rs->password = rd->password; @@ -2151,7 +2145,7 @@ session_make(struct output_device *rd, int family, output_status_cb cb, bool onl raop_sessions = rs; // rs is now the official device session - rd->session = rs; + outputs_device_session_add(rd->id, rs); return rs; @@ -2323,7 +2317,7 @@ raop_cb_metadata(struct evrtsp_request *req, void *arg) if (ret < 0) goto error; - /* No status_cb call, user doesn't want/need to know about the status + /* No callback to player, user doesn't want/need to know about the status * of metadata requests unless they cause the session to fail. */ @@ -2730,15 +2724,15 @@ raop_cb_set_volume(struct evrtsp_request *req, void *arg) /* Volume in [0 - 100] */ static int -raop_set_volume_one(struct output_device *rd, output_status_cb cb) +raop_set_volume_one(struct output_device *device, int callback_id) { - struct raop_session *rs = rd->session; + struct raop_session *rs = device->session; int ret; if (!rs || !(rs->state & RAOP_STATE_F_CONNECTED)) return 0; - ret = raop_set_volume_internal(rs, rd->volume, raop_cb_set_volume); + ret = raop_set_volume_internal(rs, device->volume, raop_cb_set_volume); if (ret < 0) { session_failure(rs); @@ -2746,7 +2740,7 @@ raop_set_volume_one(struct output_device *rd, output_status_cb cb) return 0; } - rs->status_cb = cb; + rs->callback_id = callback_id; return 1; } @@ -3557,11 +3551,18 @@ static void raop_cb_startup_retry(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; - struct output_device *rd = rs->device; - output_status_cb cb = rs->status_cb; + struct output_device *device; + int callback_id = rs->callback_id; + + device = outputs_device_get(rs->device_id); + if (!device) + { + session_failure(rs); + return; + } session_cleanup(rs); - raop_device_start(rd, cb); + raop_device_start(device, callback_id); } static void @@ -3575,11 +3576,11 @@ raop_cb_startup_cancel(struct evrtsp_request *req, void *arg) static void raop_startup_cancel(struct raop_session *rs) { - struct output_device *rd = rs->device; - output_status_cb cb; + struct output_device *device; int ret; - if (!rs->session) + device = outputs_device_get(rs->device_id); + if (!device || !rs->session) { session_failure(rs); return; @@ -3590,17 +3591,12 @@ raop_startup_cancel(struct raop_session *rs) if (rs->family == AF_INET6 && !(rs->state & RAOP_STATE_F_FAILED)) { // This flag is permanent and will not be overwritten by mdns advertisements - rd->v6_disabled = 1; + device->v6_disabled = 1; // Stop current session and wait for call back ret = raop_send_req_teardown(rs, raop_cb_startup_retry, "startup_cancel"); if (ret < 0) - { - // No connection at all, lets clean up and try again - cb = rs->status_cb; - session_cleanup(rs); - raop_device_start(rd, cb); - } + raop_cb_startup_retry(NULL, rs); // No connection at all, call retry directly return; } @@ -3995,6 +3991,7 @@ static void raop_cb_startup_options(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; + struct output_device *device; const char *param; int ret; @@ -4044,7 +4041,11 @@ raop_cb_startup_options(struct evrtsp_request *req, void *arg) if (req->response_code == RTSP_FORBIDDEN) { - rs->device->requires_auth = 1; + device = outputs_device_get(rs->device_id); + if (!device) + goto cleanup; + + device->requires_auth = 1; ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "startup_options"); if (ret < 0) @@ -4251,6 +4252,7 @@ static void raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; + struct output_device *device; int ret; verification_verify_free(rs->verification_verify_ctx); @@ -4258,9 +4260,13 @@ raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg) ret = raop_verification_response_process(5, req, rs); if (ret < 0) { + device = outputs_device_get(rs->device_id); + if (!device) + goto error; + // Clear auth_key, the device did not accept it - free(rs->device->auth_key); - rs->device->auth_key = NULL; + free(device->auth_key); + device->auth_key = NULL; goto error; } @@ -4270,7 +4276,7 @@ raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg) raop_send_req_options(rs, raop_cb_startup_options, "verify_step2"); - player_speaker_status_trigger(); + outputs_listener_notify(); return; @@ -4283,14 +4289,19 @@ static void raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; + struct output_device *device; int ret; ret = raop_verification_response_process(4, req, rs); if (ret < 0) { + device = outputs_device_get(rs->device_id); + if (!device) + goto error; + // Clear auth_key, the device did not accept it - free(rs->device->auth_key); - rs->device->auth_key = NULL; + free(device->auth_key); + device->auth_key = NULL; goto error; } @@ -4311,14 +4322,14 @@ raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg) static int raop_verification_verify(struct raop_session *rs) { + struct output_device *device; int ret; - rs->verification_verify_ctx = verification_verify_new(rs->device->auth_key); // Naughty boy is dereferencing device - if (!rs->verification_verify_ctx) - { - DPRINTF(E_LOG, L_RAOP, "Out of memory for verification verify context\n"); - return -1; - } + device = outputs_device_get(rs->device_id); + if (!device) + goto error; + + CHECK_NULL(L_RAOP, rs->verification_verify_ctx = verification_verify_new(device->auth_key)); ret = raop_verification_request_send(4, rs, raop_cb_verification_verify_step1); if (ret < 0) @@ -4340,6 +4351,7 @@ static void raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg) { struct raop_session *rs = arg; + struct output_device *device; const char *authorization_key; int ret; @@ -4356,11 +4368,15 @@ raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg) DPRINTF(E_LOG, L_RAOP, "Verification setup stage complete, saving authorization key\n"); - // Dereferencing output_device and a blocking db call... :-~ - free(rs->device->auth_key); - rs->device->auth_key = strdup(authorization_key); + device = outputs_device_get(rs->device_id); + if (!device) + goto error; - db_speaker_save(rs->device); + free(device->auth_key); + device->auth_key = strdup(authorization_key); + + // A blocking db call... :-~ + db_speaker_save(device); // The player considers this session failed, so we don't need it any more session_cleanup(rs); @@ -4734,7 +4750,7 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha /* Thread: player */ static int -raop_device_start_generic(struct output_device *rd, output_status_cb cb, bool only_probe) +raop_device_start_generic(struct output_device *device, int callback_id, bool only_probe) { struct raop_session *rs; int ret; @@ -4744,12 +4760,12 @@ raop_device_start_generic(struct output_device *rd, output_status_cb cb, bool on * address and build our session URL for all subsequent requests. */ - rs = session_make(rd, AF_INET6, cb, only_probe); + rs = session_make(device, AF_INET6, callback_id, only_probe); if (rs) { - if (rd->auth_key) + if (device->auth_key) ret = raop_verification_verify(rs); - else if (rd->requires_auth) + else if (device->requires_auth) ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start"); else ret = raop_send_req_options(rs, raop_cb_startup_options, "device_start"); @@ -4763,13 +4779,13 @@ raop_device_start_generic(struct output_device *rd, output_status_cb cb, bool on } } - rs = session_make(rd, AF_INET, cb, only_probe); + rs = session_make(device, AF_INET, callback_id, only_probe); if (!rs) return -1; - if (rd->auth_key) + if (device->auth_key) ret = raop_verification_verify(rs); - else if (rd->requires_auth) + else if (device->requires_auth) ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start"); else ret = raop_send_req_options(rs, raop_cb_startup_options, "device_start"); @@ -4785,41 +4801,41 @@ raop_device_start_generic(struct output_device *rd, output_status_cb cb, bool on } static int -raop_device_probe(struct output_device *rd, output_status_cb cb) +raop_device_probe(struct output_device *device, int callback_id) { - return raop_device_start_generic(rd, cb, 1); + return raop_device_start_generic(device, callback_id, 1); } static int -raop_device_start(struct output_device *rd, output_status_cb cb) +raop_device_start(struct output_device *device, int callback_id) { - return raop_device_start_generic(rd, cb, 0); + return raop_device_start_generic(device, callback_id, 0); } static int -raop_device_stop(struct output_device *rd, output_status_cb cb) +raop_device_stop(struct output_device *device, int callback_id) { - struct raop_session *rs = rd->session; + struct raop_session *rs = device->session; - rs->status_cb = cb; + rs->callback_id = callback_id; return session_teardown(rs, "device_stop"); } static void -raop_device_free_extra(struct output_device *rd) +raop_device_cb_set(struct output_device *device, int callback_id) { - struct raop_extra *re = rd->extra_device_info; + struct raop_session *rs = device->session; - free(re); + rs->callback_id = callback_id; } static void -raop_device_set_cb(struct output_device *rd, output_status_cb cb) +raop_device_free_extra(struct output_device *device) { - struct raop_session *rs = rd->session; + struct raop_extra *re = device->extra_device_info; - rs->status_cb = cb; + free(re); } static void @@ -4879,7 +4895,7 @@ raop_write(struct output_buffer *obuf) } static int -raop_flush(output_status_cb cb) +raop_flush(int callback_id) { struct timeval tv; struct raop_session *rs; @@ -4902,7 +4918,7 @@ raop_flush(output_status_cb cb) continue; } - rs->status_cb = cb; + rs->callback_id = callback_id; pending++; } @@ -5085,10 +5101,10 @@ struct output_definition output_raop = .device_start = raop_device_start, .device_stop = raop_device_stop, .device_probe = raop_device_probe, + .device_cb_set = raop_device_cb_set, .device_free_extra = raop_device_free_extra, .device_volume_set = raop_set_volume_one, .device_volume_to_pct = raop_volume_to_pct, - .device_set_cb = raop_device_set_cb, .playback_stop = raop_playback_stop, .write = raop_write, .flush = raop_flush, diff --git a/src/player.c b/src/player.c index 2f66411f..6e474fd6 100644 --- a/src/player.c +++ b/src/player.c @@ -256,9 +256,6 @@ static uint64_t pb_pos; // Stream position (packets) static uint64_t last_rtptime; -// Output devices -static struct output_device *dev_list; - // Output status static int output_sessions; @@ -326,7 +323,7 @@ volume_master_update(int newvol) master_volume = newvol; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->selected) device->relvol = vol_to_rel(device->volume); @@ -341,7 +338,7 @@ volume_master_find(void) newmaster = -1; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->selected && (device->volume > newmaster)) newmaster = device->volume; @@ -1219,14 +1216,14 @@ device_list_sort(void) { swaps = 0; prev = NULL; - for (device = dev_list; device && device->next; device = device->next) + 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 == dev_list) - dev_list = next; + if (device == output_device_list) + output_device_list = next; if (prev) prev->next = next; @@ -1248,7 +1245,7 @@ device_remove(struct output_device *remove) int ret; prev = NULL; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device == remove) break; @@ -1271,7 +1268,7 @@ device_remove(struct output_device *remove) speaker_deselect_output(remove); if (!prev) - dev_list = remove->next; + output_device_list = remove->next; else prev->next = remove->next; @@ -1283,7 +1280,7 @@ device_check(struct output_device *check) { struct output_device *device; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device == check) break; @@ -1304,7 +1301,7 @@ device_add(void *arg, int *retval) cmdarg = arg; add = cmdarg->device; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->id == add->id) break; @@ -1331,8 +1328,8 @@ device_add(void *arg, int *retval) else device->selected = 0; - device->next = dev_list; - dev_list = device; + device->next = output_device_list; + output_device_list = device; } // Update to a device already in the list else @@ -1392,7 +1389,7 @@ device_remove_family(void *arg, int *retval) cmdarg = arg; remove = cmdarg->device; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->id == remove->id) break; @@ -1512,7 +1509,7 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu device_remove(device); } else - outputs_device_set_cb(device, device_streaming_cb); + outputs_device_cb_set(device, device_streaming_cb); } static void @@ -1520,7 +1517,7 @@ device_command_cb(struct output_device *device, enum output_device_state status) { DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_command_cb (status %d)\n", outputs_name(device->type), status); - outputs_device_set_cb(device, device_streaming_cb); + outputs_device_cb_set(device, device_streaming_cb); if (status == OUTPUT_STATE_FAILED) device_streaming_cb(device, status); @@ -1622,7 +1619,7 @@ device_activate_cb(struct output_device *device, enum output_device_state status output_sessions++; - outputs_device_set_cb(device, device_streaming_cb); + outputs_device_cb_set(device, device_streaming_cb); out: /* cur_cmd->ret already set @@ -1719,12 +1716,32 @@ device_restart_cb(struct output_device *device, enum output_device_state status) } output_sessions++; - outputs_device_set_cb(device, device_streaming_cb); + outputs_device_cb_set(device, device_streaming_cb); out: commands_exec_end(cmdbase, retval); } +const char * +player_pmap(void *p) +{ + if (p == device_restart_cb) + return "device_restart_cb"; + else if (p == device_probe_cb) + return "device_probe_cb"; + else if (p == device_activate_cb) + 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) + return "device_shutdown_cb"; + else + return "unknown"; +} /* ------------------------- Internal playback routines --------------------- */ @@ -2092,7 +2109,7 @@ playback_start_item(void *arg, int *retval) // Start sessions on selected devices *retval = 0; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->selected && !device->session) { @@ -2110,7 +2127,7 @@ playback_start_item(void *arg, int *retval) // If autoselecting is enabled, try to autoselect a non-selected device if the above failed if (speaker_autoselect && (*retval == 0) && (output_sessions == 0)) - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if ((outputs_priority(device) == 0) || device->session) continue; @@ -2434,15 +2451,6 @@ playback_pause(void *arg, int *retval) return COMMAND_END; } -/* - * Notify of speaker/device changes - */ -void -player_speaker_status_trigger(void) -{ - listener_notify(LISTENER_SPEAKER); -} - static void device_to_speaker_info(struct player_speaker_info *spk, struct output_device *device) { @@ -2469,7 +2477,7 @@ speaker_enumerate(void *arg, int *retval) struct output_device *device; struct player_speaker_info spk; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->advertised || device->selected) { @@ -2592,7 +2600,7 @@ speaker_set(void *arg, int *retval) *retval = 0; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { for (i = 1; i <= nspk; i++) { @@ -2639,7 +2647,7 @@ speaker_enable(void *arg, int *retval) *retval = 0; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (*id == device->id) { @@ -2666,7 +2674,7 @@ speaker_disable(void *arg, int *retval) *retval = 0; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (*id == device->id) { @@ -2696,7 +2704,7 @@ volume_set(void *arg, int *retval) master_volume = volume; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (!device->selected) continue; @@ -2726,7 +2734,7 @@ static void debug_print_speaker() DPRINTF(E_DBG, L_PLAYER, "*** Master: %d\n", master_volume); - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (!device->selected) continue; @@ -2748,7 +2756,7 @@ volume_setrel_speaker(void *arg, int *retval) id = vol_param->spk_id; relvol = vol_param->volume; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (device->id != id) continue; @@ -2799,7 +2807,7 @@ volume_setabs_speaker(void *arg, int *retval) master_volume = volume; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if (!device->selected) continue; @@ -2853,7 +2861,7 @@ volume_byactiveremote(void *arg, int *retval) *retval = 0; activeremote = ar_param->activeremote; - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { if ((uint32_t)device->id == activeremote) break; @@ -3456,7 +3464,7 @@ player(void *arg) db_speaker_clear_all(); - for (device = dev_list; device; device = device->next) + for (device = output_device_list; device; device = device->next) { ret = db_speaker_save(device); if (ret < 0) diff --git a/src/player.h b/src/player.h index 3dc6eee4..525d2f03 100644 --- a/src/player.h +++ b/src/player.h @@ -93,9 +93,6 @@ player_speaker_enable(uint64_t id); int player_speaker_disable(uint64_t id); -void -player_speaker_status_trigger(void); - int player_playback_start(void); @@ -163,6 +160,9 @@ player_raop_verification_kickoff(char **arglist); void player_metadata_send(void *imd, void *omd); +const char * +player_pmap(void *p); + int player_init(void);