mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
[outputs] Refactor outputs some more, eg change callback system
This commit is contained in:
parent
e99f20992e
commit
a7e8476996
@ -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;
|
||||||
|
457
src/outputs.c
457
src/outputs.c
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
88
src/player.c
88
src/player.c
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user