[player] Limit auto reconnection + add option for user override
With this commit auto reconnection per default will only be done for ATV4s and HomePods. Reconnection is not always desirable, for instance if the device cuts the connection because it is busy with something else, ref. issue #934. The commit also adds an option to override auto reconnection, thus either enabling it for other devices or disabling it for affected devices.
This commit is contained in:
parent
e8bdcd2365
commit
a71444d3e1
|
@ -300,6 +300,11 @@ audio {
|
|||
# present. The speaker will remain until restart of forked-daapd.
|
||||
# permanent = false
|
||||
|
||||
# Some devices spuriously disconnect during playback, and based on the
|
||||
# device type forked-daapd may attempt to reconnect. Setting this option
|
||||
# overrides this so reconnecting is either always enabled or disabled.
|
||||
# reconnect = false
|
||||
|
||||
# AirPlay password
|
||||
# password = "s1kr3t"
|
||||
#}
|
||||
|
|
|
@ -157,6 +157,7 @@ static cfg_opt_t sec_airplay[] =
|
|||
CFG_INT("max_volume", 11, CFGF_NONE),
|
||||
CFG_BOOL("exclude", cfg_false, CFGF_NONE),
|
||||
CFG_BOOL("permanent", cfg_false, CFGF_NONE),
|
||||
CFG_BOOL("reconnect", cfg_false, CFGF_NODEFAULT),
|
||||
CFG_STR("password", NULL, CFGF_NONE),
|
||||
CFG_END()
|
||||
};
|
||||
|
|
|
@ -119,6 +119,7 @@ struct output_device
|
|||
unsigned v6_disabled:1;
|
||||
unsigned prevent_playback:1;
|
||||
unsigned busy:1;
|
||||
unsigned resurrect:1;
|
||||
|
||||
// Credentials if relevant
|
||||
const char *password;
|
||||
|
|
|
@ -123,6 +123,7 @@ enum raop_devtype {
|
|||
RAOP_DEV_APEX3_80211N,
|
||||
RAOP_DEV_APPLETV,
|
||||
RAOP_DEV_APPLETV4,
|
||||
RAOP_DEV_HOMEPOD,
|
||||
RAOP_DEV_OTHER,
|
||||
};
|
||||
|
||||
|
@ -307,6 +308,7 @@ static const char *raop_devtype[] =
|
|||
"AirPort Express 3 - 802.11n",
|
||||
"AppleTV",
|
||||
"AppleTV4",
|
||||
"HomePod",
|
||||
"Other",
|
||||
};
|
||||
|
||||
|
@ -2124,10 +2126,9 @@ session_make(struct output_device *rd, int callback_id, bool only_probe)
|
|||
rs->auth_quirk_itunes = 0;
|
||||
break;
|
||||
|
||||
case RAOP_DEV_OTHER:
|
||||
default:
|
||||
rs->encrypt = re->encrypt;
|
||||
rs->auth_quirk_itunes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = session_connection_setup(rs, rd, AF_INET6);
|
||||
|
@ -4408,7 +4409,10 @@ raop_verification_verify(struct raop_session *rs)
|
|||
/* ------------------ RAOP devices discovery - mDNS callback ---------------- */
|
||||
/* Thread: main (mdns) */
|
||||
|
||||
|
||||
/* Examples of txt content:
|
||||
* HomePod
|
||||
["cn=0,1,2,3" "da=true" "et=0,3,5" "ft=0x4A7FCA00,0x56BD0" "sf=0x404" "md=0,1,2" "am=AudioAccessory1,1" "pk=1...f" "tp=UDP" "vn=65537" "vs=356.19" "ov=11.2.5" "vv=2"]
|
||||
* Apple TV 2:
|
||||
["sf=0x4" "am=AppleTV2,1" "vs=130.14" "vn=65537" "tp=UDP" "ss=16" "sr=4 4100" "sv=false" "pw=false" "md=0,1,2" "et=0,3,5" "da=true" "cn=0,1,2,3" "ch=2"]
|
||||
["sf=0x4" "am=AppleTV2,1" "vs=105.5" "md=0,1,2" "tp=TCP,UDP" "vn=65537" "pw=false" "ss=16" "sr=44100" "da=true" "sv=false" "et=0,3" "cn=0,1" "ch=2" "txtvers=1"]
|
||||
|
@ -4440,7 +4444,8 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
{
|
||||
struct output_device *rd;
|
||||
struct raop_extra *re;
|
||||
cfg_t *airplay;
|
||||
cfg_t *devcfg;
|
||||
cfg_opt_t *cfgopt;
|
||||
const char *p;
|
||||
char *at_name;
|
||||
char *password;
|
||||
|
@ -4470,14 +4475,14 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
|
||||
DPRINTF(E_DBG, L_RAOP, "Event for AirPlay device '%s' (port %d, id %" PRIx64 ")\n", at_name, port, id);
|
||||
|
||||
airplay = cfg_gettsec(cfg, "airplay", at_name);
|
||||
if (airplay && cfg_getbool(airplay, "exclude"))
|
||||
devcfg = cfg_gettsec(cfg, "airplay", at_name);
|
||||
if (devcfg && cfg_getbool(devcfg, "exclude"))
|
||||
{
|
||||
DPRINTF(E_LOG, L_RAOP, "Excluding AirPlay device '%s' as set in config\n", at_name);
|
||||
|
||||
return;
|
||||
}
|
||||
if (airplay && cfg_getbool(airplay, "permanent") && (port < 0))
|
||||
if (devcfg && cfg_getbool(devcfg, "permanent") && (port < 0))
|
||||
{
|
||||
DPRINTF(E_INFO, L_RAOP, "AirPlay device '%s' disappeared, but set as permanent in config\n", at_name);
|
||||
|
||||
|
@ -4559,9 +4564,8 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
{
|
||||
DPRINTF(E_LOG, L_RAOP, "AirPlay device '%s' is password-protected\n", at_name);
|
||||
|
||||
airplay = cfg_gettsec(cfg, "airplay", at_name);
|
||||
if (airplay)
|
||||
password = cfg_getstr(airplay, "password");
|
||||
if (devcfg)
|
||||
password = cfg_getstr(devcfg, "password");
|
||||
|
||||
if (!password)
|
||||
DPRINTF(E_LOG, L_RAOP, "No password given in config for AirPlay device '%s'\n", at_name);
|
||||
|
@ -4610,9 +4614,19 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
re->devtype = RAOP_DEV_APPLETV4; // Stream to ATV with tvOS 10 needs to be kept alive
|
||||
else if (strncmp(p, "AppleTV", strlen("AppleTV")) == 0)
|
||||
re->devtype = RAOP_DEV_APPLETV;
|
||||
else if (strncmp(p, "AudioAccessory", strlen("AudioAccessory")) == 0)
|
||||
re->devtype = RAOP_DEV_HOMEPOD;
|
||||
else if (*p == '\0')
|
||||
DPRINTF(E_LOG, L_RAOP, "AirPlay device '%s': am has no value\n", at_name);
|
||||
|
||||
// If the user didn't set any reconnect setting we enable for Apple TV and
|
||||
// HomePods due to https://github.com/ejurgensen/forked-daapd/issues/734
|
||||
cfgopt = devcfg ? cfg_getopt(devcfg, "reconnect") : NULL;
|
||||
if (cfgopt && cfgopt->nvalues == 1)
|
||||
rd->resurrect = cfg_opt_getnbool(cfgopt, 0);
|
||||
else
|
||||
rd->resurrect = (re->devtype == RAOP_DEV_APPLETV4) || (re->devtype == RAOP_DEV_HOMEPOD);
|
||||
|
||||
// Encrypt stream
|
||||
p = keyval_get(txt, "ek");
|
||||
if (p && (*p == '1'))
|
||||
|
|
21
src/player.c
21
src/player.c
|
@ -1466,22 +1466,26 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu
|
|||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Output device disappeared during streaming!\n");
|
||||
}
|
||||
else if (status == OUTPUT_STATE_FAILED && player_state == PLAY_PLAYING)
|
||||
else if (status == OUTPUT_STATE_FAILED)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "The %s device '%s' failed during playback - attempting reconnect in %d sec\n", device->type_name, device->name, PLAYER_SPEAKER_RESURRECT_TIME);
|
||||
DPRINTF(E_WARN, L_PLAYER, "The %s device '%s' failed\n", device->type_name, device->name);
|
||||
|
||||
// The device can fail outside of playback, e.g. if it disconnects after a
|
||||
// flush command
|
||||
if (player_state != PLAY_PLAYING)
|
||||
goto out;
|
||||
|
||||
if (outputs_sessions_count() == 0)
|
||||
pb_suspend();
|
||||
|
||||
if (!device->resurrect)
|
||||
goto out;
|
||||
|
||||
DPRINTF(E_LOG, L_PLAYER, "Attempting reconnection in %d sec to the %s device '%s'\n", PLAYER_SPEAKER_RESURRECT_TIME, device->type_name, device->name);
|
||||
|
||||
// TODO do this internally instead of through the worker
|
||||
worker_execute(player_speaker_resurrect, &(device->id), sizeof(device->id), PLAYER_SPEAKER_RESURRECT_TIME);
|
||||
}
|
||||
else if (status == OUTPUT_STATE_FAILED)
|
||||
{
|
||||
// The device can fail outside of playback, e.g. if it disconnects after a
|
||||
// flush command
|
||||
DPRINTF(E_WARN, L_PLAYER, "The %s device '%s' failed\n", device->type_name, device->name);
|
||||
}
|
||||
else if (status == OUTPUT_STATE_STOPPED)
|
||||
{
|
||||
DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' stopped\n", device->type_name, device->name);
|
||||
|
@ -1492,6 +1496,7 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu
|
|||
outputs_device_cb_set(device, device_streaming_cb);
|
||||
}
|
||||
|
||||
out:
|
||||
// We don't do this in the other cb's because they are triggered by a command
|
||||
// and thus the update should be done as part of the command completion (which
|
||||
// can better determine which type of listener event to use)
|
||||
|
|
Loading…
Reference in New Issue