mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-27 13:29:17 -05:00
Merge branch 'speaker_selection1'
This commit is contained in:
commit
945aefaaa5
@ -47,7 +47,7 @@ general {
|
||||
|
||||
# When starting playback, autoselect speaker (if none of the previously
|
||||
# selected speakers/outputs are available)
|
||||
# speaker_autoselect = yes
|
||||
# speaker_autoselect = no
|
||||
|
||||
# Most modern systems have a high-resolution clock, but if you are on an
|
||||
# unusual platform and experience audio drop-outs, you can try changing
|
||||
|
@ -54,7 +54,7 @@ static cfg_opt_t sec_general[] =
|
||||
CFG_BOOL("ipv6", cfg_true, CFGF_NONE),
|
||||
CFG_STR("cache_path", STATEDIR "/cache/" PACKAGE "/cache.db", CFGF_NONE),
|
||||
CFG_INT("cache_daap_threshold", 1000, CFGF_NONE),
|
||||
CFG_BOOL("speaker_autoselect", cfg_true, CFGF_NONE),
|
||||
CFG_BOOL("speaker_autoselect", cfg_false, CFGF_NONE),
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
CFG_BOOL("high_resolution_clock", cfg_false, CFGF_NONE),
|
||||
#else
|
||||
|
287
src/outputs.c
287
src/outputs.c
@ -75,6 +75,9 @@ static struct output_definition *outputs[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
// Default volume (must be from 0 - 100)
|
||||
#define OUTPUTS_DEFAULT_VOLUME 50
|
||||
|
||||
// When we stop, we keep the outputs open for a while, just in case we are
|
||||
// actually just restarting. This timeout determines how long we wait before
|
||||
// full stop.
|
||||
@ -210,11 +213,13 @@ deferred_cb(int fd, short what, void *arg)
|
||||
|
||||
// The device has left the building (stopped/failed), and the backend
|
||||
// is not using it any more
|
||||
if (!device->advertised && !device->session)
|
||||
if (device && !device->advertised && !device->session)
|
||||
{
|
||||
outputs_device_remove(device);
|
||||
device = NULL;
|
||||
}
|
||||
else if (device)
|
||||
device->state = state;
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Making deferred callback to %s, id was %d\n", player_pmap(cb), callback_id);
|
||||
|
||||
@ -239,9 +244,12 @@ stop_timer_cb(int fd, short what, void *arg)
|
||||
}
|
||||
|
||||
static void
|
||||
device_stop_cb(struct output_device *device, enum output_device_state status)
|
||||
device_stop_cb(struct output_device *device, enum output_device_state state)
|
||||
{
|
||||
if (status == OUTPUT_STATE_FAILED)
|
||||
if (device)
|
||||
device->state = state;
|
||||
|
||||
if (state == OUTPUT_STATE_FAILED)
|
||||
DPRINTF(E_WARN, L_PLAYER, "Failed to stop device\n");
|
||||
else
|
||||
DPRINTF(E_INFO, L_PLAYER, "Device stopped properly\n");
|
||||
@ -406,6 +414,16 @@ device_list_sort(void)
|
||||
while (swaps > 0);
|
||||
}
|
||||
|
||||
// Convenience function
|
||||
static inline int
|
||||
device_state_update(struct output_device *device, int ret)
|
||||
{
|
||||
if (ret < 0)
|
||||
device->state = OUTPUT_STATE_FAILED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
metadata_cb_send(int fd, short what, void *arg)
|
||||
{
|
||||
@ -461,6 +479,70 @@ metadata_send(enum output_types type, uint32_t item_id, bool startup, output_met
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------- Volume helpers ----------------------------- */
|
||||
|
||||
static int
|
||||
rel_to_vol(int relvol, int master_volume)
|
||||
{
|
||||
float vol;
|
||||
|
||||
if (relvol == 100)
|
||||
return master_volume;
|
||||
|
||||
vol = ((float)relvol * (float)master_volume) / 100.0;
|
||||
|
||||
return (int)vol;
|
||||
}
|
||||
|
||||
static int
|
||||
vol_to_rel(int volume, int master_volume)
|
||||
{
|
||||
float rel;
|
||||
|
||||
if (volume == master_volume)
|
||||
return 100;
|
||||
|
||||
rel = ((float)volume / (float)master_volume) * 100.0;
|
||||
|
||||
return (int)rel;
|
||||
}
|
||||
|
||||
static void
|
||||
vol_adjust(void)
|
||||
{
|
||||
struct output_device *device;
|
||||
int selected_highest = -1;
|
||||
int all_highest = -1;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
if (device->selected && (device->volume > selected_highest))
|
||||
selected_highest = device->volume;
|
||||
|
||||
if (device->volume > all_highest)
|
||||
all_highest = device->volume;
|
||||
}
|
||||
|
||||
outputs_master_volume = (selected_highest >= 0) ? selected_highest : all_highest;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
if (!device->selected && (device->volume > outputs_master_volume))
|
||||
device->volume = outputs_master_volume;
|
||||
|
||||
device->relvol = vol_to_rel(device->volume, outputs_master_volume);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VOLUME
|
||||
DPRINTF(E_DBG, L_PLAYER, "*** Master: %d\n", outputs_master_volume);
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d selected %d\n", device->name, device->volume, device->relvol, device->selected);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ----------------------------------- API ---------------------------------- */
|
||||
|
||||
struct output_device *
|
||||
@ -474,7 +556,7 @@ outputs_device_get(uint64_t device_id)
|
||||
return device;
|
||||
}
|
||||
|
||||
DPRINTF(E_LOG, L_PLAYER, "Output device with id %" PRIu64 " has disappeared from our list\n", device_id);
|
||||
DPRINTF(E_WARN, L_PLAYER, "Output device with id %" PRIu64 " has disappeared from our list\n", device_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -613,7 +695,7 @@ outputs_listener_notify(void)
|
||||
/* ---------------------------- Called by player ---------------------------- */
|
||||
|
||||
struct output_device *
|
||||
outputs_device_add(struct output_device *add, bool new_deselect, int default_volume)
|
||||
outputs_device_add(struct output_device *add, bool new_deselect)
|
||||
{
|
||||
struct output_device *device;
|
||||
char *keep_name;
|
||||
@ -637,7 +719,7 @@ outputs_device_add(struct output_device *add, bool new_deselect, int default_vol
|
||||
if (ret < 0)
|
||||
{
|
||||
device->selected = 0;
|
||||
device->volume = default_volume;
|
||||
device->volume = (outputs_master_volume >= 0) ? outputs_master_volume : OUTPUTS_DEFAULT_VOLUME;;
|
||||
}
|
||||
|
||||
free(device->name);
|
||||
@ -686,9 +768,11 @@ outputs_device_add(struct output_device *add, bool new_deselect, int default_vol
|
||||
|
||||
device_list_sort();
|
||||
|
||||
vol_adjust();
|
||||
|
||||
device->advertised = 1;
|
||||
|
||||
listener_notify(LISTENER_SPEAKER);
|
||||
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
|
||||
|
||||
return device;
|
||||
}
|
||||
@ -732,37 +816,62 @@ outputs_device_remove(struct output_device *remove)
|
||||
|
||||
outputs_device_free(remove);
|
||||
|
||||
listener_notify(LISTENER_SPEAKER);
|
||||
vol_adjust();
|
||||
|
||||
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
|
||||
}
|
||||
|
||||
void
|
||||
outputs_device_select(struct output_device *device)
|
||||
{
|
||||
device->selected = 1;
|
||||
device->prevent_playback = 0;
|
||||
device->busy = 0;
|
||||
|
||||
vol_adjust();
|
||||
}
|
||||
|
||||
void
|
||||
outputs_device_deselect(struct output_device *device)
|
||||
{
|
||||
device->selected = 0;
|
||||
|
||||
vol_adjust();
|
||||
}
|
||||
|
||||
int
|
||||
outputs_device_start(struct output_device *device, output_status_cb cb)
|
||||
outputs_device_start(struct output_device *device, output_status_cb cb, bool only_probe)
|
||||
{
|
||||
if (outputs[device->type]->disabled || !outputs[device->type]->device_start)
|
||||
int ret;
|
||||
|
||||
if (outputs[device->type]->disabled || !outputs[device->type]->device_start || !outputs[device->type]->device_probe)
|
||||
return -1;
|
||||
|
||||
if (device->session)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_start() called for a device that already has a session\n");
|
||||
return -1;
|
||||
}
|
||||
return 0; // Device is already running, nothing to do
|
||||
|
||||
return outputs[device->type]->device_start(device, callback_add(device, cb));
|
||||
if (only_probe)
|
||||
ret = outputs[device->type]->device_probe(device, callback_add(device, cb));
|
||||
else
|
||||
ret = outputs[device->type]->device_start(device, callback_add(device, cb));
|
||||
|
||||
return device_state_update(device, ret);;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_device_stop(struct output_device *device, output_status_cb cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (outputs[device->type]->disabled || !outputs[device->type]->device_stop)
|
||||
return -1;
|
||||
|
||||
if (!device->session)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_stop() called for a device that has no session\n");
|
||||
return -1;
|
||||
}
|
||||
return 0; // Device is already stopped, nothing to do
|
||||
|
||||
return outputs[device->type]->device_stop(device, callback_add(device, cb));
|
||||
ret = outputs[device->type]->device_stop(device, callback_add(device, cb));
|
||||
|
||||
return device_state_update(device, ret);
|
||||
}
|
||||
|
||||
int
|
||||
@ -772,52 +881,58 @@ outputs_device_stop_delayed(struct output_device *device, output_status_cb cb)
|
||||
return -1;
|
||||
|
||||
if (!device->session)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_stop_delayed() called for a device that has no session\n");
|
||||
return -1;
|
||||
}
|
||||
return 0; // Device is already stopped, nothing to do
|
||||
|
||||
outputs[device->type]->device_cb_set(device, callback_add(device, cb));
|
||||
|
||||
event_add(device->stop_timer, &outputs_stop_timeout);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_device_flush(struct output_device *device, output_status_cb cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (outputs[device->type]->disabled || !outputs[device->type]->device_flush)
|
||||
return -1;
|
||||
|
||||
if (!device->session)
|
||||
return -1;
|
||||
return 0; // Nothing to flush
|
||||
|
||||
return outputs[device->type]->device_flush(device, callback_add(device, cb));
|
||||
ret = outputs[device->type]->device_flush(device, callback_add(device, cb));
|
||||
|
||||
return device_state_update(device, ret);
|
||||
}
|
||||
|
||||
int
|
||||
outputs_device_probe(struct output_device *device, output_status_cb cb)
|
||||
void
|
||||
outputs_device_volume_register(struct output_device *device, int absvol, int relvol)
|
||||
{
|
||||
if (outputs[device->type]->disabled || !outputs[device->type]->device_probe)
|
||||
return -1;
|
||||
if (absvol > -1)
|
||||
device->volume = absvol;
|
||||
else if (relvol > -1)
|
||||
device->volume = rel_to_vol(relvol, outputs_master_volume);
|
||||
|
||||
if (device->session)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_probe() called for a device that already has a session\n");
|
||||
return -1;
|
||||
}
|
||||
vol_adjust();
|
||||
|
||||
return outputs[device->type]->device_probe(device, callback_add(device, cb));
|
||||
listener_notify(LISTENER_VOLUME);
|
||||
}
|
||||
|
||||
int
|
||||
outputs_device_volume_set(struct output_device *device, output_status_cb cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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));
|
||||
if (!device->session)
|
||||
return 0; // Device isn't active
|
||||
|
||||
ret = outputs[device->type]->device_volume_set(device, callback_add(device, cb));
|
||||
|
||||
return device_state_update(device, ret);
|
||||
}
|
||||
|
||||
int
|
||||
@ -832,10 +947,14 @@ outputs_device_volume_to_pct(struct output_device *device, const char *volume)
|
||||
int
|
||||
outputs_device_quality_set(struct output_device *device, struct media_quality *quality, output_status_cb cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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));
|
||||
ret = outputs[device->type]->device_quality_set(device, quality, callback_add(device, cb));
|
||||
|
||||
return device_state_update(device, ret);
|
||||
}
|
||||
|
||||
void
|
||||
@ -876,30 +995,37 @@ outputs_device_free(struct output_device *device)
|
||||
free(device);
|
||||
}
|
||||
|
||||
// The return value will be the number of devices we need to wait for, either
|
||||
// because they are starting or shutting down. The return value is only negative
|
||||
// if we don't have to wait, i.e. all the selected devices failed immediately.
|
||||
int
|
||||
outputs_flush(output_status_cb cb)
|
||||
outputs_start(output_status_cb started_cb, output_status_cb stopped_cb, bool only_probe)
|
||||
{
|
||||
struct output_device *device;
|
||||
int count = 0;
|
||||
int ret;
|
||||
int pending = 0;
|
||||
int ret = -1;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
ret = outputs_device_flush(device, cb);
|
||||
if (device->selected)
|
||||
ret = outputs_device_start(device, started_cb, only_probe);
|
||||
else
|
||||
ret = outputs_device_stop(device, stopped_cb);
|
||||
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
pending += ret;
|
||||
}
|
||||
|
||||
return count;
|
||||
return (pending > 0) ? pending : ret;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_stop(output_status_cb cb)
|
||||
{
|
||||
struct output_device *device;
|
||||
int count = 0;
|
||||
int pending = 0;
|
||||
int ret;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
@ -911,10 +1037,10 @@ outputs_stop(output_status_cb cb)
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
pending += ret;
|
||||
}
|
||||
|
||||
return count;
|
||||
return pending;
|
||||
}
|
||||
|
||||
int
|
||||
@ -928,6 +1054,69 @@ outputs_stop_delayed_cancel(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_flush(output_status_cb cb)
|
||||
{
|
||||
struct output_device *device;
|
||||
int pending = 0;
|
||||
int ret;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
ret = outputs_device_flush(device, cb);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
pending += ret;
|
||||
}
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_volume_set(int volume, output_status_cb cb)
|
||||
{
|
||||
struct output_device *device;
|
||||
int pending = 0;
|
||||
int ret;
|
||||
|
||||
if (outputs_master_volume == volume)
|
||||
return 0;
|
||||
|
||||
outputs_master_volume = volume;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
{
|
||||
if (!device->selected)
|
||||
continue;
|
||||
|
||||
device->volume = rel_to_vol(device->relvol, outputs_master_volume);
|
||||
|
||||
ret = outputs_device_volume_set(device, cb);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
pending += ret;
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_VOLUME);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
int
|
||||
outputs_sessions_count(void)
|
||||
{
|
||||
struct output_device *device;
|
||||
int count = 0;
|
||||
|
||||
for (device = output_device_list; device; device = device->next)
|
||||
if (device->session)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
outputs_write(void *buf, size_t bufsize, int nsamples, struct media_quality *quality, struct timespec *pts)
|
||||
{
|
||||
@ -1004,6 +1193,8 @@ outputs_init(void)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
outputs_master_volume = -1;
|
||||
|
||||
CHECK_NULL(L_PLAYER, outputs_deferredev = evtimer_new(evbase_player, deferred_cb, NULL));
|
||||
|
||||
no_output = 1;
|
||||
|
@ -122,6 +122,10 @@ struct output_device
|
||||
// Type of output (string)
|
||||
const char *type_name;
|
||||
|
||||
// Last state that the backend returned to the handlers in outputs.c. This
|
||||
// field must only be set in outputs.c (not in the backends/player).
|
||||
enum output_device_state state;
|
||||
|
||||
// Misc device flags
|
||||
unsigned selected:1;
|
||||
unsigned advertised:1;
|
||||
@ -218,6 +222,11 @@ struct output_definition
|
||||
// Deinitialization function called at shutdown
|
||||
void (*deinit)(void);
|
||||
|
||||
// For all the below that take callbacks, the return values are:
|
||||
// - negative: error
|
||||
// - zero: ok, won't make a callback
|
||||
// - positive: number of callbacks that will be made
|
||||
|
||||
// Prepare a playback session on device and call back
|
||||
int (*device_start)(struct output_device *device, int callback_id);
|
||||
|
||||
@ -293,17 +302,26 @@ outputs_listener_notify(void);
|
||||
|
||||
/* ---------------------------- Called by player ---------------------------- */
|
||||
|
||||
int
|
||||
outputs_master_volume;
|
||||
|
||||
// Ownership of *add is transferred, so don't address after calling. Instead you
|
||||
// can address the return value (which is not the same if the device was already
|
||||
// in the list.
|
||||
struct output_device *
|
||||
outputs_device_add(struct output_device *add, bool new_deselect, int default_volume);
|
||||
outputs_device_add(struct output_device *add, bool new_deselect);
|
||||
|
||||
void
|
||||
outputs_device_remove(struct output_device *remove);
|
||||
|
||||
void
|
||||
outputs_device_select(struct output_device *device);
|
||||
|
||||
void
|
||||
outputs_device_deselect(struct output_device *device);
|
||||
|
||||
int
|
||||
outputs_device_start(struct output_device *device, output_status_cb cb);
|
||||
outputs_device_start(struct output_device *device, output_status_cb cb, bool only_probe);
|
||||
|
||||
int
|
||||
outputs_device_stop(struct output_device *device, output_status_cb cb);
|
||||
@ -314,8 +332,8 @@ outputs_device_stop_delayed(struct output_device *device, output_status_cb cb);
|
||||
int
|
||||
outputs_device_flush(struct output_device *device, output_status_cb cb);
|
||||
|
||||
int
|
||||
outputs_device_probe(struct output_device *device, output_status_cb cb);
|
||||
void
|
||||
outputs_device_volume_register(struct output_device *device, int absvol, int relvol);
|
||||
|
||||
int
|
||||
outputs_device_volume_set(struct output_device *device, output_status_cb cb);
|
||||
@ -333,14 +351,23 @@ void
|
||||
outputs_device_free(struct output_device *device);
|
||||
|
||||
int
|
||||
outputs_flush(output_status_cb cb);
|
||||
outputs_start(output_status_cb started_cb, output_status_cb stopped_cb, bool only_probe);
|
||||
|
||||
int
|
||||
outputs_stop(output_status_cb cb);
|
||||
|
||||
int
|
||||
outputs_flush(output_status_cb cb);
|
||||
|
||||
int
|
||||
outputs_volume_set(int volume, output_status_cb cb);
|
||||
|
||||
int
|
||||
outputs_stop_delayed_cancel(void);
|
||||
|
||||
int
|
||||
outputs_sessions_count(void);
|
||||
|
||||
void
|
||||
outputs_write(void *buf, size_t bufsize, int nsamples, struct media_quality *quality, struct timespec *pts);
|
||||
|
||||
|
@ -1143,7 +1143,7 @@ alsa_device_start(struct output_device *device, int callback_id)
|
||||
as->state = OUTPUT_STATE_CONNECTED;
|
||||
alsa_status(as);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1155,7 +1155,7 @@ alsa_device_stop(struct output_device *device, int callback_id)
|
||||
as->state = OUTPUT_STATE_STOPPED;
|
||||
alsa_status(as); // Will terminate the session since the state is STOPPED
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1169,7 +1169,7 @@ alsa_device_flush(struct output_device *device, int callback_id)
|
||||
as->state = OUTPUT_STATE_CONNECTED;
|
||||
alsa_status(as);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1184,7 +1184,7 @@ alsa_device_probe(struct output_device *device, int callback_id)
|
||||
as->state = OUTPUT_STATE_STOPPED;
|
||||
alsa_status(as); // Will terminate the session since the state is STOPPED
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1932,7 +1932,7 @@ cast_device_start_generic(struct output_device *device, int callback_id, cast_re
|
||||
cast_session_cleanup(cs);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cs = cast_session_make(device, AF_INET, callback_id);
|
||||
@ -1950,7 +1950,7 @@ cast_device_start_generic(struct output_device *device, int callback_id, cast_re
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1974,7 +1974,7 @@ cast_device_stop(struct output_device *device, int callback_id)
|
||||
|
||||
cast_session_shutdown(cs, CAST_STATE_NONE);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1986,7 +1986,7 @@ cast_device_flush(struct output_device *device, int callback_id)
|
||||
cs->state = CAST_STATE_MEDIA_CONNECTED;
|
||||
cast_status(cs);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -115,7 +115,7 @@ dummy_device_start(struct output_device *device, int callback_id)
|
||||
|
||||
dummy_status(ds);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -128,7 +128,7 @@ dummy_device_stop(struct output_device *device, int callback_id)
|
||||
|
||||
dummy_status(ds);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -141,7 +141,7 @@ dummy_device_flush(struct output_device *device, int callback_id)
|
||||
|
||||
dummy_status(ds);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -158,7 +158,7 @@ dummy_device_probe(struct output_device *device, int callback_id)
|
||||
|
||||
dummy_status(ds);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -302,7 +302,7 @@ fifo_device_start(struct output_device *device, int callback_id)
|
||||
|
||||
fifo_status(fifo_session);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -320,7 +320,7 @@ fifo_device_stop(struct output_device *device, int callback_id)
|
||||
fifo_session->state = OUTPUT_STATE_STOPPED;
|
||||
fifo_status(fifo_session);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -335,7 +335,7 @@ fifo_device_flush(struct output_device *device, int callback_id)
|
||||
fifo_session->state = OUTPUT_STATE_CONNECTED;
|
||||
fifo_status(fifo_session);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -362,7 +362,7 @@ fifo_device_probe(struct output_device *device, int callback_id)
|
||||
|
||||
fifo_status(fifo_session);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -754,7 +754,7 @@ pulse_device_start(struct output_device *device, int callback_id)
|
||||
|
||||
pulse_status(ps);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -768,7 +768,7 @@ pulse_device_stop(struct output_device *device, int callback_id)
|
||||
|
||||
stream_close(ps, close_cb);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -801,7 +801,7 @@ pulse_device_flush(struct output_device *device, int callback_id)
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse.mainloop);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -823,7 +823,7 @@ pulse_device_probe(struct output_device *device, int callback_id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -100,6 +100,12 @@
|
||||
#define RAOP_MD_WANTS_ARTWORK (1 << 1)
|
||||
#define RAOP_MD_WANTS_PROGRESS (1 << 2)
|
||||
|
||||
// ATV4 and Homepod disconnect for reasons that are not clear, but sending them
|
||||
// progress metadata at regular intervals reduces the problem. The below
|
||||
// interval was determined via testing, see:
|
||||
// https://github.com/ejurgensen/forked-daapd/issues/734#issuecomment-622959334
|
||||
#define RAOP_KEEP_ALIVE_INTERVAL 25
|
||||
|
||||
// This is an arbitrary value which just needs to be kept in sync with the config
|
||||
#define RAOP_CONFIG_MAX_VOLUME 11
|
||||
|
||||
@ -142,6 +148,8 @@ enum raop_state {
|
||||
RAOP_STATE_CONNECTED = RAOP_STATE_F_CONNECTED | 0x01,
|
||||
// Media data is being sent
|
||||
RAOP_STATE_STREAMING = RAOP_STATE_F_CONNECTED | 0x02,
|
||||
// Session teardown in progress (-> going to STOPPED state)
|
||||
RAOP_STATE_TEARDOWN = RAOP_STATE_F_CONNECTED | 0x03,
|
||||
// Session is failed, couldn't startup or error occurred
|
||||
RAOP_STATE_FAILED = RAOP_STATE_F_FAILED | 0x01,
|
||||
// Password issue: unknown password or bad password
|
||||
@ -337,7 +345,7 @@ static struct output_metadata *raop_cur_metadata;
|
||||
|
||||
/* Keep-alive timer - hack for ATV's with tvOS 10 */
|
||||
static struct event *keep_alive_timer;
|
||||
static struct timeval keep_alive_tv = { 30, 0 };
|
||||
static struct timeval keep_alive_tv = { RAOP_KEEP_ALIVE_INTERVAL, 0 };
|
||||
|
||||
/* Sessions */
|
||||
static struct raop_master_session *raop_master_sessions;
|
||||
@ -1224,7 +1232,6 @@ raop_send_req_teardown(struct raop_session *rs, evrtsp_req_cb cb, const char *lo
|
||||
return -1;
|
||||
}
|
||||
|
||||
rs->state = RAOP_STATE_CONNECTED;
|
||||
rs->reqs_in_flight++;
|
||||
|
||||
evrtsp_connection_set_closecb(rs->ctrl, NULL, NULL);
|
||||
@ -1737,8 +1744,12 @@ raop_status(struct raop_session *rs)
|
||||
case RAOP_STATE_STREAMING:
|
||||
state = OUTPUT_STATE_STREAMING;
|
||||
break;
|
||||
case RAOP_STATE_TEARDOWN:
|
||||
DPRINTF(E_LOG, L_RAOP, "Bug! raop_status() called with transitional state (TEARDOWN)\n");
|
||||
state = OUTPUT_STATE_STOPPED;
|
||||
break;
|
||||
default:
|
||||
DPRINTF(E_LOG, L_RAOP, "Bug! Unhandled state in raop_status()\n");
|
||||
DPRINTF(E_LOG, L_RAOP, "Bug! Unhandled state in raop_status(): %d\n", rs->state);
|
||||
state = OUTPUT_STATE_FAILED;
|
||||
}
|
||||
|
||||
@ -1956,6 +1967,9 @@ session_teardown(struct raop_session *rs, const char *log_caller)
|
||||
deferred_session_failure(rs);
|
||||
}
|
||||
|
||||
// Change state immediately so we won't write any more to the device
|
||||
rs->state = RAOP_STATE_TEARDOWN;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3514,6 +3528,8 @@ raop_startup_cancel(struct raop_session *rs)
|
||||
return;
|
||||
}
|
||||
|
||||
rs->state = RAOP_STATE_TEARDOWN;
|
||||
|
||||
ret = raop_send_req_teardown(rs, raop_cb_startup_cancel, "startup_cancel");
|
||||
if (ret < 0)
|
||||
session_failure(rs);
|
||||
@ -3759,7 +3775,7 @@ raop_cb_startup_setup(struct evrtsp_request *req, void *arg)
|
||||
token = strtok_r(token, ";=", &ptr);
|
||||
while (token)
|
||||
{
|
||||
DPRINTF(E_DBG, L_RAOP, "token: %s\n", token);
|
||||
DPRINTF(E_SPAM, L_RAOP, "token: %s\n", token);
|
||||
|
||||
if (strcmp(token, "server_port") == 0)
|
||||
{
|
||||
@ -4693,7 +4709,7 @@ raop_device_start_generic(struct output_device *device, int callback_id, bool on
|
||||
ret = raop_send_req_options(rs, raop_cb_startup_options, "device_start");
|
||||
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
DPRINTF(E_WARN, L_RAOP, "Could not send verification or OPTIONS request on IPv6\n");
|
||||
@ -4719,7 +4735,7 @@ raop_device_start_generic(struct output_device *device, int callback_id, bool on
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -4741,7 +4757,9 @@ raop_device_stop(struct output_device *device, int callback_id)
|
||||
|
||||
rs->callback_id = callback_id;
|
||||
|
||||
return session_teardown(rs, "device_stop");
|
||||
session_teardown(rs, "device_stop");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -4759,7 +4777,7 @@ raop_device_flush(struct output_device *device, int callback_id)
|
||||
|
||||
rs->callback_id = callback_id;
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
|
662
src/player.c
662
src/player.c
File diff suppressed because it is too large
Load Diff
@ -111,6 +111,9 @@ player_speaker_prevent_playback_set(uint64_t id, bool prevent_playback);
|
||||
int
|
||||
player_speaker_busy_set(uint64_t id, bool busy);
|
||||
|
||||
void
|
||||
player_speaker_resurrect(void *arg);
|
||||
|
||||
int
|
||||
player_playback_start(void);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user