[raop] Only send exact types of metadata requested by device

Airport Express announces md=2, meaning only progress metadata, so we shouldn't
send artwork and text metadata to it.
This commit is contained in:
ejurgensen 2020-04-22 21:41:55 +02:00
parent 4491928466
commit 07972c28f7

View File

@ -96,6 +96,9 @@
#define RAOP_MD_DELAY_STARTUP 15360 #define RAOP_MD_DELAY_STARTUP 15360
#define RAOP_MD_DELAY_SWITCH (RAOP_MD_DELAY_STARTUP * 2) #define RAOP_MD_DELAY_SWITCH (RAOP_MD_DELAY_STARTUP * 2)
#define RAOP_MD_WANTS_TEXT (1 << 0)
#define RAOP_MD_WANTS_ARTWORK (1 << 1)
#define RAOP_MD_WANTS_PROGRESS (1 << 2)
// This is an arbitrary value which just needs to be kept in sync with the config // This is an arbitrary value which just needs to be kept in sync with the config
#define RAOP_CONFIG_MAX_VOLUME 11 #define RAOP_CONFIG_MAX_VOLUME 11
@ -152,8 +155,8 @@ struct raop_extra
{ {
enum raop_devtype devtype; enum raop_devtype devtype;
uint16_t wanted_metadata;
bool encrypt; bool encrypt;
bool wants_metadata;
bool supports_auth_setup; bool supports_auth_setup;
}; };
@ -189,10 +192,11 @@ struct raop_session
struct evrtsp_connection *ctrl; struct evrtsp_connection *ctrl;
enum raop_state state; enum raop_state state;
uint16_t wanted_metadata;
bool req_has_auth; bool req_has_auth;
bool encrypt; bool encrypt;
bool auth_quirk_itunes; bool auth_quirk_itunes;
bool wants_metadata;
bool supports_post; bool supports_post;
bool supports_auth_setup; bool supports_auth_setup;
@ -252,9 +256,6 @@ struct raop_service
typedef void (*evrtsp_req_cb)(struct evrtsp_request *req, void *arg); typedef void (*evrtsp_req_cb)(struct evrtsp_request *req, void *arg);
/* Truncate RTP time to lower 32bits for RAOP */
#define RAOP_RTPTIME(x) ((uint32_t)((x) & (uint64_t)0xffffffff))
/* NTP timestamp definitions */ /* NTP timestamp definitions */
#define FRAC 4294967296. /* 2^32 as a double */ #define FRAC 4294967296. /* 2^32 as a double */
#define NTP_EPOCH_DELTA 0x83aa7e80 /* 2208988800 - that's 1970 - 1900 in seconds */ #define NTP_EPOCH_DELTA 0x83aa7e80 /* 2208988800 - that's 1970 - 1900 in seconds */
@ -2026,7 +2027,7 @@ session_make(struct output_device *rd, int family, int callback_id, bool only_pr
rs->password = rd->password; rs->password = rd->password;
rs->supports_auth_setup = re->supports_auth_setup; rs->supports_auth_setup = re->supports_auth_setup;
rs->wants_metadata = re->wants_metadata; rs->wanted_metadata = re->wanted_metadata;
switch (re->devtype) switch (re->devtype)
{ {
@ -2339,7 +2340,6 @@ raop_metadata_send_artwork(struct raop_session *rs, struct evbuffer *evbuf, stru
default: default:
DPRINTF(E_LOG, L_RAOP, "Unsupported artwork format %d\n", rmd->artwork_fmt); DPRINTF(E_LOG, L_RAOP, "Unsupported artwork format %d\n", rmd->artwork_fmt);
return -1; return -1;
} }
@ -2350,7 +2350,6 @@ raop_metadata_send_artwork(struct raop_session *rs, struct evbuffer *evbuf, stru
if (ret != 0) if (ret != 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Could not copy artwork for sending\n"); DPRINTF(E_LOG, L_RAOP, "Could not copy artwork for sending\n");
return -1; return -1;
} }
@ -2362,7 +2361,7 @@ raop_metadata_send_artwork(struct raop_session *rs, struct evbuffer *evbuf, stru
} }
static int static int
raop_metadata_send_metadata(struct raop_session *rs, struct evbuffer *evbuf, struct raop_metadata *rmd, char *rtptime) raop_metadata_send_text(struct raop_session *rs, struct evbuffer *evbuf, struct raop_metadata *rmd, char *rtptime)
{ {
uint8_t *buf; uint8_t *buf;
size_t len; size_t len;
@ -2375,11 +2374,10 @@ raop_metadata_send_metadata(struct raop_session *rs, struct evbuffer *evbuf, str
if (ret != 0) if (ret != 0)
{ {
DPRINTF(E_LOG, L_RAOP, "Could not copy metadata for sending\n"); DPRINTF(E_LOG, L_RAOP, "Could not copy metadata for sending\n");
return -1; return -1;
} }
ret = raop_send_req_set_parameter(rs, evbuf, "application/x-dmap-tagged", rtptime, raop_cb_metadata, "send_metadata"); ret = raop_send_req_set_parameter(rs, evbuf, "application/x-dmap-tagged", rtptime, raop_cb_metadata, "send_text");
if (ret < 0) if (ret < 0)
DPRINTF(E_LOG, L_RAOP, "Could not send SET_PARAMETER metadata request to '%s'\n", rs->devname); DPRINTF(E_LOG, L_RAOP, "Could not send SET_PARAMETER metadata request to '%s'\n", rs->devname);
@ -2400,56 +2398,48 @@ raop_metadata_send_generic(struct raop_session *rs, struct output_metadata *meta
raop_metadata_rtptimes_get(&start, &display, &pos, &end, rs->master_session, metadata); raop_metadata_rtptimes_get(&start, &display, &pos, &end, rs->master_session, metadata);
CHECK_NULL(L_RAOP, evbuf = evbuffer_new());
ret = raop_metadata_send_progress(rs, evbuf, rmd, display, pos, end);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not send progress to '%s'\n", rs->devname);
ret = -1;
goto out;
}
if (only_progress)
goto out;
ret = snprintf(rtptime, sizeof(rtptime), "rtptime=%u", start); ret = snprintf(rtptime, sizeof(rtptime), "rtptime=%u", start);
if ((ret < 0) || (ret >= sizeof(rtptime))) if ((ret < 0) || (ret >= sizeof(rtptime)))
{ {
DPRINTF(E_LOG, L_RAOP, "RTP-Info too big for buffer while sending metadata\n"); DPRINTF(E_LOG, L_RAOP, "RTP-Info too big for buffer while sending metadata\n");
ret = -1; return -1;
goto out;
} }
ret = raop_metadata_send_metadata(rs, evbuf, rmd, rtptime); CHECK_NULL(L_RAOP, evbuf = evbuffer_new());
if (ret < 0)
if (rs->wanted_metadata & RAOP_MD_WANTS_PROGRESS)
{ {
DPRINTF(E_LOG, L_RAOP, "Could not send metadata to '%s'\n", rs->devname); ret = raop_metadata_send_progress(rs, evbuf, rmd, display, pos, end);
ret = -1; if (ret < 0)
goto out; goto error;
} }
if (rmd->artwork) if (!only_progress && (rs->wanted_metadata & RAOP_MD_WANTS_TEXT))
{
ret = raop_metadata_send_text(rs, evbuf, rmd, rtptime);
if (ret < 0)
goto error;
}
if (!only_progress && (rs->wanted_metadata & RAOP_MD_WANTS_ARTWORK) && rmd->artwork)
{ {
ret = raop_metadata_send_artwork(rs, evbuf, rmd, rtptime); ret = raop_metadata_send_artwork(rs, evbuf, rmd, rtptime);
if (ret < 0) if (ret < 0)
{ goto error;
DPRINTF(E_LOG, L_RAOP, "Could not send artwork to '%s'\n", rs->devname);
ret = -1;
goto out;
}
} }
out:
evbuffer_free(evbuf); evbuffer_free(evbuf);
return 0;
return ret; error:
evbuffer_free(evbuf);
return -1;
} }
static int static int
raop_metadata_startup_send(struct raop_session *rs) raop_metadata_startup_send(struct raop_session *rs)
{ {
if (!rs->wants_metadata || !raop_cur_metadata) if (!rs->wanted_metadata || !raop_cur_metadata)
return 0; return 0;
raop_cur_metadata->startup = true; raop_cur_metadata->startup = true;
@ -2460,7 +2450,7 @@ raop_metadata_startup_send(struct raop_session *rs)
static int static int
raop_metadata_keep_alive_send(struct raop_session *rs) raop_metadata_keep_alive_send(struct raop_session *rs)
{ {
if (!rs->wants_metadata || !raop_cur_metadata) if (!rs->wanted_metadata || !raop_cur_metadata)
return 0; return 0;
raop_cur_metadata->startup = false; raop_cur_metadata->startup = false;
@ -2479,7 +2469,7 @@ raop_metadata_send(struct output_metadata *metadata)
{ {
next = rs->next; next = rs->next;
if (!(rs->state & RAOP_STATE_F_CONNECTED) || !rs->wants_metadata) if (!(rs->state & RAOP_STATE_F_CONNECTED) || !rs->wanted_metadata)
continue; continue;
ret = raop_metadata_send_generic(rs, metadata, false); ret = raop_metadata_send_generic(rs, metadata, false);
@ -4427,7 +4417,7 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
const char *p; const char *p;
char *at_name; char *at_name;
char *password; char *password;
char *et; char *s;
char *token; char *token;
char *ptr; char *ptr;
uint64_t id; uint64_t id;
@ -4600,22 +4590,32 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
p = keyval_get(txt, "ek"); p = keyval_get(txt, "ek");
if (p && (*p == '1')) if (p && (*p == '1'))
re->encrypt = 1; re->encrypt = 1;
else
re->encrypt = 0;
// Metadata support // Metadata support
// We don't use the values, but they should mean: 0 = text, 1 = artwork, 2 = progress
p = keyval_get(txt, "md"); p = keyval_get(txt, "md");
if (p && (*p != '\0')) if (p)
re->wants_metadata = 1; {
else CHECK_NULL(L_RAOP, s = strdup(p));
re->wants_metadata = 0; token = strtok_r(s, ",", &ptr);
while (token)
{
if (strcmp(token, "0") == 0)
re->wanted_metadata |= RAOP_MD_WANTS_TEXT;
else if (strcmp(token, "1") == 0)
re->wanted_metadata |= RAOP_MD_WANTS_ARTWORK;
else if (strcmp(token, "2") == 0)
re->wanted_metadata |= RAOP_MD_WANTS_PROGRESS;
token = strtok_r(NULL, ",", &ptr);
}
free(s);
}
p = keyval_get(txt, "et"); p = keyval_get(txt, "et");
if (p) if (p)
{ {
CHECK_NULL(L_RAOP, et = strdup(p)); CHECK_NULL(L_RAOP, s = strdup(p));
token = strtok_r(et, ",", &ptr); token = strtok_r(s, ",", &ptr);
while (token) while (token)
{ {
// Value of 4 seems to indicate support (!= requirement) for auth-setup // Value of 4 seems to indicate support (!= requirement) for auth-setup
@ -4624,7 +4624,7 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
token = strtok_r(NULL, ",", &ptr); token = strtok_r(NULL, ",", &ptr);
} }
free(et); free(s);
} }
switch (family) switch (family)
@ -4633,14 +4633,14 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
rd->v4_address = strdup(address); rd->v4_address = strdup(address);
rd->v4_port = port; rd->v4_port = port;
DPRINTF(E_INFO, L_RAOP, "Adding AirPlay device '%s': password: %u, verification: %u, encrypt: %u, authsetup: %u, metadata: %u, type %s, address %s:%d\n", DPRINTF(E_INFO, L_RAOP, "Adding AirPlay device '%s': password: %u, verification: %u, encrypt: %u, authsetup: %u, metadata: %u, type %s, address %s:%d\n",
at_name, rd->has_password, rd->requires_auth, re->encrypt, re->supports_auth_setup, re->wants_metadata, raop_devtype[re->devtype], address, port); at_name, rd->has_password, rd->requires_auth, re->encrypt, re->supports_auth_setup, re->wanted_metadata, raop_devtype[re->devtype], address, port);
break; break;
case AF_INET6: case AF_INET6:
rd->v6_address = strdup(address); rd->v6_address = strdup(address);
rd->v6_port = port; rd->v6_port = port;
DPRINTF(E_INFO, L_RAOP, "Adding AirPlay device '%s': password: %u, verification: %u, encrypt: %u, authsetup: %u, metadata: %u, type %s, address [%s]:%d\n", DPRINTF(E_INFO, L_RAOP, "Adding AirPlay device '%s': password: %u, verification: %u, encrypt: %u, authsetup: %u, metadata: %u, type %s, address [%s]:%d\n",
at_name, rd->has_password, rd->requires_auth, re->encrypt, re->supports_auth_setup, re->wants_metadata, raop_devtype[re->devtype], address, port); at_name, rd->has_password, rd->requires_auth, re->encrypt, re->supports_auth_setup, re->wanted_metadata, raop_devtype[re->devtype], address, port);
break; break;
default: default: