[outputs] Refactor outputs some more, eg change callback system

This commit is contained in:
ejurgensen 2019-02-12 21:25:27 +01:00
parent e99f20992e
commit a7e8476996
11 changed files with 589 additions and 418 deletions

View File

@ -267,9 +267,6 @@ input_write(struct evbuffer *evbuf, struct media_quality *quality, short flags)
return 0; 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)) if (quality && !quality_is_equal(quality, &input_buffer.cur_write_quality))
{ {
input_buffer.cur_write_quality = *quality; input_buffer.cur_write_quality = *quality;

View File

@ -29,9 +29,13 @@
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <event2/event.h>
#include "logger.h" #include "logger.h"
#include "misc.h" #include "misc.h"
#include "transcode.h" #include "transcode.h"
#include "listener.h"
#include "player.h" //TODO remove me when player_pmap is removed again
#include "outputs.h" #include "outputs.h"
extern struct output_definition output_raop; extern struct output_definition output_raop;
@ -48,6 +52,9 @@ extern struct output_definition output_pulse;
extern struct output_definition output_cast; extern struct output_definition output_cast;
#endif #endif
/* From player.c */
extern struct event_base *evbase_player;
// Must be in sync with enum output_types // Must be in sync with enum output_types
static struct output_definition *outputs[] = { static struct output_definition *outputs[] = {
&output_raop, &output_raop,
@ -66,6 +73,24 @@ static struct output_definition *outputs[] = {
NULL 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 struct output_quality_subscription
{ {
int count; int count;
@ -79,6 +104,115 @@ static bool output_got_new_subscription;
/* ------------------------------- MISC HELPERS ----------------------------- */ /* ------------------------------- 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 static enum transcode_profile
quality_to_xcode(struct media_quality *quality) quality_to_xcode(struct media_quality *quality)
{ {
@ -206,6 +340,155 @@ buffer_drain(struct output_buffer *obuf)
/* ----------------------------------- API ---------------------------------- */ /* ----------------------------------- 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 int
outputs_device_start(struct output_device *device, output_status_cb cb) 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 -1;
} }
return outputs[device->type]->device_start(device, cb); return outputs[device->type]->device_start(device, callback_add(device, cb));
} }
int int
@ -233,7 +516,7 @@ outputs_device_stop(struct output_device *device, output_status_cb cb)
return -1; return -1;
} }
return outputs[device->type]->device_stop(device, cb); return outputs[device->type]->device_stop(device, callback_add(device, cb));
} }
int int
@ -248,7 +531,46 @@ outputs_device_probe(struct output_device *device, output_status_cb cb)
return -1; 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 void
@ -274,52 +596,6 @@ outputs_device_free(struct output_device *device)
free(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 void
outputs_playback_stop(void) outputs_playback_stop(void)
{ {
@ -363,11 +639,13 @@ outputs_flush(output_status_cb cb)
ret = 0; ret = 0;
for (i = 0; outputs[i]; i++) for (i = 0; outputs[i]; i++)
{ {
if (outputs[i]->disabled) if (outputs[i]->disabled || !outputs[i]->flush)
continue; continue;
if (outputs[i]->flush) // Clear callback register for all devices belonging to outputs[i]
ret += outputs[i]->flush(cb); callback_remove_all(outputs[i]->type);
ret += outputs[i]->flush(callback_add(NULL, cb));
} }
return ret; return ret;
@ -489,77 +767,6 @@ outputs_authorize(enum output_types type, const char *pin)
outputs[type]->authorize(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 int
outputs_priority(struct output_device *device) outputs_priority(struct output_device *device)
{ {
@ -579,6 +786,8 @@ outputs_init(void)
int ret; int ret;
int i; int i;
CHECK_NULL(L_PLAYER, outputs_deferredev = evtimer_new(evbase_player, deferred_cb, NULL));
no_output = 1; no_output = 1;
for (i = 0; outputs[i]; i++) for (i = 0; outputs[i]; i++)
{ {
@ -615,6 +824,8 @@ outputs_deinit(void)
{ {
int i; int i;
evtimer_del(outputs_deferredev);
for (i = 0; outputs[i]; i++) for (i = 0; outputs[i]; i++)
{ {
if (outputs[i]->disabled) if (outputs[i]->disabled)

View File

@ -21,7 +21,6 @@
* Here is the sequence of commands from the player to the outputs, and the * 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 * callback from the output once the command has been executed. Commands marked
* with * may make multiple callbacks if multiple sessions are affected. * with * may make multiple callbacks if multiple sessions are affected.
* (TODO should callbacks always be deferred?)
* *
* PLAYER OUTPUT PLAYER CB * PLAYER OUTPUT PLAYER CB
* speaker_activate -> device_start -> device_activate_cb * speaker_activate -> device_start -> device_activate_cb
@ -150,6 +149,7 @@ struct output_metadata
{ {
enum output_types type; enum output_types type;
void *metadata; void *metadata;
struct output_metadata *next; struct output_metadata *next;
}; };
@ -169,7 +169,6 @@ struct output_buffer
struct output_frame frames[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 1]; struct output_frame frames[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 1];
} output_buffer; } output_buffer;
typedef void (*output_status_cb)(struct output_device *device, enum output_device_state status); typedef void (*output_status_cb)(struct output_device *device, enum output_device_state status);
struct output_definition struct output_definition
@ -195,38 +194,37 @@ struct output_definition
void (*deinit)(void); void (*deinit)(void);
// Prepare a playback session on device and call back // 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 // 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 // Test the connection to a device and call back
int (*device_probe)(struct output_device *device, output_status_cb cb); int (*device_probe)(struct output_device *device, int callback_id);
// Free the private device data
void (*device_free_extra)(struct output_device *device);
// Set the volume and call back // 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 // Convert device internal representation of volume to our pct scale
int (*device_volume_to_pct)(struct output_device *device, const char *volume); int (*device_volume_to_pct)(struct output_device *device, const char *volume);
// Request a change of quality from the device // 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 // 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 // Start/stop playback on devices that were started
void (*playback_start)(uint64_t next_pkt, struct timespec *ts);
void (*playback_stop)(void); void (*playback_stop)(void);
// Write stream data to the output devices // Write stream data to the output devices
void (*write)(struct output_buffer *buffer); void (*write)(struct output_buffer *buffer);
// Flush all sessions, the return must be number of sessions pending the flush // 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) // Authorize an output with a pin-code (probably coming from the filescanner)
void (*authorize)(const char *pin); void (*authorize)(const char *pin);
@ -238,6 +236,36 @@ struct output_definition
void (*metadata_prune)(uint64_t rtptime); 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 int
outputs_device_start(struct output_device *device, output_status_cb cb); 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 int
outputs_device_probe(struct output_device *device, output_status_cb cb); outputs_device_probe(struct output_device *device, output_status_cb cb);
void
outputs_device_free(struct output_device *device);
int int
outputs_device_volume_set(struct output_device *device, output_status_cb cb); outputs_device_volume_set(struct output_device *device, output_status_cb cb);
int int
outputs_device_volume_to_pct(struct output_device *device, const char *value); outputs_device_volume_to_pct(struct output_device *device, const char *value);
// TODO should this function have a callback?
int 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 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 void
outputs_playback_stop(void); outputs_playback_stop(void);
@ -290,12 +317,6 @@ outputs_metadata_free(struct output_metadata *omd);
void void
outputs_authorize(enum output_types type, const char *pin); 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 int
outputs_priority(struct output_device *device); outputs_priority(struct output_device *device);

View File

@ -196,7 +196,7 @@ alsa_session_make(struct output_device *device, output_status_cb cb)
as->next = sessions; as->next = sessions;
sessions = as; sessions = as;
device->session = as; outputs_device_session_add(device, as);
return as; return as;

View File

@ -1420,7 +1420,8 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
cs->next = sessions; cs->next = sessions;
sessions = cs; 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)); proto = gnutls_protocol_get_name(gnutls_protocol_get_version(cs->tls_session));

View File

@ -33,8 +33,7 @@
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <event2/event.h> #include "misc.h"
#include "conffile.h" #include "conffile.h"
#include "logger.h" #include "logger.h"
#include "player.h" #include "player.h"
@ -44,22 +43,12 @@ struct dummy_session
{ {
enum output_device_state state; enum output_device_state state;
struct event *deferredev; uint64_t device_id;
output_status_cb defer_cb; int callback_id;
struct output_device *device;
output_status_cb status_cb;
}; };
/* From player.c */
extern struct event_base *evbase_player;
struct dummy_session *sessions; struct dummy_session *sessions;
/* Forwards */
static void
defer_cb(int fd, short what, void *arg);
/* ---------------------------- SESSION HANDLING ---------------------------- */ /* ---------------------------- SESSION HANDLING ---------------------------- */
static void static void
@ -68,8 +57,6 @@ dummy_session_free(struct dummy_session *ds)
if (!ds) if (!ds)
return; return;
event_free(ds->deferredev);
free(ds); 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: // Normally some here code to remove from linked list - here we just say:
sessions = NULL; sessions = NULL;
ds->device->session = NULL; outputs_device_session_remove(ds->device_id);
dummy_session_free(ds); dummy_session_free(ds);
} }
static struct dummy_session * 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; struct dummy_session *ds;
ds = calloc(1, sizeof(struct dummy_session)); CHECK_NULL(L_LAUDIO, 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;
}
ds->state = OUTPUT_STATE_CONNECTED; ds->state = OUTPUT_STATE_CONNECTED;
ds->device = device; ds->device_id = device->id;
ds->status_cb = cb; ds->callback_id = callback_id;
sessions = ds; sessions = ds;
device->session = ds; outputs_device_session_add(device->id, ds);
return ds; return ds;
} }
@ -118,37 +92,24 @@ dummy_session_make(struct output_device *device, output_status_cb cb)
/* ---------------------------- STATUS HANDLERS ----------------------------- */ /* ---------------------------- 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 static void
dummy_status(struct dummy_session *ds) dummy_status(struct dummy_session *ds)
{ {
ds->defer_cb = ds->status_cb; outputs_cb(ds->callback_id, ds->device_id, ds->state);
event_active(ds->deferredev, 0, 0);
ds->status_cb = NULL; if (ds->state == OUTPUT_STATE_STOPPED)
dummy_session_cleanup(ds);
} }
/* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */ /* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */
static int 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; struct dummy_session *ds;
ds = dummy_session_make(device, cb); ds = dummy_session_make(device, callback_id);
if (!ds) if (!ds)
return -1; return -1;
@ -158,26 +119,28 @@ dummy_device_start(struct output_device *device, output_status_cb cb)
} }
static int 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; struct dummy_session *ds = device->session;
ds->callback_id = callback_id;
ds->state = OUTPUT_STATE_STOPPED; ds->state = OUTPUT_STATE_STOPPED;
dummy_status(ds); dummy_status(ds);
return 0; return 0;
} }
static int 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; struct dummy_session *ds;
ds = dummy_session_make(device, cb); ds = dummy_session_make(device, callback_id);
if (!ds) if (!ds)
return -1; return -1;
ds->status_cb = cb; ds->callback_id = callback_id;
ds->state = OUTPUT_STATE_STOPPED; ds->state = OUTPUT_STATE_STOPPED;
dummy_status(ds); dummy_status(ds);
@ -186,25 +149,25 @@ dummy_device_probe(struct output_device *device, output_status_cb cb)
} }
static int 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; struct dummy_session *ds = device->session;
if (!ds) if (!ds)
return 0; return 0;
ds->status_cb = cb; ds->callback_id = callback_id;
dummy_status(ds); dummy_status(ds);
return 1; return 1;
} }
static void 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; struct dummy_session *ds = device->session;
ds->status_cb = cb; ds->callback_id = callback_id;
} }
static void static void
@ -234,12 +197,7 @@ dummy_init(void)
nickname = cfg_getstr(cfg_audio, "nickname"); nickname = cfg_getstr(cfg_audio, "nickname");
device = calloc(1, sizeof(struct output_device)); CHECK_NULL(L_LAUDIO, device = calloc(1, sizeof(struct output_device)));
if (!device)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy device\n");
return -1;
}
device->id = 0; device->id = 0;
device->name = strdup(nickname); device->name = strdup(nickname);
@ -273,6 +231,6 @@ struct output_definition output_dummy =
.device_stop = dummy_device_stop, .device_stop = dummy_device_stop,
.device_probe = dummy_device_probe, .device_probe = dummy_device_probe,
.device_volume_set = dummy_device_volume_set, .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, .playback_stop = dummy_playback_stop,
}; };

View File

@ -31,8 +31,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <event2/event.h>
#include "misc.h" #include "misc.h"
#include "conffile.h" #include "conffile.h"
#include "logger.h" #include "logger.h"
@ -95,22 +93,12 @@ struct fifo_session
int created; int created;
struct event *deferredev; uint64_t device_id;
output_status_cb defer_cb; int callback_id;
struct output_device *device;
output_status_cb status_cb;
}; };
/* From player.c */
extern struct event_base *evbase_player;
static struct fifo_session *sessions; static struct fifo_session *sessions;
/* Forwards */
static void
defer_cb(int fd, short what, void *arg);
/* ---------------------------- FIFO HANDLING ---------------------------- */ /* ---------------------------- FIFO HANDLING ---------------------------- */
@ -242,8 +230,6 @@ fifo_session_free(struct fifo_session *fifo_session)
if (!fifo_session) if (!fifo_session)
return; return;
event_free(fifo_session->deferredev);
free(fifo_session); free(fifo_session);
free_buffer(); 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: // Normally some here code to remove from linked list - here we just say:
sessions = NULL; sessions = NULL;
fifo_session->device->session = NULL; outputs_device_session_remove(fifo_session->device_id);
fifo_session_free(fifo_session); fifo_session_free(fifo_session);
} }
static struct 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; struct fifo_session *fifo_session;
CHECK_NULL(L_FIFO, fifo_session = calloc(1, sizeof(struct 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->state = OUTPUT_STATE_CONNECTED;
fifo_session->device = device; fifo_session->device_id = device->id;
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
fifo_session->created = 0; fifo_session->created = 0;
fifo_session->path = device->extra_device_info; 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; sessions = fifo_session;
device->session = fifo_session; outputs_device_session_add(device->id, fifo_session);
return fifo_session; return fifo_session;
} }
@ -293,33 +271,19 @@ fifo_session_make(struct output_device *device, output_status_cb cb)
/* ---------------------------- STATUS HANDLERS ----------------------------- */ /* ---------------------------- 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 static void
fifo_status(struct fifo_session *fifo_session) fifo_status(struct fifo_session *fifo_session)
{ {
fifo_session->defer_cb = fifo_session->status_cb; outputs_cb(fifo_session->callback_id, fifo_session->device_id, fifo_session->state);
event_active(fifo_session->deferredev, 0, 0);
fifo_session->status_cb = NULL;
}
if (fifo_session->state == OUTPUT_STATE_STOPPED)
fifo_session_cleanup(fifo_session);
}
/* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */ /* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */
static int 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; struct fifo_session *fifo_session;
int ret; int ret;
@ -328,7 +292,7 @@ fifo_device_start(struct output_device *device, output_status_cb cb)
if (ret < 0) if (ret < 0)
return -1; return -1;
fifo_session = fifo_session_make(device, cb); fifo_session = fifo_session_make(device, callback_id);
if (!fifo_session) if (!fifo_session)
return -1; return -1;
@ -342,13 +306,13 @@ fifo_device_start(struct output_device *device, output_status_cb cb)
} }
static int 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; struct fifo_session *fifo_session = device->session;
outputs_quality_unsubscribe(&fifo_quality); outputs_quality_unsubscribe(&fifo_quality);
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
fifo_close(fifo_session); fifo_close(fifo_session);
free_buffer(); free_buffer();
@ -360,12 +324,12 @@ fifo_device_stop(struct output_device *device, output_status_cb cb)
} }
static int 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; struct fifo_session *fifo_session;
int ret; int ret;
fifo_session = fifo_session_make(device, cb); fifo_session = fifo_session_make(device, callback_id);
if (!fifo_session) if (!fifo_session)
return -1; return -1;
@ -378,7 +342,7 @@ fifo_device_probe(struct output_device *device, output_status_cb cb)
fifo_close(fifo_session); fifo_close(fifo_session);
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
fifo_session->state = OUTPUT_STATE_STOPPED; fifo_session->state = OUTPUT_STATE_STOPPED;
fifo_status(fifo_session); fifo_status(fifo_session);
@ -387,25 +351,25 @@ fifo_device_probe(struct output_device *device, output_status_cb cb)
} }
static int 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; struct fifo_session *fifo_session = device->session;
if (!fifo_session) if (!fifo_session)
return 0; return 0;
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
fifo_status(fifo_session); fifo_status(fifo_session);
return 1; return 1;
} }
static void 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; struct fifo_session *fifo_session = device->session;
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
} }
static void static void
@ -423,7 +387,7 @@ fifo_playback_stop(void)
} }
static int static int
fifo_flush(output_status_cb cb) fifo_flush(int callback_id)
{ {
struct fifo_session *fifo_session = sessions; struct fifo_session *fifo_session = sessions;
@ -433,7 +397,7 @@ fifo_flush(output_status_cb cb)
fifo_empty(fifo_session); fifo_empty(fifo_session);
free_buffer(); free_buffer();
fifo_session->status_cb = cb; fifo_session->callback_id = callback_id;
fifo_session->state = OUTPUT_STATE_CONNECTED; fifo_session->state = OUTPUT_STATE_CONNECTED;
fifo_status(fifo_session); fifo_status(fifo_session);
return 1; return 1;
@ -448,7 +412,7 @@ fifo_write(struct output_buffer *obuf)
ssize_t bytes; ssize_t bytes;
int i; int i;
if (!fifo_session || !fifo_session->device->selected) if (!fifo_session)
return; return;
for (i = 0; obuf->frames[i].buffer; i++) for (i = 0; obuf->frames[i].buffer; i++)
@ -535,12 +499,7 @@ fifo_init(void)
memset(&buffer, 0, sizeof(struct fifo_buffer)); memset(&buffer, 0, sizeof(struct fifo_buffer));
device = calloc(1, sizeof(struct output_device)); CHECK_NULL(L_FIFO, device = calloc(1, sizeof(struct output_device)));
if (!device)
{
DPRINTF(E_LOG, L_FIFO, "Out of memory for fifo device\n");
return -1;
}
device->id = 100; device->id = 100;
device->name = strdup(nickname); device->name = strdup(nickname);
@ -574,7 +533,7 @@ struct output_definition output_fifo =
.device_stop = fifo_device_stop, .device_stop = fifo_device_stop,
.device_probe = fifo_device_probe, .device_probe = fifo_device_probe,
.device_volume_set = fifo_device_volume_set, .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, .playback_stop = fifo_playback_stop,
.write = fifo_write, .write = fifo_write,
.flush = fifo_flush, .flush = fifo_flush,

View File

@ -160,7 +160,7 @@ pulse_session_make(struct output_device *device, output_status_cb cb)
ps->next = sessions; ps->next = sessions;
sessions = ps; sessions = ps;
device->session = ps; outputs_device_session_add(device, ps);
return ps; return ps;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2017 Espen Jürgensen <espenjurgensen@gmail.com> * Copyright (C) 2012-2019 Espen Jürgensen <espenjurgensen@gmail.com>
* Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org> * Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org>
* *
* RAOP AirTunes v2 * RAOP AirTunes v2
@ -180,6 +180,9 @@ struct raop_master_session
struct raop_session struct raop_session
{ {
uint64_t device_id;
int callback_id;
struct raop_master_session *master_session; struct raop_master_session *master_session;
struct evrtsp_connection *ctrl; struct evrtsp_connection *ctrl;
@ -212,9 +215,6 @@ struct raop_session
int volume; int volume;
uint64_t start_rtptime; uint64_t start_rtptime;
struct output_device *device;
output_status_cb status_cb;
/* AirTunes v2 */ /* AirTunes v2 */
unsigned short server_port; unsigned short server_port;
unsigned short control_port; unsigned short control_port;
@ -355,7 +355,7 @@ static struct raop_session *raop_sessions;
// Forwards // Forwards
static int 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 ----------------------------- */ /* ------------------------------- 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 // 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(). // 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); evrtsp_add_header(req->output_headers, "Active-Remote", buf);
if (rs->session) 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 static void
raop_status(struct raop_session *rs) raop_status(struct raop_session *rs)
{ {
output_status_cb status_cb = rs->status_cb;
enum output_device_state state; enum output_device_state state;
switch (rs->state) switch (rs->state)
@ -1749,16 +1748,16 @@ raop_status(struct raop_session *rs)
state = OUTPUT_STATE_STREAMING; state = OUTPUT_STATE_STREAMING;
break; break;
default: 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; state = OUTPUT_STATE_FAILED;
} }
rs->status_cb = NULL; outputs_cb(rs->callback_id, rs->device_id, state);
if (status_cb) rs->callback_id = -1;
status_cb(rs->device, state);
// Ugly... fixme...
if (rs->state == RAOP_STATE_UNVERIFIED) if (rs->state == RAOP_STATE_UNVERIFIED)
player_speaker_status_trigger(); outputs_listener_notify();
} }
static struct raop_master_session * static struct raop_master_session *
@ -1895,7 +1894,7 @@ session_cleanup(struct raop_session *rs)
s->next = rs->next; s->next = rs->next;
} }
rs->device->session = NULL; outputs_device_session_remove(rs->device_id);
session_free(rs); session_free(rs);
} }
@ -1947,23 +1946,17 @@ static void
session_teardown_cb(struct evrtsp_request *req, void *arg) session_teardown_cb(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *rd = rs->device;
output_status_cb status_cb = rs->status_cb;
rs->reqs_in_flight--; rs->reqs_in_flight--;
if (!req || req->response_code != RTSP_OK) 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); 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 rs->state = OUTPUT_STATE_STOPPED;
// calls back into us - e.g. tries to make a new session in the callback)
raop_status(rs);
session_cleanup(rs); session_cleanup(rs);
// Can't use raop_status() here, sadly
if (status_cb)
status_cb(rd, OUTPUT_STATE_STOPPED);
return;
} }
static int static int
@ -1982,7 +1975,7 @@ session_teardown(struct raop_session *rs, const char *log_caller)
} }
static struct raop_session * 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_session *rs;
struct raop_extra *re; 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->reqs_in_flight = 0;
rs->cseq = 1; rs->cseq = 1;
rs->device = rd; rs->device_id = rd->id;
rs->status_cb = cb; rs->callback_id = callback_id;
rs->server_fd = -1; rs->server_fd = -1;
rs->password = rd->password; 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; raop_sessions = rs;
// rs is now the official device session // rs is now the official device session
rd->session = rs; outputs_device_session_add(rd->id, rs);
return rs; return rs;
@ -2323,7 +2317,7 @@ raop_cb_metadata(struct evrtsp_request *req, void *arg)
if (ret < 0) if (ret < 0)
goto error; 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. * 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] */ /* Volume in [0 - 100] */
static int 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; int ret;
if (!rs || !(rs->state & RAOP_STATE_F_CONNECTED)) if (!rs || !(rs->state & RAOP_STATE_F_CONNECTED))
return 0; 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) if (ret < 0)
{ {
session_failure(rs); session_failure(rs);
@ -2746,7 +2740,7 @@ raop_set_volume_one(struct output_device *rd, output_status_cb cb)
return 0; return 0;
} }
rs->status_cb = cb; rs->callback_id = callback_id;
return 1; return 1;
} }
@ -3557,11 +3551,18 @@ static void
raop_cb_startup_retry(struct evrtsp_request *req, void *arg) raop_cb_startup_retry(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *rd = rs->device; struct output_device *device;
output_status_cb cb = rs->status_cb; int callback_id = rs->callback_id;
device = outputs_device_get(rs->device_id);
if (!device)
{
session_failure(rs);
return;
}
session_cleanup(rs); session_cleanup(rs);
raop_device_start(rd, cb); raop_device_start(device, callback_id);
} }
static void static void
@ -3575,11 +3576,11 @@ raop_cb_startup_cancel(struct evrtsp_request *req, void *arg)
static void static void
raop_startup_cancel(struct raop_session *rs) raop_startup_cancel(struct raop_session *rs)
{ {
struct output_device *rd = rs->device; struct output_device *device;
output_status_cb cb;
int ret; int ret;
if (!rs->session) device = outputs_device_get(rs->device_id);
if (!device || !rs->session)
{ {
session_failure(rs); session_failure(rs);
return; return;
@ -3590,17 +3591,12 @@ raop_startup_cancel(struct raop_session *rs)
if (rs->family == AF_INET6 && !(rs->state & RAOP_STATE_F_FAILED)) if (rs->family == AF_INET6 && !(rs->state & RAOP_STATE_F_FAILED))
{ {
// This flag is permanent and will not be overwritten by mdns advertisements // 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 // Stop current session and wait for call back
ret = raop_send_req_teardown(rs, raop_cb_startup_retry, "startup_cancel"); ret = raop_send_req_teardown(rs, raop_cb_startup_retry, "startup_cancel");
if (ret < 0) if (ret < 0)
{ raop_cb_startup_retry(NULL, rs); // No connection at all, call retry directly
// No connection at all, lets clean up and try again
cb = rs->status_cb;
session_cleanup(rs);
raop_device_start(rd, cb);
}
return; return;
} }
@ -3995,6 +3991,7 @@ static void
raop_cb_startup_options(struct evrtsp_request *req, void *arg) raop_cb_startup_options(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *device;
const char *param; const char *param;
int ret; int ret;
@ -4044,7 +4041,11 @@ raop_cb_startup_options(struct evrtsp_request *req, void *arg)
if (req->response_code == RTSP_FORBIDDEN) 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"); ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "startup_options");
if (ret < 0) if (ret < 0)
@ -4251,6 +4252,7 @@ static void
raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg) raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *device;
int ret; int ret;
verification_verify_free(rs->verification_verify_ctx); 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); ret = raop_verification_response_process(5, req, rs);
if (ret < 0) if (ret < 0)
{ {
device = outputs_device_get(rs->device_id);
if (!device)
goto error;
// Clear auth_key, the device did not accept it // Clear auth_key, the device did not accept it
free(rs->device->auth_key); free(device->auth_key);
rs->device->auth_key = NULL; device->auth_key = NULL;
goto error; 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"); raop_send_req_options(rs, raop_cb_startup_options, "verify_step2");
player_speaker_status_trigger(); outputs_listener_notify();
return; return;
@ -4283,14 +4289,19 @@ static void
raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg) raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *device;
int ret; int ret;
ret = raop_verification_response_process(4, req, rs); ret = raop_verification_response_process(4, req, rs);
if (ret < 0) if (ret < 0)
{ {
device = outputs_device_get(rs->device_id);
if (!device)
goto error;
// Clear auth_key, the device did not accept it // Clear auth_key, the device did not accept it
free(rs->device->auth_key); free(device->auth_key);
rs->device->auth_key = NULL; device->auth_key = NULL;
goto error; goto error;
} }
@ -4311,14 +4322,14 @@ raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
static int static int
raop_verification_verify(struct raop_session *rs) raop_verification_verify(struct raop_session *rs)
{ {
struct output_device *device;
int ret; int ret;
rs->verification_verify_ctx = verification_verify_new(rs->device->auth_key); // Naughty boy is dereferencing device device = outputs_device_get(rs->device_id);
if (!rs->verification_verify_ctx) if (!device)
{ goto error;
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification verify context\n");
return -1; 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); ret = raop_verification_request_send(4, rs, raop_cb_verification_verify_step1);
if (ret < 0) if (ret < 0)
@ -4340,6 +4351,7 @@ static void
raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg) raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
{ {
struct raop_session *rs = arg; struct raop_session *rs = arg;
struct output_device *device;
const char *authorization_key; const char *authorization_key;
int ret; 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"); DPRINTF(E_LOG, L_RAOP, "Verification setup stage complete, saving authorization key\n");
// Dereferencing output_device and a blocking db call... :-~ device = outputs_device_get(rs->device_id);
free(rs->device->auth_key); if (!device)
rs->device->auth_key = strdup(authorization_key); 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 // The player considers this session failed, so we don't need it any more
session_cleanup(rs); session_cleanup(rs);
@ -4734,7 +4750,7 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
/* Thread: player */ /* Thread: player */
static int 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; struct raop_session *rs;
int ret; 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. * 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 (rs)
{ {
if (rd->auth_key) if (device->auth_key)
ret = raop_verification_verify(rs); 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"); ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start");
else else
ret = raop_send_req_options(rs, raop_cb_startup_options, "device_start"); 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) if (!rs)
return -1; return -1;
if (rd->auth_key) if (device->auth_key)
ret = raop_verification_verify(rs); 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"); ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start");
else else
ret = raop_send_req_options(rs, raop_cb_startup_options, "device_start"); 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 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 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 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"); return session_teardown(rs, "device_stop");
} }
static void 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 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 static void
@ -4879,7 +4895,7 @@ raop_write(struct output_buffer *obuf)
} }
static int static int
raop_flush(output_status_cb cb) raop_flush(int callback_id)
{ {
struct timeval tv; struct timeval tv;
struct raop_session *rs; struct raop_session *rs;
@ -4902,7 +4918,7 @@ raop_flush(output_status_cb cb)
continue; continue;
} }
rs->status_cb = cb; rs->callback_id = callback_id;
pending++; pending++;
} }
@ -5085,10 +5101,10 @@ struct output_definition output_raop =
.device_start = raop_device_start, .device_start = raop_device_start,
.device_stop = raop_device_stop, .device_stop = raop_device_stop,
.device_probe = raop_device_probe, .device_probe = raop_device_probe,
.device_cb_set = raop_device_cb_set,
.device_free_extra = raop_device_free_extra, .device_free_extra = raop_device_free_extra,
.device_volume_set = raop_set_volume_one, .device_volume_set = raop_set_volume_one,
.device_volume_to_pct = raop_volume_to_pct, .device_volume_to_pct = raop_volume_to_pct,
.device_set_cb = raop_device_set_cb,
.playback_stop = raop_playback_stop, .playback_stop = raop_playback_stop,
.write = raop_write, .write = raop_write,
.flush = raop_flush, .flush = raop_flush,

View File

@ -256,9 +256,6 @@ static uint64_t pb_pos;
// Stream position (packets) // Stream position (packets)
static uint64_t last_rtptime; static uint64_t last_rtptime;
// Output devices
static struct output_device *dev_list;
// Output status // Output status
static int output_sessions; static int output_sessions;
@ -326,7 +323,7 @@ volume_master_update(int newvol)
master_volume = newvol; master_volume = newvol;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (device->selected) if (device->selected)
device->relvol = vol_to_rel(device->volume); device->relvol = vol_to_rel(device->volume);
@ -341,7 +338,7 @@ volume_master_find(void)
newmaster = -1; 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)) if (device->selected && (device->volume > newmaster))
newmaster = device->volume; newmaster = device->volume;
@ -1219,14 +1216,14 @@ device_list_sort(void)
{ {
swaps = 0; swaps = 0;
prev = NULL; 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; next = device->next;
if ( (outputs_priority(device) > outputs_priority(next)) || if ( (outputs_priority(device) > outputs_priority(next)) ||
(outputs_priority(device) == outputs_priority(next) && strcasecmp(device->name, next->name) > 0) ) (outputs_priority(device) == outputs_priority(next) && strcasecmp(device->name, next->name) > 0) )
{ {
if (device == dev_list) if (device == output_device_list)
dev_list = next; output_device_list = next;
if (prev) if (prev)
prev->next = next; prev->next = next;
@ -1248,7 +1245,7 @@ device_remove(struct output_device *remove)
int ret; int ret;
prev = NULL; prev = NULL;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (device == remove) if (device == remove)
break; break;
@ -1271,7 +1268,7 @@ device_remove(struct output_device *remove)
speaker_deselect_output(remove); speaker_deselect_output(remove);
if (!prev) if (!prev)
dev_list = remove->next; output_device_list = remove->next;
else else
prev->next = remove->next; prev->next = remove->next;
@ -1283,7 +1280,7 @@ device_check(struct output_device *check)
{ {
struct output_device *device; struct output_device *device;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (device == check) if (device == check)
break; break;
@ -1304,7 +1301,7 @@ device_add(void *arg, int *retval)
cmdarg = arg; cmdarg = arg;
add = cmdarg->device; 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) if (device->id == add->id)
break; break;
@ -1331,8 +1328,8 @@ device_add(void *arg, int *retval)
else else
device->selected = 0; device->selected = 0;
device->next = dev_list; device->next = output_device_list;
dev_list = device; output_device_list = device;
} }
// Update to a device already in the list // Update to a device already in the list
else else
@ -1392,7 +1389,7 @@ device_remove_family(void *arg, int *retval)
cmdarg = arg; cmdarg = arg;
remove = cmdarg->device; 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) if (device->id == remove->id)
break; break;
@ -1512,7 +1509,7 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu
device_remove(device); device_remove(device);
} }
else else
outputs_device_set_cb(device, device_streaming_cb); outputs_device_cb_set(device, device_streaming_cb);
} }
static void 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); 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) if (status == OUTPUT_STATE_FAILED)
device_streaming_cb(device, status); device_streaming_cb(device, status);
@ -1622,7 +1619,7 @@ device_activate_cb(struct output_device *device, enum output_device_state status
output_sessions++; output_sessions++;
outputs_device_set_cb(device, device_streaming_cb); outputs_device_cb_set(device, device_streaming_cb);
out: out:
/* cur_cmd->ret already set /* cur_cmd->ret already set
@ -1719,12 +1716,32 @@ device_restart_cb(struct output_device *device, enum output_device_state status)
} }
output_sessions++; output_sessions++;
outputs_device_set_cb(device, device_streaming_cb); outputs_device_cb_set(device, device_streaming_cb);
out: out:
commands_exec_end(cmdbase, retval); 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 --------------------- */ /* ------------------------- Internal playback routines --------------------- */
@ -2092,7 +2109,7 @@ playback_start_item(void *arg, int *retval)
// Start sessions on selected devices // Start sessions on selected devices
*retval = 0; *retval = 0;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (device->selected && !device->session) 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 autoselecting is enabled, try to autoselect a non-selected device if the above failed
if (speaker_autoselect && (*retval == 0) && (output_sessions == 0)) 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) if ((outputs_priority(device) == 0) || device->session)
continue; continue;
@ -2434,15 +2451,6 @@ playback_pause(void *arg, int *retval)
return COMMAND_END; return COMMAND_END;
} }
/*
* Notify of speaker/device changes
*/
void
player_speaker_status_trigger(void)
{
listener_notify(LISTENER_SPEAKER);
}
static void static void
device_to_speaker_info(struct player_speaker_info *spk, struct output_device *device) 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 output_device *device;
struct player_speaker_info spk; 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) if (device->advertised || device->selected)
{ {
@ -2592,7 +2600,7 @@ speaker_set(void *arg, int *retval)
*retval = 0; *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++) for (i = 1; i <= nspk; i++)
{ {
@ -2639,7 +2647,7 @@ speaker_enable(void *arg, int *retval)
*retval = 0; *retval = 0;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (*id == device->id) if (*id == device->id)
{ {
@ -2666,7 +2674,7 @@ speaker_disable(void *arg, int *retval)
*retval = 0; *retval = 0;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (*id == device->id) if (*id == device->id)
{ {
@ -2696,7 +2704,7 @@ volume_set(void *arg, int *retval)
master_volume = volume; master_volume = volume;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (!device->selected) if (!device->selected)
continue; continue;
@ -2726,7 +2734,7 @@ static void debug_print_speaker()
DPRINTF(E_DBG, L_PLAYER, "*** Master: %d\n", master_volume); 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) if (!device->selected)
continue; continue;
@ -2748,7 +2756,7 @@ volume_setrel_speaker(void *arg, int *retval)
id = vol_param->spk_id; id = vol_param->spk_id;
relvol = vol_param->volume; 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) if (device->id != id)
continue; continue;
@ -2799,7 +2807,7 @@ volume_setabs_speaker(void *arg, int *retval)
master_volume = volume; master_volume = volume;
for (device = dev_list; device; device = device->next) for (device = output_device_list; device; device = device->next)
{ {
if (!device->selected) if (!device->selected)
continue; continue;
@ -2853,7 +2861,7 @@ volume_byactiveremote(void *arg, int *retval)
*retval = 0; *retval = 0;
activeremote = ar_param->activeremote; 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) if ((uint32_t)device->id == activeremote)
break; break;
@ -3456,7 +3464,7 @@ player(void *arg)
db_speaker_clear_all(); 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); ret = db_speaker_save(device);
if (ret < 0) if (ret < 0)

View File

@ -93,9 +93,6 @@ player_speaker_enable(uint64_t id);
int int
player_speaker_disable(uint64_t id); player_speaker_disable(uint64_t id);
void
player_speaker_status_trigger(void);
int int
player_playback_start(void); player_playback_start(void);
@ -163,6 +160,9 @@ player_raop_verification_kickoff(char **arglist);
void void
player_metadata_send(void *imd, void *omd); player_metadata_send(void *imd, void *omd);
const char *
player_pmap(void *p);
int int
player_init(void); player_init(void);